/***************************************************************************
 *   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 "channelwgt.h"

#include <QList>
#include <QProcess>
#include <QColor>
#include <QCoreApplication>
#include <QIcon>
#include <QScrollBar>
#include <QPainter>
#include <QCryptographicHash>
#include <QBitmap>
#include <QDir>
#include <QShortcut>
#include <QDateTime>

#include <assert.h>

#include "chatwgt.h"
#include "chatcore.h"
#include "msghistory.h"
#include "userslist.h"
#include "userslistwgt.h"
#include "message.h"
#include "statuseditwgt.h"
#include "chattextwgt.h"
#include "inputrichtextwgt.h"
#include "userwgt.h"
#include "userinfo.h"
#include "usersstatisticswgt.h"
#include "usersstatisticsmodel.h"
#include "userstatistics.h"

#include "textformattingwgt.h"
#include "formattingtoolbar.h"

#include "qchaticon.h"

#include "userlisticonconfigurewgt.h"
#include "userlisticonformat.h"

ChannelWgt::ChannelWgt(QString name_, QWidget *parent, AbstractChatCore::ChannelType type, quint64 dest_uid)
 : QWidget(parent),
  m_logFile         (NULL),
  m_type            (type),
  m_destUid         (dest_uid),
  m_name            (name_),
  m_usersNum        (0),
  m_passwordSize    (0),
  m_messageSize     (0),
  m_displayInfoMsgs (true),
  m_soundOnMessageIn(true),
  m_requestsRest    (0)
{
  m_parent = (ChatWgt*)parentWidget();

  QSplitter* lv_split;
  QSplitter* rv_split;
  QSplitter* h_split;

  m_smilesFromSender   = new QList<Smile>;

  m_chatMsgs           = new MsgHistory;
  m_users              = new UsersList;

  m_statusWgt          = new StatusEditWgt(this);
  m_file               = new QFile();
  mw_grid              = new QGridLayout(this);
  mw_chatTextStack     = new QStackedWidget(this);
  mw_usersStack        = new QStackedWidget(this);
  lv_split             = new QSplitter(Qt::Vertical);
  rv_split             = new QSplitter(Qt::Vertical);
  h_split              = new QSplitter(Qt::Horizontal);
  mw_chatText          = new ChatTextWgt(this);
  mw_clearChatText     = new ChatTextWgt(this);
  mw_inputText         = new InputRichTextWgt(this);
  mw_usersList         = new UsersListWgt(this);
  mw_usersStatistics   = new UsersStatisticsWgt(this);
  m_sendBtn            = new QPushButton(this);
  m_refreshUlBtn       = new QPushButton(this);
  m_sysMessagesChbx    = new QCheckBox(this);
  m_refreshTimer       = new QTimer(this);
  m_statusChangedTimer = new QTimer(this);
  m_initTimer          = new QTimer(this);
  m_usersModel         = new UsersStatisticsModel(this);

  m_splitters.append(lv_split);
  m_splitters.append(rv_split);
  m_splitters.append(h_split);

  m_usersModel->setUsers(m_users);
  mw_usersStatistics->setModel(m_usersModel);
  mw_usersStatistics->setSortingEnabled(true);

  mw_clearChatText->setSmilesFromSender(m_smilesFromSender);
  mw_chatText     ->setSmilesFromSender(m_smilesFromSender);

  m_status           = m_statusWgt->status();
  m_oldStatus          = m_statusWgt->status();
  m_statusDescription  = m_statusWgt->description();

  mw_clearChatText->hide();

  mw_chatTextStack->addWidget(mw_chatText);
  mw_chatTextStack->addWidget(mw_clearChatText);

  mw_usersStack->addWidget(mw_usersList);
  mw_usersStack->addWidget(mw_usersStatistics);

  mw_grid->setMargin(1);

  lv_split->addWidget(m_sysMessagesChbx);
  lv_split->addWidget(mw_chatTextStack);
  lv_split->addWidget(mw_inputText);
  lv_split->addWidget(m_sendBtn);

  rv_split->addWidget(m_statusWgt);
  rv_split->addWidget(mw_usersStack);
  rv_split->addWidget(m_refreshUlBtn);

  h_split ->addWidget(lv_split);
  h_split ->addWidget(rv_split);

  resize(800, 370);

  QList<int> tmplst;
  tmplst.append(590);
  tmplst.append(210);

  h_split ->setSizes(tmplst);

  tmplst.clear();
  tmplst.append(25);
  tmplst.append(300);
  tmplst.append(70);
  tmplst.append(25);

  lv_split->setSizes(tmplst);

  tmplst.clear();
  tmplst.append(25);
  tmplst.append(370);
  tmplst.append(25);

  rv_split->setSizes(tmplst);

  mw_grid->addWidget(h_split    , 0, 0);

  m_refreshUlBtn   ->setMaximumHeight(25);
  m_sendBtn        ->setMaximumHeight(25);
  m_sysMessagesChbx->setMaximumHeight(24);
  mw_inputText     ->setMinimumHeight(30);

  connect(m_statusChangedTimer, SIGNAL(timeout()), this        , SLOT(slot_statusChanged()));
  connect(m_refreshTimer      , SIGNAL(timeout()), this        , SLOT(slot_statusRequest()));
  connect(m_sendBtn           , SIGNAL(clicked()), mw_inputText, SLOT(sendMsg()));
  connect(m_sendBtn           , SIGNAL(clicked()), mw_inputText, SLOT(setFocus()));
  connect(m_refreshUlBtn      , SIGNAL(clicked()), this        , SLOT(slot_refreshUL()));
  connect(m_sysMessagesChbx   , SIGNAL(clicked()), this        , SLOT(slot_chbxInfChgd()));
  connect(mw_inputText        , SIGNAL(wantSend()), this, SLOT(slot_msgOut   ()));

  connect(mw_usersList        , SIGNAL(itemDoubleClicked(UserWgt*)),
          m_parent            , SLOT  (slot_showUserInfo(UserWgt*)));

  connect(mw_usersList        , SIGNAL(itemDoubleClicked(UserWgt*)),
          this                , SLOT  (slot_infoRequest (UserWgt*)));

  connect(m_statusWgt         , SIGNAL(statusChanged()),
          this                , SLOT  (slot_startStatusChangedTimer()));

  connect(m_statusWgt         , SIGNAL(editing()),  m_statusChangedTimer, SLOT(stop()));

  connect(h_split, SIGNAL(splitterMoved(int, int)),this, SLOT(slot_controlSplitter(int, int)));

  connect((QObject*)mw_usersStatistics->horizontalHeader(), SIGNAL(sectionClicked(int)),
          mw_usersStatistics                    , SLOT(sortByColumn(int)));

  connect(mw_usersStatistics, SIGNAL(sorted()),
          this              , SLOT  (updateUsersView()));

  if(m_parent)
  {
    connect(mw_usersList  , SIGNAL(singleMessage   (const QString &, quint64, bool)),
            m_parent      , SIGNAL(singleMessage   (const QString &, quint64, bool)));
    connect(mw_usersList  , SIGNAL(wantPrivateChat (const QString &, quint64)),
            m_parent      , SLOT  (slot_privateChat(const QString &, quint64)));
    connect(mw_usersList  , SIGNAL(wantSendFile    (quint64)),
            m_parent      , SLOT  (slot_sendFile   (quint64)));
  }

  FormattingToolBar* obj = m_parent->formattingToolBar();

  connect(obj, SIGNAL(wantSetBold     (bool)), mw_inputText, SLOT(setBold(bool)));
  connect(obj, SIGNAL(wantSetItalic   (bool)), mw_inputText, SLOT(setItalic(bool)));
  connect(obj, SIGNAL(wantSetUnderline(bool)), mw_inputText, SLOT(setUnderline(bool)));

  connect(obj, SIGNAL(wantSetColor     (const QColor&)) , mw_inputText, SLOT(setColor(const QColor&)));
  connect(obj, SIGNAL(wantSetFontFamily(const QString&)), mw_inputText, SLOT(setFontFamily(const QString&)));
  connect(obj, SIGNAL(wantSetFontSize  (const QString&)), mw_inputText, SLOT(setFontSize(const QString&)));
  connect(obj, SIGNAL(wantSetTextStyle (int))           , mw_inputText, SLOT(setTextStyle(int)));
  connect(obj, SIGNAL(wantCreateTable  (uint, uint))    , mw_inputText, SLOT(createTable(uint, uint)));

  connect(mw_inputText, SIGNAL(currentCharFormatChanged(const QTextCharFormat&)),
          obj         , SLOT  (currentCharFormatChanged(const QTextCharFormat&)));

  mw_clearChatText->text()->verticalScrollBar()->setValue(mw_clearChatText->text()->verticalScrollBar()->maximum());
  mw_chatText     ->text()->verticalScrollBar()->setValue(mw_chatText     ->text()->verticalScrollBar()->maximum());

  m_refreshTimer->start(QChatSettings::settings()->usersListRefreshInterval() * 1000);

  if(!QChatSettings::loggingDir().isEmpty())
  {
    QString filename = QChatSettings::loggingDir() + "/" + m_name + "_" + QDateTime::currentDateTime().toString("hh:mm:ss_dd.MM.yy") + ".log";

    m_logFile = new QFile(filename);

    if(!m_logFile->open(QIODevice::WriteOnly | QIODevice::Unbuffered))
    {
      qWarning("[ChannelWgt::ChannelWgt]: Couldn't open log file(%s) for writing. Disabling logging for channel %s", filename.toLocal8Bit().data(), m_name.toLocal8Bit().data());

      delete m_logFile;

      m_logFile = NULL;
    }
  }

  retranslate();
}
//\*****************************************************************************
ChannelWgt::~ChannelWgt()
{
  qDebug("[~ChannelWgt '%s']", m_name.toLocal8Bit().data());
  emit wantSaveState(m_name, saveState());
  slot_disconnected();

  delete m_logFile;
}
//\*****************************************************************************
UserWgt* ChannelWgt::findUser(quint64 uid) const
{
  return m_users->findUser(uid);
}
//\*****************************************************************************
void ChannelWgt::setFocus2InputText()
{
  mw_inputText->setFocus();
}
//\*****************************************************************************
void ChannelWgt::processData(QC_DatagramHeader* Hdr)
{
  qDebug("[ChannelWgt::processData]");

  switch(Hdr->type)
  {
    case AbstractChatCore::MESSAGE        :
      qDebug("[ChannelWgt::processData]: Message");
      emit wantActivate();
      getSmilesFromData(Hdr);
      addMsg(Hdr);
      break;

    case AbstractChatCore::STATUS_REQUEST :
      qDebug("[ChannelWgt::processData]: StatusRequest");
      sendStatusAnswer(Hdr->src_ip, 0, Hdr->programVersion < 14);
      break;

    case AbstractChatCore::STATUS_ANSWER  :
      qDebug("[ChannelWgt::processData]: StatusAnswer");
      addUser(Hdr);
      break;

    case AbstractChatCore::CONNECTED  :
      qDebug("[ChannelWgt::processData]: Connected");
      addUser(Hdr);
      addInfoMsg(Hdr);
      break;

    case AbstractChatCore::DISCONNECTED  :
      qDebug("[ChannelWgt::processData]: Disconnected");
      addInfoMsg(Hdr);
      hideUser(Hdr);
      break;

    case AbstractChatCore::INF_STATUS_CHNGD :
      qDebug("[ChannelWgt::processData]: StatusChanged");
      addUser(Hdr);
      addInfoMsg(Hdr);
      break;

    case AbstractChatCore::INFO_REQUEST  :
      qDebug("[ChannelWgt::processData]: UserInfoRequest");
      sendInfoAnswer(Hdr);
      break;

    case AbstractChatCore::INFO_ANSWER  :
      qDebug("[ChannelWgt::processData]: UserInfoAnswer");
      addUserInfo(Hdr);
      addUser(Hdr);
      break;

    case AbstractChatCore::MSGS_HISTORY_REQUEST :
      qDebug("[ChannelWgt::processData]: Msgs Request");
      qDebug("[ChannelWgt::processData]: par.size = %d", ChatCore::getParametr("MaxMsgsHistorySize", Hdr->parametrs).size());

      emit sendMsgsHistory(m_name, Hdr->src_ip, m_chatMsgs->toByteArray(ChatCore::getParametr("MaxMsgsHistorySize", Hdr->parametrs).toLongLong()), m_type);
      break;

    case AbstractChatCore::MSGS_HISTORY_ANSWER :
    {
      qDebug("[ChannelWgt::processData]: Msgs Answer");

      int diff_time = time(NULL) - Hdr->tm;
      m_chatMsgs->fromByteArray(ChatCore::getParametr("MsgsHistory", Hdr->parametrs));

      for(uint i = 0; i < m_chatMsgs->size(); i++)
      {
        m_chatMsgs->msg(i)->setReceiveTime(m_chatMsgs->msg(i)->receiveTime() + diff_time);
        m_chatMsgs->msg(i)->setIsHtml(Hdr->protocolVersion >= 4);
      }

      rebuildChatText();
      break;
    }
    case AbstractChatCore::MSGS_NUM_REQUEST :
      qDebug("[ChannelWgt::processData]: Msgs Num Request");
      emit sendMsgsNum(m_name, Hdr->src_ip, m_chatMsgs->size(), m_type);
      break;

    case AbstractChatCore::MSGS_NUM_ANSWER :
      qDebug("[ChannelWgt::processData]: Msgs Num Answer");
      msgsNumAnswer(Hdr);
      break;

    case AbstractChatCore::AVATAR_REQUEST :
    {
      qDebug("[ChannelWgt::processData]: Avatar Request");
      QByteArray ba = ChatCore::getParametr("IconHash", Hdr->parametrs);

      if(ba.isEmpty() || UserInfo::myInfo()->avatarHash() != ba)
        emit avatarAnswer(m_name, Hdr->src_ip, m_type);
      break;
    }

    case AbstractChatCore::AVATAR_ANSWER :
      qDebug("[ChannelWgt::processData]: Avatar Answer");
      addAvatar(Hdr);
      break;
  }
}
//\*****************************************************************************
void ChannelWgt::slot_msgOut()
{
  if(!mw_inputText->toPlainText().isEmpty())
  {
    if(m_type == 1)
    {
      emit sendMessage(m_name, m_destUid, m_type, mw_inputText->document()->clone());
      emit sendMessage(m_name, Globals::localhost(), m_type, mw_inputText->document()->clone());
    }
    else
      emit sendMessage(m_name, QChatSettings::settings()->broadcast().toIPv4Address(), m_type, mw_inputText->document()->clone());

    mw_inputText->clear();
  }
}
//\*****************************************************************************
void ChannelWgt::addInfoMsg(QC_DatagramHeader* Hdr)
{
  Message* msg = new Message(Hdr);

  logMessage(Hdr);

  m_chatMsgs->addMsg(msg);
  mw_chatText->addMsg(msg);
}
//\*****************************************************************************
void ChannelWgt::addMsg(QC_DatagramHeader* Hdr)
{
  if(QChatSettings::settings()->boolOption("IsExecuteCommandOnIncomingMessage") && !QChatSettings::settings()->executeCommandOnIncomingMsg().isEmpty())
  {
    QProcess pr;
    pr.startDetached(QChatSettings::settings()->executeCommandOnIncomingMsg());
  }

  logMessage(Hdr);

  Message* msg = new Message(Hdr);
  m_chatMsgs->addMsg(msg);

  mw_chatText->addMsg(msg);
  mw_clearChatText->addMsg(msg);
}
//\*****************************************************************************
void ChannelWgt::addMsg2Chat(Message* msg)
{
  if(msg)
  {
    qDebug("[ChannelWgt::addMsg2Chat]: adding: %s\n", msg->msg().toLocal8Bit().data());
    mw_chatText->addMsg(msg);
    if(!isSystemMsg(msg->type()))
      mw_clearChatText->addMsg(msg);
  }
}
//\*****************************************************************************
void ChannelWgt::addUser(QC_DatagramHeader* Hdr)
{
  UserWgt*   usr;
  UserInfo*  info;
  UserStatistics* stats;
  QByteArray buf;

  qDebug("[ChannelWgt::addUser]: uid = %s", QHostAddress(Hdr->src_ip).toString().toAscii().data());

  if(!(usr = m_users->enableIfExists(Hdr->src_ip)))
  {
    info = new UserInfo();
    if(!info) return;

    stats = new UserStatistics;
    if(!stats) return;

    usr  = new UserWgt();
    if(!usr) return;

    usr->setInfo(info);
    usr->setStats(stats);

    m_users->addUser(usr);
  }
  else
    info = usr->info();

  // this is only needed in server mode
  usr->info()->setUid(Hdr->src_ip);

  addUserInfo(usr, Hdr);

  mw_usersList->addUser(usr);

  m_usersModel->resort();
  updateUsersView();
}
//\*****************************************************************************
void ChannelWgt::hideUser(QC_DatagramHeader* Hdr)
{
  UserWgt* user;

  if(m_users->exists(Hdr->src_ip))
  {
    user = m_users->findUser(Hdr->src_ip);
    if(user)
    {
      mw_usersList->hideUser(user);
      user->info()->setEnabled(false);
    }
  }

  m_usersModel->resort();
  updateUsersView();
}
//\*****************************************************************************
void ChannelWgt::addUserInfo(QC_DatagramHeader* Hdr)
{
  UserWgt*   usr = m_users->findUser(Hdr->src_ip);
  if(NULL == usr)
  {
    usr = new UserWgt();
    usr->setInfo(new UserInfo());
    m_users->addUser(usr);
  }

  addUserInfo(usr, Hdr);
}

void ChannelWgt::addUserInfo(UserWgt* usr, QC_DatagramHeader* Hdr)
{
  QByteArray buf;
  QPixmap*   icon, *tmp;
  QCryptographicHash md5_hash(QCryptographicHash::Md5);
  bool need_avatar = true;

  if(!(buf = ChatCore::getParametr("IpAddress"  , Hdr->parametrs)).isNull())
    usr->info()->setIP((QString().fromUtf8(buf)).toLongLong());

  usr->info()->setGender(QString().fromUtf8(ChatCore::getParametr("Gender", Hdr->parametrs)).toInt());

  if(!(buf = ChatCore::getParametr("OS"         , Hdr->parametrs)).isNull())
    usr->stats()->setOs(QString().fromUtf8(buf));

  if(!(buf = ChatCore::getParametr("Uptime"     , Hdr->parametrs)).isNull())
    usr->stats()->setUptime((QString().fromUtf8(buf)).toLong());

  if(!(buf = ChatCore::getParametr("ChatTime"   , Hdr->parametrs)).isNull())
    usr->stats()->setChatTime((QString().fromUtf8(buf)).toLong());

  if(!(buf = ChatCore::getParametr("LastName"   , Hdr->parametrs)).isNull())
    usr->info()->setLastName   (QString().fromUtf8(buf));

  if(!(buf = ChatCore::getParametr("FirstName"  , Hdr->parametrs)).isNull())
    usr->info()->setFirstName  (QString().fromUtf8(buf));

  if(!(buf = ChatCore::getParametr("SecondName" , Hdr->parametrs)).isNull())
    usr->info()->setSecondName (QString().fromUtf8(buf));

  if(!(buf = ChatCore::getParametr("DateOfBorn" , Hdr->parametrs)).isNull())
    usr->info()->setDateOfBorn(QDate().fromString(QString().fromUtf8(buf)));

  if(!(buf = ChatCore::getParametr("Address"    , Hdr->parametrs)).isNull())
    usr->info()->setAddress(QString().fromUtf8(buf));

  if(!(buf = ChatCore::getParametr("HomePhone"  , Hdr->parametrs)).isNull())
    usr->info()->setHomePhone(QString().fromUtf8(buf));

  if(!(buf = ChatCore::getParametr("MobilePhone", Hdr->parametrs)).isNull())
    usr->info()->setMobilePhone(QString().fromUtf8(buf));

  if(!(buf = ChatCore::getParametr("WorkPhone"  , Hdr->parametrs)).isNull())
    usr->info()->setWorkPhone(QString().fromUtf8(buf));

  if(!(buf = ChatCore::getParametr("e-mail"     , Hdr->parametrs)).isNull())
    usr->info()->setE_mail(QString().fromUtf8(buf));

  if(!(buf = ChatCore::getParametr("ICQ"        , Hdr->parametrs)).isNull())
    usr->info()->setICQ(QString().fromUtf8(buf));

  if(!(buf = ChatCore::getParametr("Homepage"   , Hdr->parametrs)).isNull())
    usr->info()->setHomepage(QString().fromUtf8(buf));

  if(!(buf = ChatCore::getParametr("AboutInfo"  , Hdr->parametrs)).isNull())
    usr->info()->setAboutInfo(QString().fromUtf8(buf));

  if(!(buf = ChatCore::getParametr("StatusDescription", Hdr->parametrs)).isNull())
    usr->info()->setStatusDescription(QString().fromUtf8(buf));

//   if(!(buf = ChatCore::getParametr("Gender", Hdr->parametrs)).isNull())
//     usr->info()->setGender(QString().fromUtf8(buf).toInt());

  usr->info()->setStatus     (Hdr->status);

  buf = ChatCore::getParametr("Icon", Hdr->parametrs);
  if(!buf.isNull())
  {
    md5_hash.addData(buf);
    usr->info()->setAvatarHash(md5_hash.result());

    icon = new QPixmap();
    icon->loadFromData(buf);

    need_avatar = false;
  }
  else
    icon = usr->info()->avatar();

  tmp = drawUsersIcon(icon, usr->info());
  usr->setIcon(QIcon(*tmp));

  delete tmp;
  if(!need_avatar)
    delete icon;

  buf = ChatCore::getParametr("PhotoOk", Hdr->parametrs);
  if(buf.isNull())
  {
    buf = ChatCore::getParametr("Photo", Hdr->parametrs);

    if(!buf.isNull())
      usr->info()->setPhoto(buf);

    buf = ChatCore::getParametr("PhotoHash", Hdr->parametrs);
    usr->info()->setPhotoHash(buf);
  }

  buf = ChatCore::getParametr("PictureOk", Hdr->parametrs);
  if(buf.isNull())
  {
    buf = ChatCore::getParametr("Picture", Hdr->parametrs);

    if(!buf.isNull())
      usr->info()->setPicture(buf);

    buf = ChatCore::getParametr("PictureHash", Hdr->parametrs);
    usr->info()->setPictureHash(buf);
  }

  if(need_avatar)
  {
    ChatCore::addParametr("IconHash", usr->info()->avatarHash(), m_parametrs);
    emitSomeData(AbstractChatCore::AVATAR_REQUEST, "", Hdr->src_ip);
  }

  usr->info()->setProgramVerName(Hdr->versionName);
  usr->info()->setNickname      (Hdr->name);
  usr->info()->setCompName      (Hdr->comp_name);
  usr->info()->setStatus        (Hdr->status);
  usr->info()->setStatusDescription(QString().fromUtf8(ChatCore::getParametr("StatusDescription", Hdr->parametrs)));

  usr->setData(Qt::StatusTipRole,
               usr->info()->nickname() + tr(" on ") + usr->info()->compName() + "[" +
               QHostAddress(usr->info()->ip()).toString() + "]" + tr(" running QChat ") +
               usr->info()->programVerName()
              );

  usr->setData(Qt::ToolTipRole, usr->info()->statusDescription());
}
//\*****************************************************************************
void ChannelWgt::slot_refreshUL()
{
  qDebug("\n[ChannelWgt::slot_refreshUL]\n");

  mw_usersList->clear();
  m_users->disableAll();
//   slot_statusRequest();
  slot_deepRefreshUL();
}
//\*****************************************************************************
void ChannelWgt::slot_chbxInfChgd()
{
  if(m_sysMessagesChbx->checkState() == Qt::Checked)
  {
    mw_chatTextStack->setCurrentIndex(1);
    m_displayInfoMsgs = false;
  }
  if(m_sysMessagesChbx->checkState() == Qt::Unchecked)
  {
    m_displayInfoMsgs = true;
    mw_chatTextStack->setCurrentIndex(0);
  }
}
//\*****************************************************************************
void ChannelWgt::sendInfoAnswer(QC_DatagramHeader* Hdr)
{
  uchar photo_ok = 0;
  uchar pic_ok   = 0;
  QByteArray ba;

  if(m_status != Globals::INVISIBLE)
  {
    UserInfo::myInfo()->setStatus(m_name, m_status);
    UserInfo::myInfo()->setStatusDescription(m_name, m_statusWgt->description());

    ba = ChatCore::getParametr("PhotoHash"  , Hdr->parametrs);
    if(!ba.isEmpty())
      photo_ok = UserInfo::myInfo()->photoHash() == ba;

    ba = ChatCore::getParametr("PictureHash", Hdr->parametrs);
    if(!ba.isEmpty())
      pic_ok = UserInfo::myInfo()->pictureHash() == ba;

    emit infoAnswer(m_name, Hdr->src_ip, m_type, pic_ok | (2 * photo_ok));
  }
}
//\*****************************************************************************
void ChannelWgt::sendStatusAnswer(quint64 uid, bool changed, bool send_icon)
{
  if(m_status != Globals::INVISIBLE)
  {
    UserInfo::myInfo()->setStatus(m_name, m_status);
    UserInfo::myInfo()->setStatusDescription(m_name, m_statusDescription);

    if(m_type == 1)
    {
      emit statusAnswer(m_name, Globals::localhost(), m_type, changed, send_icon);
      emit statusAnswer(m_name, m_destUid, m_type, changed, send_icon);
    }
    else
      emit statusAnswer(m_name, uid, m_type, changed, send_icon);
  }
}
//\*****************************************************************************
void ChannelWgt::slot_infoRequest(UserWgt* user)
{
  m_parametrs.clear();

  if(QFile::exists(user->info()->pictureFilename()))
    ChatCore::addParametr("PictureHash", user->info()->pictureHash(), m_parametrs);

  if(QFile::exists(user->info()->photoFilename()))
    ChatCore::addParametr("PhotoHash"  , user->info()->photoHash()  , m_parametrs);

  emit sendSomeData(m_name, user->info()->uid(), AbstractChatCore::INFO_REQUEST, "", m_type, &m_parametrs);
}
//\*****************************************************************************
void ChannelWgt::slot_statusChanged()
{
  UserInfo::myInfo()->setStatus           (m_name, m_status            = m_statusWgt->status());
  UserInfo::myInfo()->setStatusDescription(m_name, m_statusDescription = m_statusWgt->description());

  sendStatusAnswer(QChatSettings::settings()->broadcast().toIPv4Address(), m_oldStatus != m_status);

  m_oldStatus = m_status;
}
//\*****************************************************************************
void ChannelWgt::slot_startStatusChangedTimer()
{
  m_statusChangedTimer->setInterval(1 * 1000);
  m_statusChangedTimer->setSingleShot(true);
  m_statusChangedTimer->start();
}

void ChannelWgt::getSmilesFromData(QC_DatagramHeader * Hdr) // TODO rewrite
{
  m_smilesFromSender->clear();

  QFile file;

  QString fname;
  QString str;

  QByteArray barr_hash;
  QByteArray barr1;
  QByteArray barr = ChatCore::getParametr("Smiles", Hdr->parametrs);
  QByteArray sm_ba;

  QCryptographicHash md5_hash(QCryptographicHash::Md5);
  int len;

  qDebug("[ChannelWgt::getSmilesFromData]: Smiles = %s", QString().fromUtf8(barr).toLocal8Bit().data());

  for(int i = 0; i < barr.size(); i++)
  {
    sm_ba.clear();
    for(; barr.at(i) != 0 && i < barr.size(); i++)
      sm_ba.append(barr.at(i));

    str = QString().fromUtf8(sm_ba);

    qDebug("[ChannelWgt::getSmilesFromData]: str = %s", str.toLocal8Bit().data());

    barr1 = ChatCore::getParametr("Smile" + str, Hdr->parametrs);
    md5_hash.addData(barr1);

    barr_hash = md5_hash.result();
    md5_hash.reset();
    fname = QDir::tempPath() + "/smile";

    fname.append('_');

    len = barr_hash.size();

    for(int i = 0; i < len; i++)
      fname.append(QString::number((uchar)barr_hash.at(i), 16));

    file.setFileName(fname);
    if(file.open(QIODevice::WriteOnly | QIODevice::Unbuffered))
      file.write(barr1);
    else
      qWarning("[ChannelWgt::getSmilesFromData]: couldn't open file %s for writing", fname.toLocal8Bit().data());
    file.close();

    m_smilesFromSender->append(Smile(QStringList(str), fname));
  }
}

void ChannelWgt::initChannel()
{
  emit sendSomeData(m_name, QChatSettings::settings()->broadcast().toIPv4Address(), AbstractChatCore::MSGS_NUM_REQUEST, "", m_type, NULL);

  m_msgsReqSent     = QTime::currentTime();
  m_msgsReqReceived = m_msgsReqSent;

  connect(m_initTimer, SIGNAL(timeout()), this, SLOT(slot_finMsgsHistoryReq()));

  m_msgsReqNum = 0;

  m_initTimer->setSingleShot(true);
  m_initTimer->start(QChatSettings::settings()->historyReqTimeout());

  slot_statusRequest();
  slot_connected();
}

void ChannelWgt::rebuildChatText()
{
  mw_chatText     ->clear();
  mw_clearChatText->clear();

  for(uint i = 0; i < m_chatMsgs->size(); i++)
    addMsg2Chat(m_chatMsgs->msg(i));
}

void ChannelWgt::slot_finMsgsHistoryReq()
{
  qDebug("\n[ChannelWgt::slot_finMsgsHistoryReq]: num = %d\n", m_msgsReqNum);

  ChatCore::addParametr("MaxMsgsHistorySize", QString().setNum(QChatSettings::settings()->nHistoryMsgs()).toUtf8(), m_parametrs);

  // FIXME !!!
  if(m_msgsReqNum > 0)
    emit sendSomeData(m_name, m_msgsReqAddr.toIPv4Address(), AbstractChatCore::MSGS_HISTORY_REQUEST, "", m_type, &m_parametrs);
}

void ChannelWgt::msgsNumAnswer(QC_DatagramHeader* Hdr)
{
  uint num = QString(ChatCore::getParametr("MsgsNum", Hdr->parametrs)).toULongLong();

  if(num > m_msgsReqNum && (QChatSettings::settings()->nHistoryMsgs() < 0 || (int)m_msgsReqNum < QChatSettings::settings()->nHistoryMsgs()))
  {
    m_msgsReqNum      = num;
    m_msgsReqAddr     = QHostAddress(Hdr->src_ip);
    m_msgsReqReceived = QTime::currentTime();
  }
}

void ChannelWgt::retranslate()
{
  m_sendBtn            ->setText(tr("Send"));
  m_refreshUlBtn       ->setText(tr("Refresh"));
  m_sysMessagesChbx    ->setText(tr("Hide System Messages"));
}

void ChannelWgt::emitSomeData(AbstractChatCore::DataType data_type, const QString & msg, quint64 uid) const
{
  if(m_type == 1)
  {
    emit sendSomeData (m_name, m_destUid, data_type, msg, m_type, &m_parametrs);
    emit sendSomeData (m_name, Globals::localhost(), data_type, msg, m_type, &m_parametrs);
  }
  else if(uid == 0)
    // FIXME !!!
    emit sendSomeData (m_name, QChatSettings::settings()->broadcast().toIPv4Address(), data_type, msg, m_type, &m_parametrs);
  else
    emit sendSomeData (m_name, uid, data_type, msg, m_type, &m_parametrs);
}

void ChannelWgt::slot_changeUlRefreshInterval(uint erval)
{
  if(erval == 0)
    m_refreshTimer->stop();
  else
    m_refreshTimer->start(erval * 1000);
}

void ChannelWgt::slot_changeUlDeepRefreshInterval(uint /*erval*/)
{

}

