/***************************************************************************
 *   Copyright (C) 2007 by Anistratov Oleg                                 *
 *   ower@users.sourceforge.net                                            *
 *                                                                         *
 *   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 <assert.h>

#include "senderthread.h"
#include "globals.h"
#include "abstractchatcore.h"

SenderThread::SenderThread(QObject *parent)
 : QThread         (parent),
  m_socket         (NULL),
  m_udp            (NULL),
  m_tcp            (NULL),
  m_port           (61100),
  m_datagrams      (NULL),
  m_datagramsNum   (0),
  m_datagramsMaxNum(0),
  m_timer          (NULL)
{
  m_buffer = (char*)malloc(65535);
  assert(NULL != m_buffer);

  moveToThread(this);
}
//\*****************************************************************************
SenderThread::~SenderThread()
{
  qDebug("[~SenderThread]");
  delete m_socket;
  free(m_buffer);
}
//\*****************************************************************************
void SenderThread::run()
{
  // TODO stop timer when $m_datagramsNum == 0 and start it when new task added..
  // TODO ..or create timer for every datagram(maybe include it in LargeDatagramOut)
  m_udp = new QUdpSocket(this);

  m_socket = m_udp;

  m_timer    = new QTimer(this);

  m_timer->setInterval(10);
  m_timer->start();

  connect(m_timer, SIGNAL(timeout()), this, SLOT(sending()));

  exec();
}
//\*****************************************************************************
void SenderThread::addTask(char* header, quint16 header_len, char* data, quint32 data_len, quint64 dest_uid, quint32 id)
{
  Q_ASSERT(currentThread() == this);

  qDebug("[SenderThread::addTask]: ID = %u", id);

  LargeDatagramOut** tmp;
  LargeDatagramOut*  dtgrm = new LargeDatagramOut(this);

  QHostAddress addr = QHostAddress(dest_uid);

  dtgrm->moveToThread(this);

  if(dtgrm)
  {
    dtgrm->init(header, header_len, data, data_len, dest_uid, id);

    connect(dtgrm, SIGNAL(wantDie(LargeDatagramOut*)), this, SLOT(deleteDatagram(LargeDatagramOut*)));

    qDebug("[SenderThread::addTask]: adding(%lu)", (unsigned long)id);

    m_datagramsNum++;

    if(m_datagramsMaxNum < m_datagramsNum)
    {
      m_datagramsMaxNum++;
      tmp = (LargeDatagramOut**)realloc(m_datagrams, m_datagramsMaxNum * sizeof(LargeDatagramOut*));

      if(!tmp)
        delete dtgrm;
      else
      {
        m_datagrams = tmp;
        m_datagrams[m_datagramsNum - 1] = dtgrm;
      }
    }
    else
      m_datagrams[m_datagramsNum - 1] = dtgrm;
  }
}
//\*****************************************************************************
void SenderThread::addFileTask(char* header, quint16 header_len, const QString & filename, quint64 dest_uid, quint32 id)
{
  Q_ASSERT(currentThread() == this);

  LargeDatagramOut** tmp;
  LargeDatagramOut*  dtgrm = new LargeDatagramOut(this);

  dtgrm->moveToThread(this);

  if(dtgrm)
  {
    connect(dtgrm, SIGNAL(wantDie(LargeDatagramOut*)), this, SLOT  (deleteDatagram(LargeDatagramOut*)));
    connect(dtgrm, SIGNAL(sendingCancelled(quint16 )), this, SIGNAL(sendingCancelled(quint16        )));

    dtgrm->init(header, header_len, filename, dest_uid, id);

    qDebug("[SenderThread::addFileTask]: adding(%lu)", (unsigned long)id);

    m_datagramsNum++;

    if(m_datagramsMaxNum < m_datagramsNum)
    {
      m_datagramsMaxNum++;
      tmp = (LargeDatagramOut**)realloc(m_datagrams, m_datagramsMaxNum * sizeof(LargeDatagramOut*));

      if(!tmp)
        delete dtgrm;
      else
      {
        m_datagrams = tmp;
        m_datagrams[m_datagramsNum - 1] = dtgrm;
      }
    }
    else
      m_datagrams[m_datagramsNum - 1] = dtgrm;
  }
}
//\*******s**********************************************************************
quint32 SenderThread::getValidID() const
{
  for(quint32 i = 1; i > 0; i++)
    if(!containID(i))
      return i;

  return 0;
}
//\*****************************************************************************
bool SenderThread::containID(quint32 ID) const
{
  quint32 i;

  for(i = 0; i < m_datagramsNum; i++)
    if(m_datagrams[i]->id() == ID)
      return true;

  return false;
}
//\*****************************************************************************
void SenderThread::deleteDatagram(LargeDatagramOut* dtgrm)
{
  Q_ASSERT(currentThread() == this);

  for(quint32 i = 0; i < m_datagramsNum; i++)
    if(dtgrm == m_datagrams[i])
    {
      emit sendingFinished(dtgrm->id());
      m_datagrams[i] = m_datagrams[m_datagramsNum - 1];
      m_datagramsNum--;
      delete dtgrm;
      break;
    }
}
//\*****************************************************************************
void SenderThread::slot_fragmentsRequest(char* dtgrm_, quint32 dtgrm_len)
{
  qDebug("[SenderThread::slot_fragmentsRequest]");

  quint16 ID              = AbstractChatCore::packetId(dtgrm_);
  LargeDatagramOut* dtgrm = findDatagram(ID);

  if(dtgrm)
    dtgrm->fragmentsRequest(dtgrm_, dtgrm_len);
}
//\*****************************************************************************
LargeDatagramOut* SenderThread::findDatagram(quint16 ID) const
{
  for(quint32 i = 0; i < m_datagramsNum; i++)
    if(m_datagrams[i]->id() == ID)
      return m_datagrams[i];

  return NULL;
}
//\*****************************************************************************
void SenderThread::slot_acceptSending (quint16 ID)
{
  LargeDatagramOut* dtgrm = findDatagram(ID);

  if(dtgrm)
    dtgrm->acceptSending();
}
//\*****************************************************************************
void SenderThread::slot_cancelTask(quint16 id)
{
  Q_ASSERT(currentThread() == this);

  LargeDatagramOut* dtgrm = findDatagram(id);

  if(dtgrm)
    deleteDatagram(dtgrm);
}
//\*****************************************************************************
int SenderThread::send(char* buf, int size, const QHostAddress& addr)
{
  int bs = -2;

  if(m_socket == m_tcp)
  {
    if(m_socket->state() == QAbstractSocket::ConnectedState)
      bs = m_socket->write(buf, size);

    m_socket->flush();
  }
  else if(m_socket == m_udp)
    bs = m_udp->writeDatagram(buf, size, addr, m_port);

  qDebug("[SenderThread::send]: dtgrm size = %d, sent = %d\n", size, bs);

  return bs;
}
