/***************************** LICENSE START ***********************************

 Copyright 2019 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#pragma once

#include <ostream>
#include <string>
#include <vector>

#include <QElapsedTimer>
#include <QStandardItemModel>
#include <QComboBox>
#include <QPixmap>
#include <QPalette>
#include <QLabel>
#include <QTimer>
#include <QTime>
#include <QObject>
#include <QProcess>
#include <QString>

#include "MvRequest.h"
#include "MvQDragDrop.h"
#include "ui_MacroEdit.h"
#include "Highlighter.h"
#include "LanguageHelper.h"
#include "LanguageHelperFactory.h"
#include "MacroRunOptions.h"
#include "TabsSettings.h"

class FindTextDialog;
class GotoLineDialog;
class TabsDialog;
class RunDialog;
class FunctionListDialog;
class CodeTemplateDialog;
class MvQColourWidget;
class VerbFunctionHelpWidget;

template <class T>
class Handle
{
    T* ptr_;

public:
    Handle(T* ptr = 0) :
        ptr_(ptr)
    { /*if(ptr_) ptr_->attach();*/
    }

    virtual ~Handle()
    { /*if(ptr_) ptr_->detach();*/
    }

    Handle(const Handle<T>& other) :
        ptr_(other.ptr_)
    { /*if(ptr_) ptr_->attach();*/
    }

    Handle<T>& operator=(const Handle<T>& other)
    {
        if (ptr_ != other.ptr_) {
            /*if(ptr_) ptr_->detach();*/
            ptr_ = other.ptr_;
            /*if(ptr_) ptr_->attach();*/
        }
        return *this;
    }

    T* operator->() const { return ptr_; }
    T& operator*() const { return *ptr_; }

    operator T*() const { return ptr_; }

    bool operator<(const Handle<T>& other) const
    {
        return ptr_ < other.ptr_;
    }
};


//class Request;

//class ReplyObserver : public virtual Counted  {
class ReplyObserver : public QObject
{
    Q_OBJECT

public:
    ReplyObserver(QObject* parent);
    virtual ~ReplyObserver();  // Change to virtual if base class

    virtual void reply(const MvRequest&, int) = 0;
    virtual void progress(const MvRequest&)   = 0;
    virtual void message(const std::string&)  = 0;
    void attach();  //ir
    void detach();  //ir
    int macroPid() const { return macroPid_; }
    void setMacroPid(int v) { macroPid_ = v; }

protected:
    void callService(const std::string&, const MvRequest&);

    int macroPid_;

private:
    // No copy allowed

    ReplyObserver(const ReplyObserver&);
    ReplyObserver& operator=(const ReplyObserver&);
};


class ReplyObserverH : public Handle<ReplyObserver>
{
public:
    ReplyObserverH(ReplyObserver* o) :
        Handle<ReplyObserver>(o) {}
};


class MacroEdit;

class MacroRunTask : public ReplyObserver
{
    Q_OBJECT

public:
    MacroRunTask(MacroEdit* editor, QString macro, MacroRunOptions options);
    ~MacroRunTask();
    void start();
    void stop();

protected slots:
    void slotProcStdOutput();
    void slotProcFinished(int exitCode, QProcess::ExitStatus exitStatus);


signals:
    void sendMessageToLog(const std::string& s);
    void sendMacroFinishedMsg(int err);
    void sendCurrentLineNumber(int line);
    void finished();
    void stopAttempFailed(int);

protected:
    void print(std::ostream&) const;  // Change to virtual if base class

private:
    // No copy allowed
    MacroRunTask(const MacroRunTask&);
    MacroRunTask& operator=(const MacroRunTask&);

    MacroEdit* editor_;
    QString macro_;
    MacroRunOptions options_;
    QProcess* proc_;

    // -- Overridden methods

    // From Task
    // From ReplyObserver
    virtual void reply(const MvRequest&, int);
    virtual void progress(const MvRequest&);
    virtual void message(const std::string&);
};


/*
class MvPlainTextEdit : public QPlainTextEdit
{
    Q_OBJECT

public:
    MvPlainTextEdit(QWidget * parent = 0);
	~MvPlainTextEdit() {};
};
*/


//class MacroEdit : public QMainWindow, private Ui::MainWindow, public MacroRunTask
class MacroEdit : public QMainWindow, private Ui::MainWindow
{
    Q_OBJECT

public:
    MacroEdit(QString appName, QWidget* parent = 0, QString* fileToLoad = nullptr);
    ~MacroEdit();
    void metview_dnd();
    void loadTextFile(const QString& fileToLoad);
    bool saveTextFile(const QString& fileToSave);
    void setLanguage(const QString& language);
    void autoInsertLicence();
    void autoInsertLanguageHeader();

    LanguageHelper* languageHelper() { return languageHelper_; }

