#include "qgscopeworkbench.h"

#include "qgscopemainwindow.h"
#include "qgloggerdockwidget.h"
#include "qgscopechrono.h"
#include "qgscopedevice.h"
#include "qgscopeobjectdockwidget.h"
#include "qgaboutdialog.h"
#include "qgrunsession.h"

#include <QMainWindow>
#include <QStatusBar>
#include <QDockWidget>
#include <QToolBar>
#include <QMenu>
#include <QInputDialog>
#include <QComboBox>
#include <QToolButton>

#include "DockManager.h"
#include "DockWidget.h"
#include "DockAreaWidget.h"
#include "DockAreaTitleBar.h"
#include "DockAreaTabBar.h"
#include "FloatingDockContainer.h"
#include "DockComponentsFactory.h"
//
#include <QtGlobal>

Q_DECLARE_METATYPE(QgScopeWorkbench)
Q_DECLARE_METATYPE(QgScopeChronoTimer)
//Q_DECLARE_METATYPE(QgScopeChrono)

QgScopeScriptSiteAdaptor::~QgScopeScriptSiteAdaptor()
{
}

class DummySciptSite: public QgScopeScriptSiteAdaptor
{
public:
    virtual void registerObject(const QString& /*objName*/, QObject* /*obj*/) override
    {
    }
};


class QGScopeImplementation
{
public:
    QgScopeMainWindow* mainWindow;

    QgRunSession* session;

    QString appTitle;
    QString prjName;

    QToolBar* tb;
    QToolButton* tbChronoFreeze;
    QToolButton* tbMenu;
    QMenu* wbMenu;
    QMenu* docksMenu;
    QMenu* perspectiveMenu;
    QAction* savePerspective;
    QString lastPerspective;

    QgScopeObjectDockWidget* wObj;

    QgLoggerDockWidget* logDock;

    QList<QgScopeDevice*> devicesList;
    QList<QObject*> deviceObjectsList;
    QList<QObject*> channelObjectsList;

    QgScopeObjMap objMap;
    QVariant objMapVariant;

    QgScopeScriptSiteAdaptor* ssAdaptor;

    DummySciptSite dummyScriptSite;

    ads::CDockManager* DockManager;

    QString aboutMsg;

    void updateTitle()
    {
        mainWindow->setWindowTitle(prjName+" ["+appTitle+"]");               
    }


    QGScopeImplementation():
        ssAdaptor(&dummyScriptSite)
    {
        //session=&QgRunSession::defaultSession();

        objMapVariant=objMap;
    }
};

QGScopeImplementation qgScope_;


static void myMessageOutput(QtMsgType type, const QMessageLogContext &/*context*/, const QString &msg)
{
  QgLogger::LogMessageType qgtype=QgLogger::InfoMsg;

  switch (type) {
  case QtDebugMsg:
      qgtype=QgLogger::DebugMsg;
      break;
  case QtInfoMsg:
      qgtype=QgLogger::InfoMsg;
      break;
  case QtWarningMsg:
      qgtype=QgLogger::WarningMsg;
      break;
  case QtFatalMsg:
  default:
      qgtype=QgLogger::FatalMsg;
      break;
  //case QtCriticalMsg:
  //    qgtype=QgLogger::ErrorMsg;
  //    break;
  case QtSystemMsg:
      qgtype=QgLogger::SystemFatalMsg;
      break;
  }
  qgScope_.logDock->log(msg, qgtype, "QT");
}

QgScopeWorkbench::QgScopeWorkbench(QObject *parent) : QObject(parent)
{
    setObjectName("QgScopeWorkbench");
}

QgScopeWorkbench::QgScopeWorkbench(const QgScopeWorkbench& wb):
    QObject(wb.parent())
{
}

QgScopeWorkbench::~QgScopeWorkbench()
{
}

const QString& QgScopeWorkbench::version()
{
    static QString ver("1.0");
    return ver;
}

QVariant QgScopeWorkbench::objs() const
{
    return QVariant(qgScope_.objMap); //.objMapVariant;
}

//Public, just to be dbg-nice
QgScopeWorkbench* theInstance=0;

//Q_DECLARE_METATYPE(QgScopeChrono)

