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

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

#ifndef CONTACTS_H
#define CONTACTS_H

#include "simcore.h"
#include "data.h"

#include <QMainWindow>
#include <QTableView>
#include <QSystemTrayIcon>
#include <QStyledItemDelegate>
#include <QTextEdit>
#include <QSessionManager>

namespace Ui {
class Contacts;
}

class ContactsModel : public QAbstractTableModel
{
  Q_OBJECT
public:
  enum E_SortType {
    sort_none = -1,
    sort_level,
    sort_nick
  };

  ContactsModel();
  ~ContactsModel() Q_DECL_OVERRIDE;

  void readSettings(bool noAvatar);

  int rowCount(const QModelIndex & /*parent = QModelIndex()*/) const Q_DECL_OVERRIDE { return int(m_mapRow2Id.size()); }
  int columnCount(const QModelIndex & /*parent = QModelIndex()*/) const Q_DECL_OVERRIDE { return ccol_nCols; }
  QVariant data(const QModelIndex & index, int role) const Q_DECL_OVERRIDE;
  QVariant headerData(int section, Qt::Orientation orientation, int role) const Q_DECL_OVERRIDE;

  void emitDataChanged(int id, int column);

  Contact * getContact(const QModelIndex & index) const
  {
    return index.row() >= int(m_mapRow2Id.size()) ? 0 : SimCore::getContact(m_mapRow2Id[index.row()]);
  }

  int getRow(int id) const { return unsigned(id) >= m_mapId2Row.size() ? -1 : m_mapId2Row[id]; }

public slots:
  void onSignalContactAdded(int nContacts);
  void onSignalContactStatusChanged(unsigned id, int oldStatus, int newStatus);
  void onSignalContactAudioChanged(unsigned id, SimAudioState);
  void onSignalContactChanged(unsigned id);

public:
  void clear();
  void refilter();

  int getCurContactId() const { return m_curContactId; }
  void setCurContactId(int curContactId) { m_curContactId = curContactId; }

  void setShowDeleted(bool showDeleted);
  void setSortBy(E_SortType sortBy);

  void setSort(E_SortType sort) { m_sort = sort; }
  E_SortType getSort() const { return m_sort; }

  void sortContacts(int start, int end);
  void sortContacts();
  bool compareContacts(int leftId, int rightId) const;

  std::vector<unsigned> m_mapRow2Id; // remap row to contact index
  std::vector<int> m_mapId2Row;      // remap contact index to row

protected:
  QColor m_highlightColor;   // value of ui.color.highlight
  QColor m_highlightedColor; // value of ui.color.highlighted
  bool m_showDeleted;
  E_SortType m_sort;
  int m_curContactId;
  bool m_noAvatar;
  bool m_showInfoLine;
};

class StatusDelegate : public QStyledItemDelegate
{
  Q_OBJECT

  typedef QStyledItemDelegate Parent;

public:
  explicit StatusDelegate(QObject * parent = 0);

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

  QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const Q_DECL_OVERRIDE;

  QPixmap m_smallPixmaps[Contact::state_Nstates];
  QPixmap m_largePixmaps[Contact::state_Nstates];

  void setAvatarPaint(int noAvatar) { m_noAvatar = noAvatar; }
  int getAvatarPaint() const { return m_noAvatar; }
  QSize getDefaultAvatarSize() const { return m_avatarSize; }

  void setShowOfflineIcon(bool showOffline) { m_offlineIcon = showOffline; }

private:
  QImage m_avatar;
  QPixmap m_call, m_callLarge;
  QPixmap m_file, m_fileLarge;
  QPixmap m_msg, m_msgLarge;
  QPixmap m_typing, m_typingLarge;
  int m_noAvatar;
  bool m_offlineIcon;
  QSize m_avatarSize;
};

class ContactsTableView : public QTableView
{
  typedef QTableView Parent;

public:
  explicit ContactsTableView(QWidget * parent = 0)
    : Parent(parent)
    , m_mouseButtonUsed(Qt::NoButton) {}

  void mousePressEvent(QMouseEvent * event) Q_DECL_OVERRIDE;

  Qt::MouseButton m_mouseButtonUsed;
};

class Contacts : public QMainWindow
{
  Q_OBJECT

  typedef QMainWindow Parent;

public:
  explicit Contacts(QWidget * parent = 0);
  ~Contacts() Q_DECL_OVERRIDE;