void ChannelWgt::slot_controlSplitter(int idx, int /*pos*/)
{
  if((double)idx / (double)width() <= 0.5)
  {
    mw_usersStack->setCurrentIndex(1);
    m_usersModel->resort();
    updateUsersView();
  }
  else
    mw_usersStack->setCurrentIndex(0);
}

void ChannelWgt::updateUsersView()
{
  m_usersModel->resetModel();
  mw_usersStatistics->reset();
  mw_usersStatistics->setModel(m_usersModel);
}

void ChannelWgt::slot_deepRefreshUL()
{
  if(!m_requestsRest)
  {
    QTimer* t = new QTimer;

    m_requestsRest = 3;

    connect(t, SIGNAL(timeout()), this, SLOT(nextStatusRequest()));

    t->start(1000);

    slot_statusRequest();
  }
  else
    slot_statusRequest();
}

QByteArray ChannelWgt::saveState() const
{
  // Format:
  // 2 bytes - splittersStatesSize
  // 3x:
  // 2 bytes - splitter[i] State Size(SSS)
  // SSS bytes - splitter[i] state , i = (1, 3)
  //
  // from splittersStatesSize to state.size(); - usersstatisticswgt state

  QByteArray state, tmp;
  char sz[2];

  for(int i = 0; i < m_splitters.size(); i++)
  {
    tmp = m_splitters[i]->saveState();
    catUS2str(sz, tmp.size());

    state.append(sz[0]);
    state.append(sz[1]);
    state.append(tmp);
  }

  catUS2str(sz, state.size());
  state.prepend(sz[1]);
  state.prepend(sz[0]);

  state.append(mw_usersStatistics->saveState());

  return state;
}