QgScopeWorkbench& QgScopeWorkbench::instance()
{
    if (!theInstance)
    {
        //LV singleton per la creazione della schermata principale con widget
        theInstance=new QgScopeWorkbench;

        Q_INIT_RESOURCE(ads);

        //qRegisterMetaType<QgScopeChrono>();

        //main window, che contiene tutti widget.
        qgScope_.mainWindow=new QgScopeMainWindow;
        qgScope_.tb=qgScope_.mainWindow->addToolBar(QString("Main Menu"));
        qgScope_.tb->setVisible(true);
        qgScope_.tb->toggleViewAction()->setEnabled(false);

        //barra superiore con gestione chrono
        {
            qgScope_.tbMenu=new QToolButton;
            qgScope_.tbMenu->setText("=");

            qgScope_.wbMenu=new QMenu("Main Menu");
            qgScope_.docksMenu=new QMenu("Docks");
            QAction* actAbout=new QAction("About...", qgScope_.mainWindow);
            connect(actAbout, &QAction::triggered, theInstance,
                []()
                {
                    static QString myMsg=
                            "<p><b>GrifoScope</b>: an advanced(?) Grifo Radar Run-time monitor</p>";
                    if (!qgScope_.aboutMsg.isEmpty())
                        myMsg=qgScope_.aboutMsg;
                    static QgAboutDialog dlgAbout(myMsg);
                    dlgAbout.exec();
                });

            {
                qgScope_.perspectiveMenu=new QMenu("Perspectives");
                QAction* act=qgScope_.perspectiveMenu->addAction("switch to...");
                connect(act, &QAction::triggered, theInstance, []()
                {
                   QStringList plist=qgScope_.DockManager->perspectiveNames();
                   QString pname=QInputDialog::getItem(0, "Switch Perspective", QString(), plist, 0, true);
                   if (!pname.isEmpty())
                   {
                       if (plist.contains(pname, Qt::CaseInsensitive))
                       {
                           qgScope_.DockManager->openPerspective(pname);
                       }
                       else
                       {
                         qgScope_.DockManager->addPerspective(pname);
                       }
                       //qgScope_.lastPerspective=pname;
                   }
                });

                qgScope_.savePerspective=qgScope_.perspectiveMenu->addAction("save...");
                connect(qgScope_.savePerspective, &QAction::triggered, theInstance, []()
                {
                   QStringList plist=qgScope_.DockManager->perspectiveNames();
                   QString pname=QInputDialog::getItem(0, "Save Perspective", QString(), plist, 0, true);
                   if (!pname.isEmpty())
                   {
                       qgScope_.DockManager->addPerspective(pname);
                   }
                });
                act=qgScope_.perspectiveMenu->addAction("save default...");
                connect(act, &QAction::triggered, theInstance, []()
                {
                   QStringList plist=qgScope_.DockManager->perspectiveNames();
                   QString pname=QInputDialog::getItem(0, "Save As Defualt Perspective", QString(), plist, 0, true);
                   if (!pname.isEmpty())
                   {
                       //qgScope_.DockManager->addPerspective(pname);
                       qgScope_.lastPerspective=pname;
                   }
                });

            }
            qgScope_.wbMenu->addMenu(qgScope_.docksMenu);
            qgScope_.wbMenu->addMenu(qgScope_.perspectiveMenu);
            qgScope_.wbMenu->addAction(actAbout);
            qgScope_.tbMenu->setMenu(qgScope_.wbMenu);
            //qgScope_.tbMenu->setIcon(st->standardIcon(QStyle::SP_ArrowDown)); //QIcon(":/icons/icons/list89.png"));
            qgScope_.tbMenu->setContextMenuPolicy(Qt::ActionsContextMenu);
            qgScope_.tbMenu->setPopupMode(QToolButton::InstantPopup);
        }

        qgScope_.tbChronoFreeze=new QToolButton;
        qgScope_.tbChronoFreeze->setText("!");
        qgScope_.tbChronoFreeze->setCheckable(true);

        connect(&QgScopeChrono::instance(), &QgScopeChrono::chronoFrozen, theInstance,
                [&](bool checked)
                {
                    qgScope_.tbChronoFreeze->setChecked(checked);
                }
            );
        connect(qgScope_.tbChronoFreeze, &QToolButton::toggled, &QgScopeChrono::instance(), QgScopeChrono::setFreeze);

        qgScope_.tb->setIconSize(QSize(24, 24));

        qgScope_.tb->addWidget(qgScope_.tbMenu);

        qgScope_.tb->addWidget(qgScope_.tbChronoFreeze);
        qgScope_.tb->addWidget(qgScope_.mainWindow->chronoCB());

        ads::CDockManager::setConfigFlag(ads::CDockManager::FocusHighlighting, true);
        ads::CDockManager::setConfigFlag(ads::CDockManager::OpaqueSplitterResize, false);
        ads::CDockManager::setConfigFlag(ads::CDockManager::OpaqueUndocking, false);
        ads::CDockManager::setConfigFlag(ads::CDockManager::DragPreviewIsDynamic, false);
        ads::CDockManager::setConfigFlag(ads::CDockManager::DragPreviewShowsContentPixmap, false);
        ads::CDockManager::setConfigFlag(ads::CDockManager::DragPreviewHasWindowFrame, false);

        qgScope_.DockManager = new ads::CDockManager(qgScope_.mainWindow);
        //widget per la visualizzazione degli oggetti
        qgScope_.wObj=new QgScopeObjectDockWidget;
        //qgScope_.mainWindow->addDockWidget(Qt::LeftDockWidgetArea, qgScope_.wObj);
        //qgScope_.DockManager->addDockWidget(ads::LeftDockWidgetArea, qgScope_.wObj);
        theInstance->addOuterPannel(Qt::LeftDockWidgetArea, qgScope_.wObj);

        //widget con la visualizzazione del logger
        qgScope_.logDock=new QgLoggerDockWidget;
        qgScope_.mainWindow->addDockWidget(Qt::BottomDockWidgetArea, qgScope_.logDock);

        qInstallMessageHandler(myMessageOutput);

        qgScope_.session=&QgRunSession::defaultSession();
    }

    //qgScope_.ssAdaptor->registerObject("session", qgScope_.session);

    return *theInstance;
}

