/***************************************************************************
 *   Copyright (C) 2007 by Anistratov Oleg                                 *
 *   ower86@gmail.com                                                      *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License version 2        *
 *   as published by the Free Software Foundation;                         *
 *                                                                         *
 *   This program 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.                          *
 *                                                                         *
 ***************************************************************************/
#ifndef ABSTRACTCORE_H
#define ABSTRACTCORE_H

#include <QHostAddress>

// TODO remove "globals.h" from this header
#include "globals.h"
#include "abstractprotocol.h"

class QC_DatagramHeader;
class AbstractProtocol;
class ProtocolVersion3;
class ProtocolVersion4;

bool isSystemMsg(quint16 type);


/**
  @author Anistratov Oleg <ower86@gmail.com>
  @brief Abstract class for manipulating with QChat's protocol
*/
class AbstractChatCore
{
  private:
    static AbstractProtocol* m_protocol;
    static ProtocolVersion3* m_protocolV3;
    static ProtocolVersion4* m_protocolV4;
    /// Output buffer - here writes data(<= 2^16-1) before sending
    char* m_outputBuffer;

    /// Size of m_outputBuffer
    uint  m_outputBufferSize;

    /// Input buffer - here writes data(<= 2^16-1) after receiving
    char* m_inputBuffer;

    /// Size of m_inputBuffer
    uint  m_inputBufferSize;

    /// Flag shows that size of sending packet > 2^16-1
    bool  m_largeDtgrm;

    /** Used for sending large(> 2^16-1) packets
     *  Stores header of large packet
     */
    char*           m_header;
    /// Size if m_header
    int             m_headerSize;

    /** Used for sending large(> 2^16-1) packets
     *  Stores data(message and Parametrs array) of large packet
     */
    char*           m_data;
    /// Size if m_data
    int             m_dataSize;

    QByteArray      m_parametrs;

    /// Stores data(name and sender's IP, message, etc) received after processing m_inputBuffer
    QC_DatagramHeader* Hdr;

  public:
    enum ChannelType{Common, Private};

    enum DataType
    {
      MESSAGE = 0,
      INFORMATION,
      CONNECTED,
      DISCONNECTED,
      AVATAR_REQUEST,
      AVATAR_ANSWER,
      STATUS_REQUEST,
      STATUS_ANSWER,
      INFO_REQUEST,
      INFO_ANSWER,
      INF_STATUS_CHNGD = 10,
      FRAGMENTS_REQUEST,
      SINGLE_MESSAGE,
      FILE,
      ACCEPT,
      REJECT,
      CONFIRM,
      FINISHED,
      PRIVATE_CHAT_REQUEST,
      MSGS_HISTORY_REQUEST,
      MSGS_HISTORY_ANSWER,
      MSGS_NUM_REQUEST,
      MSGS_NUM_ANSWER,
      PLUGIN_DATA,

      WANT_LOGIN,
      LOGIN_ACCEPTED,
      LOGIN_REJECTED,
      CHANGE_LOGIN,
      INSTRUCTION
    };

  public:
    AbstractChatCore();
    virtual ~AbstractChatCore();

//     virtual void sendData(const QHostAddress &) = 0;

    // get-set
    char* outputBuffer    (){return m_outputBuffer;}
    int   outputBufferSize(){return m_outputBufferSize;}
    char* header          (){return m_header;}
    int   headerSize      (){return m_headerSize;}
    char* data            (){return m_data;}
    int   dataSize        (){return m_dataSize;}
    void  setNULLdataAndHeader(){m_data = NULL; m_header = NULL;}
    bool  largeDtgrm      (){return m_largeDtgrm;}
    QC_DatagramHeader* hdr(){return Hdr;}
    void newHdr();
    /*non const!*/ QByteArray&  parametrs(){return m_parametrs;}

    void setInputBuffer (char* data, int dataSize){memcpy(m_inputBuffer , data, dataSize);  m_inputBufferSize = dataSize;}
    void setOutputBuffer(char* data, int dataSize){memcpy(m_outputBuffer, data, dataSize);  m_outputBufferSize = dataSize;}

    /**
     * @brief Preparing data for sending
     * @param dtgrm_type - paket type
     * @param dest_uid - user id of receiver(in serverless mode this is IP address)
     * @param nickname - sender's nickname
     * @param compname - name of sender's computer
     * @param src_uid - user id of sender(in serverless mode this is IP address)
     * @param msg - text message
     * @param chnnl_type - type of channel(Common, Private, etc)
     * @param dtgrm_id - paket id(for fragmented pakets)
     * @param dtgrm_num - paket number(for fragmented pakets)
     */
    void prepareDatagram(AbstractChatCore::DataType dtgrm_type, quint64 dest_uid,
                         const QString& nickname, const QString& compname, quint64 src_uid,
                         const QString & msg  = "",
                         ChannelType chnnl_type = Common, unsigned long dtgrm_id = 0, unsigned long dtgrm_num = 0);

    bool fillHeader();

    static QByteArray getParametr(const QString & par_name, const QByteArray & pars);
    static void addParametr(const QString & par_name, const QByteArray & par, QByteArray & buf);
    static QColor getColorParametr(QByteArray*);