void ChannelWgt::restoreState(const QByteArray & state)
{
  QByteArray tmp;
  char sz2[2];
  uint sz;
  uint state_sz;

  if(state.size() < 2)
    return;

  sz2[0] = state.at(0);
  sz2[1] = state.at(1);

  state_sz = str2US(sz2) + 2;

  if((uint)state.size() < state_sz)
    return;

  for(uint i = 0, j = 2; i < (uint)m_splitters.size(); i++)
  {
    if(j + 2 > state_sz)
      break;

    sz2[0] = state.at(j++);
    sz2[1] = state.at(j++);
    sz     = str2US(sz2);

    if(j + sz > state_sz)
      break;

    tmp = state.mid(j, sz);
    m_splitters[i]->restoreState(tmp);

    j += sz;
  }

  mw_usersStatistics->restoreState(state.mid(state_sz, state.size() - state_sz));

  if((double)m_splitters[2]->sizes()[1] / (double)width() >= 0.5)
  {
    mw_usersStack->setCurrentIndex(1);
    m_usersModel->resort();
    updateUsersView();
  }
  else
    mw_usersStack->setCurrentIndex(0);

  updateUsersView();
}

void ChannelWgt::nextStatusRequest()
{
  slot_statusRequest();
  m_requestsRest--;

  if(!m_requestsRest)
    if(qobject_cast<QTimer*>(sender()))
      delete sender();
}