    //void init(OdbMetaData*);
    QEventLoop eventLoop_;  // xxxx should really be private!

public slots:
    //void searchInColTree(QString);
    //void searchInTableTree(QString);
    //void mousePressedInTableGraph(int,int);
    //void selectTableFromList(QModelIndex);
    void findDialog();
    void findReplaceDialog();
    void findNext();
    void findSelection();
    void findString(const QString&, QTextDocument::FindFlags, bool replace = false, const QString& r = emptyString_);
    void replaceString(const QString&, const QString&, QTextDocument::FindFlags);
    void replaceStringInSelectedText(const QString&, const QString&, QTextDocument::FindFlags);
    void replaceStringInFile(const QString&, const QString&, QTextDocument::FindFlags);
    void findClosed();
    void gotoLine();
    void gotoLine(int line);
    void onIndentLines();
    void onSelectionChanged();
    void onCommentLines();
    void tabsDialog();
    void runDialog();
    void codeTemplateDialog();
    //void onReadOnlyStatusChanged (bool readOnly);
    void onTabsSettingsChanged(TabsSettings settings);
    void onRunOptionsChanged(MacroRunOptions options);
    void onInsertFunctionName(QString);
    void onInsertCodeTemplate(QString);
    void onInsertTextIntoFunctionCall(QString);
    void clipboardDataChanged();
    void run();
    //void debug();
    void stop();
    void monitor();
    void external();
    bool save();
    bool saveAs();
    bool reload();
    void print();
    void check_syntax();
    void documentWasModified();
    void cursorPositionWasChanged();
    void updateRowColLabel();
    void updateMessageLog();
    void logMessage(const std::string& s);
    void macroFinished(int errorCode);
    void stepLineNumber(int line);
    void enlargeFont();
    void shrinkFont();
    void normalFont();
    void largeFont();
    void showLineNumbers(bool show);
    void languageChosen(QAction*);
    void onFocusRegained();
    void onFocusLost();
    void insertLicenceText();
    void setAutoInsertLicenceOn() { setAutoInsertLicence(true); }
    void setAutoInsertLicenceOff() { setAutoInsertLicence(false); }
    void stopAttemptFailed(int pid);
    void insertText(QString);

protected slots:
    void slotChanegFont(bool);
    void slotSelectTheme(QAction* a);
    void slotFunctionList();
    void slotCodeHelper();
    void slotColourHelper();
    void taskFinished();
    void slotShowAboutBox();

signals:
    void selectionChanged(bool);

private:
    void setupEditor();
    void adjustSideBarForLanguage();
    bool AskToSave();
    void closeEvent(QCloseEvent* event);
    void setCurrentFile(const QString& fileName, bool lastSaveTimeOnly = false);
    QString strippedFileName(const QString& fullFileName);
    QString wordUnderCursor();
    QString currentFunctionUnderCursor();
    void runMacro(MacroRunOptions options);
    void statusMessage(const QString& msg, int timeToDisplay = 0);
    bool checkForErrorMessage(const std::string& s);
    void enableRunActions(bool enable);
    void changeFontSize(int amount);
    void setFontSize(int newSize);
    void highlightLine(int line);
    void setTheme(QString name);
    void writeSettings();
    void readSettings();
    void showFindDialog(bool showReplaceOptions);
    void setLanguageMenuCheckedStatus();
    void enableActionsForCurrentLanguage(bool enable);
    void setAutoInsertLicence(bool setting);
    //    void reply(const MvRequest& r,int err);

    /*void getTablePos(int,int,int&,int&);
    bool setSelectedTable(int,int);
    void renderTableGraph();
    void buildTableModel();
    bool searchInTree(QString,QTreeView*);
    void centreTableGraph(); */

    QString appName_;
    QString lastFindString_;
    QTextDocument::FindFlags lastFindFlags_;
    static QString emptyString_;

    int fontSize_;
    int highlightedLine_;
    bool readOnly_;
    bool haveFocus_;
    bool autoInsertLicence_;
    MacroRunOptions runOptions_;
    MacroRunTask* runTask_;
    FindTextDialog* findDialog_;
    GotoLineDialog* gotoLineDialog_;
    TabsDialog* tabsDialog_;
    RunDialog* runDialog_;
    FunctionListDialog* functionListDialog_;
    CodeTemplateDialog* codeTemplateDialog_;
    QString fileName_;
    QDateTime lastSaveTime_;
    QLabel* rowColLabel_;
    QLabel* messageLabel_;
    LanguageHelperFactory languageHelperFactory_;
    LanguageHelper* languageHelper_;
    Highlighter* highlighter_;
    QTimer logTimer_;
    QTimer buttonTimer_;
    QElapsedTimer time_;
    std::vector<QAction*> languageActions_;
    QString licenceText_;
    QActionGroup* autoInsertLicenceGroup_;
    QActionGroup* themeAg_;
    //MvQColourWidget* colourHelper_;
    //VerbFunctionHelpWidget* verbHelper_;
    //QComboBox *sidebarCombo_;
};