    void addParametr      (const QString & par_name, const QByteArray & par);
    QByteArray getParametr(const QString & par_name);
    void clearParametrs   ();
    void addParametr(const QString & par_name, const QString & filename);

    QMap<QString, QByteArray> getAllParametrs(const QByteArray &);

    // NOTE size of char* buf in following functions must be at least AbstractChatCore::protocolLen() bytes

    static uint    packetSize   (const char* buf)   {return m_protocol->packetSize(buf);}
    static void    setPacketSize(char* buf, uint sz){m_protocol->setPacketSize(buf, sz);}
    void           setPacketSize(uint sz)           {m_protocol->setPacketSize(m_outputBuffer, sz);}

    static QString programId       (const char* buf){return m_protocol->programId      (buf);}
    static uint    programVersion  (const char* buf){return m_protocol->programVersion (buf);}
    static uint    protocolVersion (const char* buf){return m_protocol->protocolVersion(buf);}
    static uint    checkProtocolVersion(const char* buf);
    static quint64 destIp          (const char* buf){return m_protocol->destIp         (buf);}
    static quint64 srcIp           (const char* buf){return m_protocol->srcIp          (buf);}
    static uint    packetType      (const char* buf){return m_protocol->packetType     (buf);}
    static uint    packetId        (const char* buf){return m_protocol->packetId       (buf);}
    static uint    fragmentSize    (const char* buf){return m_protocol->fragmentSize   (buf);}
    static uint    packetNum       (const char* buf){return m_protocol->packetNum      (buf);}
    static quint64 time            (const char* buf){return m_protocol->time           (buf);}
    static uint    channelType     (const char* buf){return m_protocol->channelType    (buf);}

    static uint    compNameLen     (const char* buf){return m_protocol->compNameLen    (buf);}
    static uint    userNameLen     (const char* buf){return m_protocol->userNameLen    (buf);}
    static uint    messageLen      (const char* buf){return m_protocol->messageLen     (buf);}
    static uint    parametrsLen    (const char* buf){return m_protocol->parametrsLen   (buf);}
    static uint    optionsLen      (const char* buf){return m_protocol->optionsLen     (buf);}
    static bool    compressed      (const char* buf){return m_protocol->compressed     (buf);}

    static QString compName        (const char* buf, uint sz){return m_protocol->compName (buf, sz);}
    static QString userName        (const char* buf, uint sz){return m_protocol->userName (buf, sz);}
    static QString message         (const char* buf, uint sz){return m_protocol->message  (buf, sz);}
    static QByteArray parametrs    (const char* buf, uint sz){return m_protocol->parametrs(buf, sz);}

    static void setProgramId      (char* buf){m_protocol->setProgramId      (buf);}
    static void setProgramVersion (char* buf){m_protocol->setProgramVersion (buf);}
    static void setProtocolVersion(char* buf){m_protocol->setProtocolVersion(buf);}

    static void setDestIp         (char* buf, quint64 val){m_protocol->setDestIp      (buf, val);}
    static void setSrcIp          (char* buf, quint64 val){m_protocol->setSrcIp       (buf, val);}
    static void setPacketType     (char* buf, uint    val){m_protocol->setPacketType  (buf, val);}
    static void setPacketId       (char* buf, uint    val){m_protocol->setPacketId    (buf, val);}
    static void setFragmentSize   (char* buf, uint    val){m_protocol->setFragmentSize(buf, val);}
    static void setPacketNum      (char* buf, quint32 val){m_protocol->setPacketNum   (buf, val);}
    static void setTime           (char* buf, quint64 val){m_protocol->setTime        (buf, val);}
    static void setChannelType    (char* buf, uint    val){m_protocol->setChannelType (buf, val);}

    static void setCompNameLen    (char* buf, uint    val){m_protocol->setCompNameLen (buf, val);}
    static void setUserNameLen    (char* buf, uint    val){m_protocol->setUserNameLen (buf, val);}
    static void setMessageLen     (char* buf, quint32 val){m_protocol->setMessageLen  (buf, val);}
    static void setParametrsLen   (char* buf, quint32 val){m_protocol->setParametrsLen(buf, val);}
    static void setOptionsLen     (char* buf, quint32 val){m_protocol->setOptionsLen  (buf, val);}
    static void setCompressed     (char* buf, bool    val){m_protocol->setCompressed  (buf, val);}

    static void setCompName       (char* buf, const QString& name)   {m_protocol->setCompName (buf, name);}
    static void setUserName       (char* buf, const QString& name)   {m_protocol->setUserName (buf, name);}
    static void setMessage        (char* buf, const QString& msg)    {m_protocol->setMessage  (buf, msg);}
    static void setParametrs      (char* buf, const QByteArray& pars){m_protocol->setParametrs(buf, pars);}

    static char* uncompress(const char* buf, uint& size);
    static char* compress  (const char* buf, uint& size);

    static uint protocolVersion    (){return m_protocol->protocolVersion();}
    static uint protocolLen        (){return m_protocol->protocolLen();}
    static const char* programId   (){return m_protocol->programId();}
    static uint programIdLen       (){return strlen(m_protocol->programId());}
    static uint optionsLen         (){return m_protocol->optionsLen();}

    static void setProtocolVersion(uint);
};

#endif