void ChannelWgt::addAvatar(QC_DatagramHeader * Hdr)
{
  UserWgt*   usr;
  QByteArray buf;
  QPixmap* icon, *tmp;

  usr = m_users->findUser(Hdr->src_ip);
  if(!usr)
    m_users->findHidden(Hdr->src_ip);

  if(!usr)
    return;

  buf = ChatCore::getParametr("Icon", Hdr->parametrs);

  if(!buf.isNull())
  {
    icon = new QPixmap();

    if(!buf.isEmpty())
      icon->loadFromData(buf);

    tmp = drawUsersIcon(icon, usr->info());
    usr->setIcon(QIcon(*tmp));

    delete usr->info()->avatar();
    usr->info()->setAvatar(icon);

    delete tmp;
  }

  buf = ChatCore::getParametr("IconHash", Hdr->parametrs);
  usr->info()->setAvatarHash(buf);

  updateUsersView();
}

QPixmap* ChannelWgt::drawUsersIcon(const QPixmap* avatar_icon, UserInfo* info)
{
  QPixmap* status_pix = NULL;
  QPixmap* gender_pix = NULL;
  QPixmap* result_pix;
  QPixmap  tmp_icon;
  QPainter painter;
  bool     disabled = true;
  QString  icon_name;

  UserListIconFormat fmt = *(QChatSettings::settings()->iconFormat());

  int status_wd = fmt.statusWidth();
  int status_he = fmt.statusHeight();
  int gender_wd = fmt.genderWidth();
  int gender_he = fmt.genderHeight();
  int avatar_wd = fmt.avatarWidth();;
  int avatar_he = fmt.avatarHeight();
  int total_wd  = fmt.totalWidth();
  int total_he  = fmt.totalHeight();

  bool draw_gender = gender_wd > 0 && gender_he > 0;
  bool draw_status = status_wd > 0 && status_he > 0;

//   avatar_wd = total_wd - status_wd - gender_wd;

  assert(NULL != info);

  switch(info->status())
  {
    case Globals::READY4CHAT :
      icon_name = "status/user-ready-for-chat";
      disabled = false;
      break;

    case Globals::FREE :
      icon_name = "status/user-online";
      disabled = false;
      break;

    case Globals::BUSY :
      icon_name = "status/user-busy";
      break;

    case Globals::DND :
      icon_name = "status/user-dnd";
      break;

    case Globals::INACTIVE :
      icon_name = "status/user-away";
      break;

    case Globals::AWAY :
      icon_name = "status/user-away-extended";
      break;
  }

  if(avatar_icon)
  {
    if(disabled)
//       tmp_icon = QIcon(*avatar_icon).pixmap(avatar_wd, avatar_he, QIcon::Disabled);
      tmp_icon = QIcon(*avatar_icon).pixmap(avatar_wd, avatar_he, QIcon::Disabled);
    else
      tmp_icon = QIcon(*avatar_icon).pixmap(avatar_wd, avatar_he);//*avatar_icon;
  }

  if(draw_gender)
    switch(info->gender())
    {
      case 'm' : gender_pix = QChatIcon::newPixmap("user-male"  , gender_wd, gender_he); break;
      case 'f' : gender_pix = QChatIcon::newPixmap("user-female", gender_wd, gender_he); break;
      default  : gender_pix = QChatIcon::newPixmap("unknown"    , gender_wd, gender_he);
    }

  result_pix = new QPixmap(total_wd, total_he);

  QPixmap tmp(total_wd, total_he);

  tmp.fill(Qt::black);
  result_pix->setAlphaChannel(tmp);

  if(draw_status)
  {
    status_pix = QChatIcon::newPixmap(icon_name, status_wd, status_he);

    tmp = QPixmap(status_pix->width(), status_pix->height());
    tmp.fill(QColor(200, 200, 200));

    status_pix->setAlphaChannel(tmp);
  }

  int g_wd = (gender_wd + fmt.genderXoffset()) * draw_gender;
  int s_wd = (status_wd + fmt.statusXoffset()) * draw_status;

  painter.begin(result_pix);

  if(draw_gender)
    painter.drawPixmap(fmt.genderXoffset(), fmt.genderYoffset(), *gender_pix);

  if(draw_status)
    painter.drawPixmap(fmt.statusXoffset(), fmt.statusYoffset(), *status_pix);

  painter.drawPixmap((s_wd > g_wd ? s_wd : g_wd) + fmt.avatarXoffset(),
                     (total_he - tmp_icon.height()) / 2 /*fmt.avatarYoffset()*/, tmp_icon);
  painter.drawLine(fmt.totalWidth(), 0, fmt.totalWidth(), fmt.totalHeight());

  painter.end();

  delete status_pix;
  delete gender_pix;

  return result_pix;
}

