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

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

#ifndef QMODELS_H
#define QMODELS_H

#include "../simcore/simapi.h"

#include <QAbstractTableModel>
#include <QSet>
#include <QColor>

enum {
  ccol_state, // state and avatar
  ccol_nick,
  ccol_nCols
};

enum {
  logscol_level,
  logscol_log,
  logscol_timestamp,
  logscol_nCols
};

enum {
  msgcol_nick,
  msgcol_msg,
  msgcol_timestamp,
  msgcol_nCols
};

class BaseModel : public QAbstractTableModel
{
  Q_OBJECT
public:
  BaseModel(int dcol, int tcol)
    : m_datacol(dcol), m_timecol(tcol), m_foundIndex(-1), m_lineBold(false) {}

  virtual void readSettings() = 0;

  int getDataCol() const { return m_datacol; }
  int getTimeCol() const { return m_timecol; }

  virtual int getTextIndex() const { return 0; }
  virtual const char * getSearchText(int) const = 0;
  virtual QString getSelectedText(int, bool) const = 0;
  virtual bool isEditAllowed(int) const { return false; }
  virtual const char * getEditText(unsigned *, int) { return 0; }
  virtual void setEditIndex(int) {}
  virtual int getEditIndex() const { return 0; }
  virtual void setFoundIndex(int);
  virtual int getFoundIndex() const { return m_foundIndex; }

  virtual char isNewDate(QColor * background, QColor * foreground, bool * bold) const = 0;
  virtual bool isRemoved(int) const { return false; }
  virtual char isReceived(int) const { return 0; }
  virtual char getMsgType(int) const { return 0; }
  virtual const char * getMsgDate(int ndx) const = 0;

  virtual void clearCache() {}
  virtual void setOldRowCount(unsigned) {}
  virtual bool doNotShowSelected(int /* row */) const = 0;

  virtual void reset() = 0;
  void notifyRowsInserted(int start, int end);
  void notifyRowsDeleted(int start, int end);
  virtual void notifyRowsChanged() = 0;

signals:
  void signalFoundUnseenNick(const QString &) const;

protected:
  int m_datacol;
  int m_timecol;
  int m_foundIndex;
  bool m_lineBold; // value of ui.chat.linebold / ui.console.linebold
};

class MessagesModel : public BaseModel
{
  Q_OBJECT
public:
  explicit MessagesModel(int contactId);
  ~MessagesModel() Q_DECL_OVERRIDE;

  void readSettings() Q_DECL_OVERRIDE;