  static Contacts * get() { return mc_contacts; }

  void setAutoStart(bool autostart) { m_autostart = autostart; }
  static void setExtensions(const QString & xss, bool xcb);

  static int getExtension(char extension);
  bool getStayInTaskbar() const;

  const QIcon & getStatusIcon(Contact * contact = 0) const;
  static QString getStatusToolTip(int status);

  static simnumber getContactFlags(Contact * contact);
  QTextEdit * getTextWidget();

  void setApplicationActive(bool active);
  bool isApplicationActive() const { return m_active; }

  bool isSplitMode() const;
  bool isAvatarMode() const { return !m_noAvatar; }
  bool isClickMode() const { return m_click; }

  void clearModel() { m_model.clear(); }
  void setQuitting() { m_doQuit = true; }
  bool isQuitting() const { return m_doQuit; }
  bool isCommitting() const { return m_committing; }
  bool isCommitted() const { return m_committed; }

  static QWidget * hasActiveWindow();

  void saveSettings(bool quit, bool mode = false) const;

  void rereadSettings();
  void updateSettings() const;
  void readSettings();

  void fixMenuIndicators() const;

  void blinkTrayIcon(bool taskbar);
  void unblinkTrayIcon(char action);

  int setMyStatus(int status, int idle = 0);
  void setMyStatusIcon(int status, const QString & statusName);

  void showStatus(const QString & text) const;
  void showStatus(const QString & text, char color) const;

  void showDialog(Contact * contact, const QPoint & pos, int msgNdx);

  void acceptContact(const QString & nick);
  void renameContact(Contact * contact);
  void changeInfoContact(Contact * contact);
  void forgetContact(Contact * contact);
  void blockContact(Contact * contact);
  void unblockContact(Contact * contact);
  void callContact(Contact * contact);
  void hangupContact(Contact * contact);
  void changeAvatar(Contact * contact);

  void doSettingsAction(Contact * contact, QAction * actions[], int i);
  void addSettingsActions(QMenu * menu, Contact * contact, QAction * actions[]);

  void emitSettingsChanged() { emit signalSettingsChanged(); }

  bool isContactShown(Contact * contact) const;

  void activateContactChat(int id, int popupNotify);

  void doHide();
  void doQuit();

private:
  void doLogout();
  void recalcTitle(int id);

  bool eventFilter(QObject * obj, QEvent * event) Q_DECL_OVERRIDE;

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

  void showEvent(QShowEvent * event) Q_DECL_OVERRIDE;

#ifdef _WIN32
  bool nativeEvent(const QByteArray & eventType, void * message, long * result) Q_DECL_OVERRIDE;
#endif

signals:
  void signalLogout();
  void signalSettingsChanged();
  void signalStatusChanged(int);
  void signalFontChanged();

public Q_SLOTS:
  void activateConsole();

  void showActivateWindow();
  void showSpontaneousWindow();

  void blinkWindow();
  void unblinkTrayIcon();

  void onLayoutChanged(QWidget * chatWindow = 0);
  void onDockMenuTriggered();

  void onRenameButtonClicked();

  void on_contactView_pressed(QModelIndex ndx);
  void on_contactView_clicked(QModelIndex ndx);
  void on_contactView_doubleClicked(QModelIndex ndx);

  void on_actionHideAll_triggered();
  void on_actionQuit_triggered();

private slots:
  void on_actionShowMainWindow_triggered();

  void on_actionOnline_triggered();
  void on_actionAway_triggered();
  void on_actionBusy_triggered();
  void on_actionInvisible_triggered();
  void on_actionOffline_triggered();

  void on_actionEnglish_triggered();
  void on_actionFrench_triggered();
  void on_actionGerman_triggered();
  void on_actionUserManual_triggered();
  void on_actionAbout_triggered();

  void on_actionAddNewContact_triggered();
  void on_actionShowDeleted_triggered();
  void on_actionSortByNick_triggered();

  void on_actionTransfers_triggered();
  void on_actionSplitMode_triggered();
  void on_actionSettings_triggered();
  void on_actionPreferences_triggered();
  void on_actionLogout_triggered();

