/***************************************************************************
 *   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.                          *
 *                                                                         *
 ***************************************************************************/
#include "qchatsocket.h"

#include <assert.h>

#include "globals.h"

#include "userinfo.h"

QChatSocket::QChatSocket(QObject* parent, QTcpSocket* soc)
 : QObject(parent),
  AbstractChatCore(),
  m_bufferSize(64 * 1024),
  m_state(Unconnected),
  m_fullDtgrmSize(0),
  m_currentDtgrmSize(0),
  m_newDtgrmReady(0),
  m_nickname("")
{
  setProtocolVersion(4);

  m_buffer = (char*)malloc(m_bufferSize);
  assert(NULL != m_buffer);

  m_dtgrm = (char*)malloc(m_bufferSize);
  assert(NULL != m_dtgrm);

  if(soc)
    m_socket = soc;
  else
    m_socket = new QTcpSocket();


  connect(m_socket, SIGNAL(disconnected()), this, SIGNAL(disconnected()));
  connect(m_socket, SIGNAL(readyRead())   , this, SLOT(processNewData()));
}

QChatSocket::~QChatSocket()
{
  qDebug("[~QChatSocket]");
}

void QChatSocket::processNewData()
{
  QTcpSocket* socket = qobject_cast<QTcpSocket*>(sender());
  int dataSize;
  char* buf;

  if(socket)
  {
    while((dataSize = socket->bytesAvailable()))
    {
      // if full new packet was received but didn't read yet
      if(m_newDtgrmReady)
      {
        emit newDtgrm();
        continue;
      }

      dataSize = socket->read(m_buffer, m_bufferSize);
      buf = m_buffer;

      while((buf = appendData(buf, dataSize)))
      {
        while(m_newDtgrmReady)
        {
          emit newDtgrm();
          continue;
        }
      }
      while(m_newDtgrmReady)
      {
        emit newDtgrm();
        continue;
      }
    }
  }
}

void QChatSocket::login(const QString& nick, const QString& /*pwd*/)
{
  m_nickname = nick;

  prepareDatagram(AbstractChatCore::WANT_LOGIN, 0, "Login", UserInfo::myInfo()->compName(), 0, nick);

  sendData();
}

void QChatSocket::sendData()
{
  if(largeDtgrm())
  {
//     quint32 id = m_sender->getValidID();

//     if(id)
//     {
//       emit wantSendLargeData(header(), headerSize(), data(), dataSize(), addr, ID);
//       setNULLdataAndHeader();
//     }
  }
  else if(state() == QAbstractSocket::ConnectedState)
  {
    quint16 size = outputBufferSize() <= MAX_PACKET_LEN ? outputBufferSize() : (MAX_PACKET_LEN);
    // FIXME
    setPacketSize(outputBufferSize());

    int bs = write(outputBuffer(), size);

    qDebug("[QChatSocket::sendData]: dtgrm size = %d, sent = %d\n", size, bs);
  }

  clearParametrs();
}

int QChatSocket::readDtgrm(char* dest, int maxSize)
{
  int size = maxSize < m_fullDtgrmSize ? maxSize : m_fullDtgrmSize;

  if(maxSize < m_fullDtgrmSize)
    qWarning("[QChatSocket::readDtgrm]: maxSize < m_fullDtgrmSize!!!");

  memcpy(dest, m_dtgrm, size);

  m_currentDtgrmSize = 0;
  m_fullDtgrmSize    = 0;
  m_newDtgrmReady    = false;

  return size;
}

/**
 *
 * @return pointer to rest of the buffer(which was not processed) or NULL of all data was processed
 */
char* QChatSocket::appendData(const char* buf, int& size)
{
  char* dtgrm = m_dtgrm + m_currentDtgrmSize;
  int min_size = protocolLen();
  int bytes_write;

  if((size + m_currentDtgrmSize) < min_size)
  {
    m_currentDtgrmSize += size;
    memcpy(dtgrm, buf, size);
    size = 0;

    return NULL;
  }

  if(m_currentDtgrmSize < min_size)
  {
    int diff = min_size - m_currentDtgrmSize;

    memcpy(dtgrm, buf, diff);
    buf                += diff;
    size               -= diff;
    dtgrm              += diff;
    m_currentDtgrmSize  = min_size;

    m_fullDtgrmSize = packetSize(m_dtgrm);

    if(qstrncmp(m_dtgrm, programId(), programIdLen() - (protocolVersion() < 4 ? 2 : 0)) || protocolVersion(m_dtgrm) != protocolVersion())
    {
      size = 0;
      m_fullDtgrmSize = 0;
      m_currentDtgrmSize = 0;

      return NULL;
    }
  }

  if(m_fullDtgrmSize <= 0)
  {
  }

  bytes_write = (size <= m_fullDtgrmSize - m_currentDtgrmSize) ? size : m_fullDtgrmSize - m_currentDtgrmSize;

  memcpy(dtgrm, buf, bytes_write);

  m_currentDtgrmSize += bytes_write;
  size               -= bytes_write;

  if(m_currentDtgrmSize == m_fullDtgrmSize)
  {
    m_newDtgrmReady = true;
    memcpy(m_dtgrm, AbstractChatCore::programId(), AbstractChatCore::programIdLen());
  }

  if(size)
    return  (char*)(buf + bytes_write);

  return NULL;
}
