/* Copyright (c) 2020-2021 The Creators of Simphone

   See the file COPYING.LESSER.txt for copying permission.
*/

#ifndef CHATFRAME_H
#define CHATFRAME_H

#include "simcore.h"
#include "qmodels.h"

#include <QTableView>
#include <QStyledItemDelegate>

namespace Ui {
class ChatFrame;
}

class DateDelegate : public QStyledItemDelegate
{
  Q_OBJECT

  typedef QStyledItemDelegate Parent;

public:
  DateDelegate(QAbstractScrollArea * parent)
    : m_parent(parent) {}

  void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const Q_DECL_OVERRIDE;

private:
  QAbstractScrollArea * m_parent;
};

class ChatTableView : public QTableView
{
  Q_OBJECT

  typedef QTableView Parent;

public:
  explicit ChatTableView(QWidget * parent = 0)
    : QTableView(parent)
    , m_scrollToBottomOnResize(true)
    , m_testUser(false)
    , m_timeColSize(0)
    , m_nickColSize(0)
    , m_scrollToBottomSafeCounter(0) {}

  void resizeEvent(QResizeEvent * event) Q_DECL_OVERRIDE;

  void scrollContentsBy(int dx, int dy) Q_DECL_OVERRIDE;

  bool calcNickSize(const QString & nick);

  void clearColSizes() { m_timeColSize = m_nickColSize = 0; }

  void setTest(bool test) { m_testUser = test; }

public slots:
  void resizeRowContentVisiblePart(bool last256 = false);

  void scrollToBottomSafe();

protected:
  void scrollToBottom();
  void updateGeometries() Q_DECL_OVERRIDE;

  bool m_scrollToBottomOnResize;
  bool m_testUser;
  unsigned m_timeColSize;
  unsigned m_nickColSize;
  unsigned m_scrollToBottomSafeCounter;
  friend class ChatFrame;
};

class ChatFrame : public QFrame
{
  Q_OBJECT

  typedef QFrame Parent;

  Ui::ChatFrame * ui;
  Contact * m_contact;
  BaseModel * m_chatModel;

public:
  explicit ChatFrame(Contact * contact, BaseModel * chatModel);
  ~ChatFrame();

  Contact * getContact() { return m_contact; }

  void getSettings(simtype params);
  void readSettings();

  void installFilter(QObject * obj);
  void removeFilter(QObject * obj);

  void setShowMinimized() { m_showMinimized = true; }

  bool isFocused();
  bool hasAutoScroll() { return m_autoScroll; }

private:
  void setAutoScrollToolTip();
  void setAutoScroll(bool checked);
  void scroll();

  void enableChat(bool enabled);
  void enableCall(bool enabled);

  void setVerified(bool verified);
  void showAuthText();

  void setEditText(const QString & text);
  void sendEditText();
  bool eventFilter(QObject * obj, QEvent * event) Q_DECL_OVERRIDE;

  void findCommand(int direction);
  void findMessage(int direction);
  void findOpen(int startNdx);
  void changeFindColor(bool error);

  void copySelection(bool textOnly);

  void editMessage(const char * text);
  void editFinish();

  int loadMessages(int nMsgs, bool ignoreErrors = true);

  int removeMessage(int row);
  void removeMessages(int row);
  void removeMessages();

  void closeEvent(QCloseEvent * event) Q_DECL_OVERRIDE;
  void changeEvent(QEvent * event) Q_DECL_OVERRIDE;

  void showEvent(QShowEvent * event) Q_DECL_OVERRIDE;
  void hideEvent(QHideEvent * event) Q_DECL_OVERRIDE;
  void focusInEvent(QFocusEvent * event) Q_DECL_OVERRIDE;

private slots:
  void on_callButton_clicked();
  void on_hangupButton_clicked();
  void on_verifyButton_clicked();
  void on_scrollButton_clicked();
  void on_findEdit_returnPressed();
  void on_findCloseButton_clicked();
  void on_findNextButton_clicked();
  void on_findPreviousButton_clicked();

  void onLayoutChanged(const QList<QPersistentModelIndex> &, QAbstractItemModel::LayoutChangeHint);
  void onHeaderChanged(Qt::Orientation orientation, int first, int last);
  void onRowsInserted(const QModelIndex & parent, int first, int last);
  void onScrollChanged(int min = 0, int max = 0);
  void onRowDoubleClicked(const QModelIndex & index);
  void onRowClicked(const QModelIndex & index);
  void onVisibilityChanged(bool visible);

  void onTextChanged();
  void onTypingTimeout();

  void onSignalFoundUnseenNick(const QString & nick);
  void onSignalMessageReceived(unsigned id, int, bool noNotify);
  void onSignalMessageEdited(unsigned id, int msgNdx);
  void onSignalContactAudioChanged(unsigned id, SimAudioState newState);
  void onSignalContactChanged(unsigned id);
  void onSignalContactConnectionChanged(unsigned id, int connectionState);

  void onCustomContextMenu(const QPoint & pos);
  void onCustomContextMenuInfo(const QPoint & pos);
  void onCustomContextMenuConnection(const QPoint & pos);

signals:
  void signalContactQuit(); // used when quit command entered in console

protected:
  int m_lastRowClicked;
  int m_startFindFrom;
  unsigned m_editNdx;
  bool m_callEnabled;
  bool m_shown;
  QDialog * m_verifyDialog;   // QInputDialog or QMessageBox if open or NULL if not open
  QString m_verifyCode;       // last entered (incorrect) verification code
  QString m_original;         // string before edit
  QStringList m_lastCommands; // preserve last commands send to test (to reuse them)
  int m_cmdNdx;
  bool m_autoScroll;
  bool m_disableBottomScroll;
  bool m_focusFind;
  bool m_showInfoLine;    // low bit of ui.main.infoline
  bool m_showConnection;  // value of ui.chat.connection / ui.console.connection
  bool m_showAll;         // value of ui.chat.showall / ui.console.showall
  bool m_altArrow;        // value of ui.chat.altarrow
  int m_ctrlEnter;        // value of ui.chat.ctrlenter
  int m_escape;           // value of ui.chat.escape / ui.console.escape
  bool m_copyText;        // value of ui.chat.copytext / ui.console.copytext
  QColor m_notfoundColor; // value of ui.chat.notfound / ui.console.notfound
  int m_loadNmsgs;        // value of ui.chat.load / ui.console.load
  bool m_someLoaded;
  bool m_allLoaded;
  bool m_noneLoaded;
  bool m_typing;
  bool m_showMinimized;
  bool m_connected;
  bool m_splitterInitialized;
  QTimer m_timer;
  DateDelegate * m_dateDelegate;

  static QPixmap * mc_connectionStatePixmaps[SimCore::state_NConnectionStates];
};

class ChatFrames : public QObject // singleton
{
  Q_OBJECT

  typedef QObject Parent;

public:
  ChatFrames();
  ~ChatFrames() { mc_chatFrames = 0; }

  ChatFrame * getFrame(Contact * contact);
  int getLastChatId();

  static ChatFrames * get() { return mc_chatFrames; }

  void activateChat(Contact * contact, int popupNotify, bool inWindow); // return true if visible

  void closeAll();
  void detachAll();

  void notifyChatUsed(unsigned id);
  void notifyChatStopped(unsigned id);

public slots:
  void onSignalMessageReceived(unsigned id, int, bool noNotify);
  void onSignalMessageEdited(unsigned id, int msgNdx);

protected:
  static ChatFrames * mc_chatFrames; // the one and only
  QList<unsigned> m_lastContactUsed;
};

#endif