  void on_action0_triggered() { doSettingsAction(m_settingsContact, m_actions, 0); }
  void on_action1_triggered() { doSettingsAction(m_settingsContact, m_actions, 1); }
  void on_action2_triggered() { doSettingsAction(m_settingsContact, m_actions, 2); }
  void on_action3_triggered() { doSettingsAction(m_settingsContact, m_actions, 3); }
  void on_action4_triggered() { doSettingsAction(m_settingsContact, m_actions, 4); }
  void on_action5_triggered() { doSettingsAction(m_settingsContact, m_actions, 5); }
  void on_action6_triggered() { doSettingsAction(m_settingsContact, m_actions, 6); }
  void on_action7_triggered() { doSettingsAction(m_settingsContact, m_actions, 7); }
  void on_action8_triggered() { doSettingsAction(m_settingsContact, m_actions, 8); }
  void on_action9_triggered() { doSettingsAction(m_settingsContact, m_actions, 9); }
  void on_action10_triggered() { doSettingsAction(m_settingsContact, m_actions, 10); }
  void on_action11_triggered() { doSettingsAction(m_settingsContact, m_actions, 11); }

  void onStatusMessageChanged();

  void onFocusWindowChanged(QWindow * focusWindow);
  void onRowsInserted(const QModelIndex & parent, int first, int last);

  void onAboutToQuit();
  void onCommitDataRequest(QSessionManager & manager);
  void onSaveStateRequest(QSessionManager & manager);

  int onAwayTimeout(int force = 0);
  void onNotifyIconTimeout();
  void onTrayIconTimeout();

  void onSignalInvalidAudioDevice(bool valid);
  void onSignalContactAudioChanged(unsigned id, SimAudioState);
  void onSignalContactChanged(unsigned contactId);

  void onCustomContextMenu(const QPoint & pos);
  void onCustomContextMenuStatus(const QPoint & pos, bool global = false);

  void onTrayIconActivated(QSystemTrayIcon::ActivationReason reason);
  void onTrayMenuAboutToShow();

  void onTrayIconChanged() { removeTrayIcon(), createTrayIcon(); }

private:
  void createTrayIcon();
  void updateTrayIcon();
  void removeTrayIcon();

  Ui::Contacts * ui;
  static Contacts * mc_contacts; // the one and only
  static QFunctionPointer mc_XScreenSaverQueryExtension;
  static QFunctionPointer mc_XScreenSaverQueryInfo;

  QMenu * m_dockMenu;
  QSystemTrayIcon * m_trayIcon;
  QMenu * m_trayIconMenu;
  QAction * m_onlineAction;
  QAction * m_awayAction;
  QAction * m_busyAction;
  QAction * m_invisibleAction;
  QAction * m_offlineAction;
  QAction * m_showAction;
  QAction * m_hideAction;
  QAction * m_quitAction;
  QLabel * m_status;
  StatusDelegate * m_statusDelegate;
  ContactsModel m_model;
  unsigned m_dockedSize;
  int m_restoring;
  int m_tray;
  int m_trayHtml;
  int m_noAvatar;
  int m_shutdownPhase;
  int m_titleMax;
  bool m_click;
  bool m_showOffline;
  bool m_committing;
  bool m_committed;
  bool m_autostart;
  bool m_doQuit;
  bool m_active;
  bool m_alert;
  bool m_reopened;
  static bool mc_hotKeys;
  static int mc_autoAway;
  int m_trayNotify;
  int m_minimized;
  QTimer m_timer;
  QTimer m_timerNotifyIcon;
  unsigned m_awayTimeSec;

  QString m_lastAddAddress; // last entered incorrect address to add
  QTextEdit * m_welcome;
  QDialog * m_rename;
  QAction * m_actions[12]; // NACT == 12
  Contact * m_settingsContact;

  QIcon m_trayIconOn;
  QIcon m_trayIconNotify;
  QIcon m_trayIconOff;
  QIcon m_trayIconBusy;
  QIcon m_trayIconInvisible;
  QIcon m_trayIconAway;
  QIcon m_trayIconUnknown;
  QIcon * m_trayIcons[SIM_STATUS_MAX - SIM_STATUS_MIN + 1];

  QColor m_infoColor;  // value of ui.color.infos
  QColor m_noteColor;  // value of ui.color.notes
  QColor m_warnColor;  // value of ui.color.warns
  QColor m_errorColor; // value of ui.color.errors
};

#endif