void QgScopeWorkbench::initializeStdToolBars()
{
}

void QgScopeWorkbench::setAboutMessage(const QString& msg)
{
    qgScope_.aboutMsg=msg;
}

void QgScopeWorkbench::setScriptSiteAdptor(QgScopeScriptSiteAdaptor* adaptor)
{

    qgScope_.ssAdaptor=adaptor ? adaptor : &qgScope_.dummyScriptSite;
    qgScope_.ssAdaptor->registerObject("session", qgScope_.session);
}

QgLoggerDockWidget& QgScopeWorkbench::loggerWidget()
{
    return *qgScope_.logDock;
}

void QgScopeWorkbench::log(const QString &str)
{
    qgScope_.logDock->log(str);
}

void QgScopeWorkbench::setApplicationTitle(const QString& title)
{
    qgScope_.appTitle=title;
    qgScope_.updateTitle();
}

void QgScopeWorkbench::setClientInitilizer(void (*handler)(void))
{
    qgScope_.mainWindow->setClientInitializer(handler);
}

void QgScopeWorkbench::setProjectName(const QString& name)
{
    qgScope_.prjName=name;
    qgScope_.updateTitle();
}

QMainWindow* QgScopeWorkbench::mainWindow() const
{
   //if (!qgScope_.mainWindow)
   //    qgScope_.mainWindow=new QgScopeMainWindow;
   return qgScope_.mainWindow;
}

ads::CDockManager* QgScopeWorkbench::dockManager()
{
    return qgScope_.DockManager;
}

void QgScopeWorkbench::show() const
{
    mainWindow()->show();
    mainWindow()->statusBar()->showMessage("Wellcome to the GrifoScope workbench");
}