void ChannelWgt::redrawIcons()
{
  UserListIconFormat* fmt = QChatSettings::settings()->iconFormat();

  mw_usersList->setIconSize(QSize(fmt->totalWidth(), fmt->totalHeight()));

  for(uint i = 0; i < m_users->num(); i++)
  {
    UserWgt* usr = m_users->user(i);
    if(usr)
    {
      usr->setSizeHint(QSize(1, fmt->totalHeight()));
      usr->setIcon(QIcon(*drawUsersIcon(usr->info()->avatar(), usr->info())));
    }
  }
}

void ChannelWgt::keyPressEvent(QKeyEvent* ev)
{
  QKeySequence seq = ev->key() + ev->modifiers();

  if(QChatSettings::settings()->shortcuts("RefreshUsersList").contains(seq))
    slot_refreshUL();
}

void ChannelWgt::setAnimationsRunning(bool b)
{
  mw_clearChatText->playPauseAnimations(b);
  mw_chatText     ->playPauseAnimations(b);
}

void ChannelWgt::logMessage(QC_DatagramHeader* Hdr)
{
  if(m_logFile)
  {
    Message   msg(Hdr);
    QDateTime date_time;
    QTextDocument doc;

    doc.setHtml(msg.msg());

    QString msg_str(QChatSettings::settings()->strOption("DisplayMessagesFormat"));
    QString tm_fmt = msg_str.section(QString("%time%"), 1, 1);

    date_time.setTime_t(msg.receiveTime());

    msg_str.replace(QRegExp("%time%([^<]*)%time%"), date_time.toString(tm_fmt));
    msg_str.replace("%user", msg.userName());
    msg_str.replace("%comp", msg.compName());

    if(msg.requested())
      msg_str.prepend(">");

     msg_str.append(doc.toPlainText() + "\n");

    m_logFile->write(msg_str.toUtf8());
  }
}