  unsigned count() const { return m_size; }
  int rowCount(const QModelIndex & /*parent = QModelIndex()*/) const Q_DECL_OVERRIDE { return count(); }
  int columnCount(const QModelIndex & /*parent = QModelIndex()*/) const Q_DECL_OVERRIDE { return msgcol_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;

  int getTextIndex() const Q_DECL_OVERRIDE { return m_lastIndex; }
  char getMessage(int ndx) const;
  const char * getSearchText(int ndx) const Q_DECL_OVERRIDE;
  QString getSelectedText(int ndx, bool textOnly) const Q_DECL_OVERRIDE;
  bool isEditAllowed(int ndx) const Q_DECL_OVERRIDE;
  const char * getEditText(unsigned * rowNdxBack, int delta) Q_DECL_OVERRIDE;
  void setEditIndex(int ndx) Q_DECL_OVERRIDE;
  int getEditIndex() const Q_DECL_OVERRIDE { return m_editIndex; }
  const char * getMessageText(int ndx) const;
  int getMessageStatus(int ndx) const;
  const char * getMessageTime(int ndx) const;
  QString getMessageTimes(int ndx) const;

  char isNewDate(QColor * background, QColor * foreground, bool * bold) const Q_DECL_OVERRIDE;
  char isEdited(QColor * color) const;
  bool isRemoved(int ndx) const Q_DECL_OVERRIDE;
  char isReceived(int ndx) const Q_DECL_OVERRIDE;
  char getMsgType(int ndx) const Q_DECL_OVERRIDE;

  const char * getMsgDate(int ndx) const Q_DECL_OVERRIDE;
  QString getMessageNick(int ndx) const;
  bool isSameNick(int ndx) const;

  void clearCache() Q_DECL_OVERRIDE { m_lastIndex = -1; }
  void setOldRowCount(unsigned nRows) Q_DECL_OVERRIDE { m_oldSize = nRows; }
  bool doNotShowSelected(int row) const Q_DECL_OVERRIDE { return row == m_editIndex || row == m_foundIndex; }

  void reset() Q_DECL_OVERRIDE { m_usedNicks.clear(); }
  void notifyRowsChanged() Q_DECL_OVERRIDE;

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

private:
  int m_contactId;
  int m_editIndex;
  int m_editCount; // number of sent outgoing messages after the edit index

  mutable unsigned m_size;    // known number of messages
  mutable unsigned m_oldSize; // number of messages on window close

  mutable int m_lastIndex;           // cache for last used message: index
  mutable simnumber m_lastTimestamp; // timestamp
  mutable simnumber m_lastRecvtime;  // received time
  mutable simnumber m_prevTimestamp; // timestamp of previous message
  mutable simnumber m_prevRecvtime;  // received time of previous message
  mutable int m_lastStatus;          // status
  mutable int m_prevStatus;          // status of previous message
  mutable std::string m_lastMessage; // message itself
  mutable QString m_lastNick;        // nick of the person (could change in different messages)
  mutable bool m_lastNickSame;       // nick in message is the same as in the previous message
  mutable bool m_lastMessageRemoved; // message has no text
  mutable bool m_lastTimeDiff;
  mutable char m_lastDateDiff;
  mutable char m_lastType;
  mutable char m_prevType;
  mutable simnumber m_editedTstamp;   // file transfer handle
  mutable QColor m_editReceivedColor; // value of ui.chat.editreceived
  mutable QColor m_editSentColor;     // value of ui.chat.editsent
  mutable QColor m_systemColor;       // value of ui.chat.system
  mutable QColor m_missedColor;       // value of ui.chat.missed
  mutable QColor m_receivedColor;     // value of ui.chat.received
  mutable QColor m_sentColor;         // value of ui.chat.sent
  mutable QColor m_notsentColor;      // value of ui.chat.notsent
  mutable int m_showTime;             // value of ui.chat.showtime
  mutable bool m_showStatus;          // value of ui.chat.showstatus
  mutable int m_showDuration;         // value of ui.chat.duration
  mutable unsigned m_xferMaxSize;     // value of ui.chat.xfersize
  mutable int m_oldTime;              // value of ui.chat.oldtime
  mutable int m_oldDate;              // value of ui.chat.olddate
  mutable QColor m_lineColor;         // value of ui.chat.line
  mutable QColor m_dateColor;         // value of ui.chat.date
  mutable QColor m_backtimeColor;     // value of ui.chat.backtime
  mutable QColor m_backdateColor;     // value of ui.chat.backdate

  mutable QSet<QString> m_usedNicks;
};

class LogsModel : public BaseModel
{
  Q_OBJECT
public:
  LogsModel();
  ~LogsModel() Q_DECL_OVERRIDE;

  void readSettings() Q_DECL_OVERRIDE;

  int rowCount(const QModelIndex & /*parent = QModelIndex()*/) const Q_DECL_OVERRIDE;
  int columnCount(const QModelIndex & /*parent = QModelIndex()*/) const Q_DECL_OVERRIDE { return logscol_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;

  const char * getSearchText(int ndx) const Q_DECL_OVERRIDE;
  QString getSelectedText(int ndx, bool textOnly) const Q_DECL_OVERRIDE;

  char isNewDate(QColor * background, QColor * foreground, bool * bold) const Q_DECL_OVERRIDE;
  const char * getMsgDate(int ndx) const Q_DECL_OVERRIDE;

  bool doNotShowSelected(int row) const Q_DECL_OVERRIDE { return row == m_foundIndex; }

  void reset() Q_DECL_OVERRIDE { m_usedLevels.clear(); }
  void notifyRowsChanged() Q_DECL_OVERRIDE;

private:
  mutable QColor m_lineColor; // value of ui.console.line
  mutable QColor m_dateColor; // value of ui.console.date
  mutable QColor m_timeColor; // value of ui.console.time
  mutable QSet<QString> m_usedLevels;
};

#endif