ads::CDockWidget* QgScopeWorkbench::addOuterPannel(Qt::DockWidgetArea area, QWidget* w, const QString &group)
{
    static int dock_counter;
#if 0
    QDockWidget* dw=qobject_cast<QDockWidget*>(w);
    if (dw==0)
    {

        QString tmp=w->objectName();
        if (tmp.isEmpty())
            tmp=QString("dock_%1").arg(dock_counter);
        else
            tmp=tmp+QString("_dock_%1").arg(dock_counter);

        dw=new QDockWidget;
        dw->setWindowTitle(w->objectName());
        dw->setObjectName(tmp);
        dw->setWidget(w);

        ++dock_counter;
    }
#endif
    auto dw=new ads::CDockWidget(w->windowTitle()); //w->objectName());
    dw->setWidget(w);
    ads::DockWidgetArea ads_area=static_cast<ads::DockWidgetArea>(area+1);
    if (area==Qt::AllDockWidgetAreas)
        ads_area=ads::CenterDockWidgetArea;

    auto ads_dock=qgScope_.DockManager->addDockWidget(ads_area, dw);

    onAdsDockCreated(dw, group);
#if 0
    QMenu* dMenu=qgScope_.docksMenu;
    if (!group.isEmpty())
    {
        //qgScope_.docksMenu->fi
    }
    QAction* dockAction=dw->toggleViewAction();
#if 0
    QAction* dockAction=new QAction(w->windowTitle(), this);
    dockAction->setData((qulonglong)ads_dock);
    connect(dockAction, &QAction::triggered, this, [this](bool checked)
    {
       QAction* a=qobject_cast<QAction*>(sender());
       ads::CDockWidget* w=reinterpret_cast<ads::CDockWidget*>(a->data().toULongLong());
       w->setToggleViewActionChecked(checked);
    });
#endif
    dMenu->addAction(dockAction);
#endif
    return dw;
}

void QgScopeWorkbench::onAppMenuCreated(QMenu* m)
{
   qgScope_.wbMenu->insertMenu(qgScope_.wbMenu->actions()[0], m);
}

void QgScopeWorkbench::onAppReady()
{
    restoreLayout();
}

void QgScopeWorkbench::onAdsDockCreated(ads::CDockWidget* dw, const QString& groupname)
{

    QMenu* dMenu=qgScope_.docksMenu;
    if (!groupname.isEmpty())
    {
        //qgScope_.docksMenu->fi
        QMenu* ch=dMenu->findChild<QMenu*>(groupname);
        if (ch)
        {
            dMenu=ch;
        }
        else
        {
            ch=dMenu->addMenu(groupname); //new QMenu(groupname);
            ch->setObjectName(groupname);
            dMenu->addMenu(ch);
            dMenu=ch;
        }
    }
    QAction* dockAction=dw->toggleViewAction();
    dMenu->addAction(dockAction);
}

QList<QgScopeDevice *> QgScopeWorkbench::devicesList() const
{
    return qgScope_.devicesList;
}

QList<QObject *> QgScopeWorkbench::deviceObjectsList() const
{
    return qgScope_.deviceObjectsList;
}


void QgScopeWorkbench::addDevice(QgScopeDevice* d)
{
    qgScope_.devicesList.append(d);
    qgScope_.deviceObjectsList.append(d);

    qgScope_.wObj->addDevice(d);

    const QString name=d->nickName();

    auto dmapped=qgScope_.objMap.find(name);
    if (dmapped!=qgScope_.objMap.end())
        qgScope_.logDock->log(QString("Duplicated object name: %1").arg(name), QgLogger::WarningMsg, "WB");
    else
    {
        qgScope_.logDock->log(QString("Registered device: %1").arg(name), QgLogger::InfoMsg, "WB");
        qgScope_.objMap[d->nickName()]=QVariant::fromValue(static_cast<QObject*>(d));
    }

    qgScope_.ssAdaptor->registerObject(d->scriptableName(), d);
}

QgScopeChrono* QgScopeWorkbench::chrono() const
{
    return &QgScopeChrono::instance();
}

QObject* QgScopeWorkbench::chronoObject() const
{
    return chrono();
}

void QgScopeWorkbench::addChannel(QgScopeChannel* c)
{
    //qgScope_.devicesList.append(d);
    qgScope_.channelObjectsList.append(c);

    qgScope_.wObj->addChannel(c);
    qgScope_.ssAdaptor->registerObject(c->scriptableName(), c);
}

QList<QObject *> QgScopeWorkbench::channelObjectsList() const
{
    return qgScope_.channelObjectsList;
}

QgRunSession* QgScopeWorkbench::runSession() const
{
    return qgScope_.session;
}

void QgScopeWorkbench::restoreLayout(const QString &lyname)
{
    //if (lyname.isEmpty())
    {
        QString lastPerspective=qgScope_.mainWindow->restoreLayout(lyname);
        qgScope_.lastPerspective=lastPerspective.isEmpty() ? "default" : lastPerspective;
    }
}

QMenu* QgScopeWorkbench::wbMenu()
{
    return qgScope_.wbMenu;
}

QString QgScopeWorkbench::defaultPerspectiveName() const
{
    return qgScope_.lastPerspective.isEmpty() ? "default" : qgScope_.lastPerspective;
}
