/*
    Copyright 2011-2012 Heikki Holstila <heikki.holstila@gmail.com>

    This file is part of FingerTerm.

    FingerTerm is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 2 of the License, or
    (at your option) any later version.

    FingerTerm is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with FingerTerm.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef TERMINAL_H
#define TERMINAL_H

#include <QtCore>

class PtyIFace;
class Util;
class QQuickView;

struct TermChar
{
    QChar c;
    int fgColor;
    int bgColor;
    int attrib;
};

const int attribNone = 0;
const int attribBold = 1;
const int attribUnderline = 2;
const int attribNegative = 4;
const QByteArray multiCharEscapes("().*+-/%#");

struct TermAttribs
{
    QPoint cursorPos;

    bool wrapAroundMode;
    bool originMode;

    int currentFgColor;
    int currentBgColor;
    int currentAttrib;
};

struct TermLine
{
    QList<TermChar> l;
    int fgColor;
    int bgColor;
    int attrib;

    TermLine()
        : fgColor(0), bgColor(0), attrib(0)
    {
    }

    TermChar& operator[](int i)
    {
        return l[i];
    }

    int size() const
    {
        return l.size();
    }

    void clear()
    {
        return l.clear();
    }

    void append(const TermChar &c)
    {
        l.append(c);
    }
};

class Terminal : public QObject
{
    Q_OBJECT
    Q_PROPERTY(int rows READ rows NOTIFY termSizeChanged)
    Q_PROPERTY(int columns READ columns NOTIFY termSizeChanged)

public:
    static const int defaultFgColor = 7;
    static const int defaultBgColor = 0;

    explicit Terminal(QObject *parent = nullptr);
    virtual ~Terminal() {}

    void setPtyIFace(PtyIFace* pty);
    void setWindow(QQuickView* win) { iWindow = win; }
    void setUtil(Util* util) { iUtil = util; }

    void insertInBuffer(const QString &chars);

    QPoint cursorPos();
    void setCursorPos(QPoint pos);
    bool showCursor();

    QSize termSize() { return iTermSize; }
    void setTermSize(QSize size);

    QList<TermLine>& buffer();
    QList<TermLine>& backBuffer() { return iBackBuffer; }

    TermLine& currentLine();

    Q_INVOKABLE void keyPress(int key, int modifiers, const QString &text = "");
    Q_INVOKABLE QStringList printableLinesFromCursor(int lines);
    Q_INVOKABLE void putString(QString str, bool unEscape = false);

    Q_INVOKABLE void pasteFromClipboard();
    Q_INVOKABLE void copySelectionToClipboard();
    Q_INVOKABLE QStringList grabURLsFromBuffer();

    Q_INVOKABLE QString getUserMenuXml();
    Q_INVOKABLE void clearSelection();

    void scrollBackBufferFwd(int lines);
    void scrollBackBufferBack(int lines);
    int backBufferScrollPos() { return iBackBufferScrollPos; }
    void resetBackBufferScrollPos();

    void setSelection(QPoint start, QPoint end, bool selectionOngoing);
    QRect selection();
    bool hasSelection();

    int rows();
    int columns();

    TermChar zeroChar() const;

signals:
    void cursorPosChanged(QPoint newPos);
    void termSizeChanged(int rows, int columns);
    void displayBufferChanged();
    void selectionChanged();
    void scrollBackBufferAdjusted(bool reset);
    void selectionFinished();

private:
    Q_DISABLE_COPY(Terminal)

    static const char ch_ESC = 0x1B; // escape
    static const int maxScrollBackLines = 300;

    void insertAtCursor(QChar c, bool overwriteMode = true, bool advanceCursor = true);
    void deleteAt(QPoint pos);
    void clearAt(QPoint pos);
    void eraseLineAtCursor(int from = -1, int to = -1);
    void clearAll(bool wholeBuffer = false);
    void ansiSequence(const QString &seq);
    void oscSequence(const QString &seq);
    void escControlChar(const QString &seq);
    void trimBackBuffer();
    void scrollBack(int lines, int insertAt = -1);
    void scrollFwd(int lines, int removeAt = -1);
    void resetTerminal();
    void resetTabs();
    void adjustSelectionPosition(int lines);

    PtyIFace* iPtyIFace;
    QQuickView* iWindow;
    Util* iUtil;

    QList<TermLine> iBuffer;
    QList<TermLine> iAltBuffer;
    QList<TermLine> iBackBuffer;
    QList<QList<int> > iTabStops;

    QSize iTermSize;
    bool iEmitCursorChangeSignal;

    bool iShowCursor;
    bool iUseAltScreenBuffer;
    bool iAppCursorKeys;
    bool iReplaceMode;
    bool iNewLineMode;

    int iMarginTop;
    int iMarginBottom;

    int iBackBufferScrollPos;

    TermAttribs iTermAttribs;
    TermAttribs iTermAttribs_saved;
    TermAttribs iTermAttribs_saved_alt;

    QString escSeq;
    QString oscSeq;
    int escape;
    QRect iSelection;
};

#endif // TERMINAL_H
