/***************************************************************************
                          dcconnectionmanager.cpp  -  description
                             -------------------
    begin                : Mon Oct 1 2001
    copyright            : (C) 2001-2005 by Mathias Küster
    email                : mathen@users.berlios.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "dcconnectionmanager.h"

#include <stdlib.h>

#include <QToolBar>
#include <QTabBar>
#include <QMdiArea>
#include <QMdiSubWindow>
#include <QMapIterator>
#include <QList>

#include <exception>
#include <iostream>

#include "dcclient.h"
#include "dciconloader.h"
#include "dcconfig.h"
#include "dcgui.h"

#include <dclib/dcos.h>

DCConnectionManager * DCConnectionManager::g_pConnectionManager = NULL;

/** */
DCConnectionManager::DCConnectionManager(QObject * parent) : QObject(parent) {
    m_pMdiArea = qobject_cast<QMdiArea*>(parent);

    m_pMessageList = new QList<CDCMessage*>();

    InitDocument();
}

/** */
DCConnectionManager::~DCConnectionManager() {
    CClient * Client = 0;
    QMapIterator<QWidget*, int> it(windowsList);

    while (it.hasNext()) {
        QWidget *wgt;
        it.next();
        wgt = it.key();
        delete wgt;
    }

    m_Mutex.Lock();

    while ((Client = m_pClientList->Next(0)) != 0) {
        m_pClientList->Remove(Client);
        Client->Disconnect();
    }

    if (m_pMessageList) {
        QList<CDCMessage*> * tmp = m_pMessageList;
        m_pMessageList = 0;

        for (QList<CDCMessage*>::const_iterator it = tmp->constBegin(); it != tmp->constEnd(); ++it) {
            delete *it;
        }

        delete tmp;
    }

    m_Mutex.UnLock();
}

/** */
void DCConnectionManager::InitDocument() {
    m_pTabBar = 0;
    m_nSelectedTab = -1;

    connect(&m_Timer, SIGNAL(timeout()), this, SLOT(timerDone()));

    connect(m_pMdiArea, SIGNAL(subWindowActivated(QMdiSubWindow *)), this, SLOT(slotMdiSubWindowActivated(QMdiSubWindow *)));

    m_Timer.setSingleShot(true);
    m_Timer.start(500);
}

/** */
void DCConnectionManager::InitTabBar(QToolBar * toolbar) {
    m_pTabBar = new DCTabBar(toolbar);
    //toolbar->setStretchableWidget(m_pTabBar);
    toolbar->addWidget(m_pTabBar);
    connect(m_pTabBar, SIGNAL(currentChanged(int)), this, SLOT(slotTabSelected(int)));
    // hide empty tabbar
    m_pTabBar->hide();

//#ifdef QT_NEW
    m_pTabBar->setTabsClosable(true);
    m_pTabBar->setMovable(true);

    connect(m_pTabBar, SIGNAL(tabCloseRequested(int)), this, SLOT(slotCloseTabButtonClicked(int)));
    connect(m_pTabBar, SIGNAL(tabMoved(int,int)), this, SLOT(slotTabMoved(int,int)));
//#endif

}

/** */
void DCConnectionManager::slotTabSelected(int id) {
    QMapIterator<QWidget*, int> it(windowsList);

    while (it.hasNext()) {
        it.next();
        if (it.value() == id) {
            QWidget *wgt = it.key();
            wgt->setFocus();

            m_pTabBar->setTabIcon(id, wgt->windowIcon());

            QMdiSubWindow *mdi_wind;

            DCChat::SetActiveChat(NULL);

            try {
                mdi_wind = dynamic_cast<QMdiSubWindow*> (wgt);
            } catch (std::exception &e) {
                DCChat::SetActiveChat(NULL);
                return;
            }

            if (!mdi_wind) {
                return;
            }

            DCClient *cl;

            try {
                cl = dynamic_cast<DCClient*> (mdi_wind->widget());
            } catch (std::exception &e) {
                DCChat::SetActiveChat(NULL);
                return;
            }

            if (!cl) {
                return;
            } else {
                DCChat::SetActiveChat(cl->GetChat());
            }


            return;
        }
    }
}

//#ifdef QT_NEW

void DCConnectionManager::slotTabMoved(int from, int to)
{
    QWidget *from_wgt = NULL;
    QWidget *to_wgt   = NULL;

    QMapIterator<QWidget*, int> it(windowsList);

    while (it.hasNext()){
        it.next();

        if (it.value() == from){
            from_wgt = it.key();
        }
        else if (it.value() == to)
            to_wgt = it.key();

        if (to_wgt && from_wgt){
            windowsList[to_wgt] = from;
            windowsList[from_wgt] = to;

            slotTabSelected(m_pTabBar->currentIndex());

            return;
        }
    }
}

//#endif

/** widget from mdi area activated */
void DCConnectionManager::slotMdiSubWindowActivated(QMdiSubWindow * w) {
    if (!w)
        return;

    QMapIterator<QWidget*, int> it(windowsList);
    QWidget *wgt = (QWidget*) w;
    QWidget *mdi_wgt = w->widget();

    while (it.hasNext()) {
        it.next();
        if ((it.key() == wgt) || (it.key() == mdi_wgt)) {
            m_nSelectedTab = it.value();
            m_pTabBar->setCurrentIndex(m_nSelectedTab);
            m_pTabBar->setTabIcon(m_nSelectedTab, QIcon());
            return;
        }
    }
}

/** */
void DCConnectionManager::HubEvent(DCClient * client) {
    if (!client)
        return;

    int i = windowsList[(QWidget*) client->GetMdiSubWindow()];

    if ((i != -1) && (i != m_pTabBar->currentIndex())) {
        m_pTabBar->setTabIcon(i, QIcon(DCIconLoader::Instance()->GetPixmap(eiMESSAGE)));
    }
}

/** */
void DCConnectionManager::CaptionChanged(DCClient * client) {
    if (client) {
        const int i = windowsList[(QWidget*) client->GetMdiSubWindow()];

        if ((i >= 0) && (i < m_pTabBar->count())) {
            QString s = QString::fromAscii(client->GetHubName().Data());
            if (s.length() > 20) {
                s = s.left(20) + "...";
            }
            m_pTabBar->setTabText(i, s);
        }
    }
}

/** */
void DCConnectionManager::HubClosing(DCClient * client) {
    if (m_pTabBar->count() == 0) {
        m_pTabBar->hide();
    }

    disconnect(client, SIGNAL(onDie(QWidget*)), DCGuiApp::Instance(), SLOT(slotChildOnDie(QWidget*)));

    RemoveHub(client);
}

/** callback function */
int DCConnectionManager::DC_CallBack(CDCMessage * DCMessage) {
    int err = -1;

    m_Mutex.Lock();

    if (DCMessage && m_pMessageList) {
        m_pMessageList->append(DCMessage);
        err = 0;
    }

    m_Mutex.UnLock();

    return err;
}

/** */
void DCConnectionManager::timerDone() {
    CDCMessage * DCMsg;
    int i;

    for (i = 0; i < 50; i++) {
        if (m_Mutex.TryLock() == false) {
            break;
        }

        if (m_pMessageList && !m_pMessageList->isEmpty()) {
            DCMsg = m_pMessageList->takeFirst();
        } else {
            DCMsg = 0;
        }

        m_Mutex.UnLock();

        if (DCMsg == 0) {
            break;
        }

        switch (DCMsg->m_eType) {
            case DC_MESSAGE_CONNECT_CLIENT:
            {
                DCMessageConnectClient *msg = (DCMessageConnectClient*) DCMsg;

                Connect(msg->m_sHubName, msg->m_sHubHost);

                break;
            }

            default:
            {
                break;
            }
        }

        if (DCMsg) {
            delete DCMsg;
        }
    }

    m_Timer.start(500);
}

/** */
void DCConnectionManager::AutoConnect() {
    DCConfigHubItem * hubitem = 0;
    CList<DCConfigHubItem> list;
    DCConfigHubProfile ConfigHubProfile;

    DCConfig::Instance()->GetBookmarkHubList(&list);

    while ((hubitem = list.Next(hubitem)) != 0) {
        // check for autoconnect
        if (DCConfig::Instance()->GetHubProfile(hubitem->m_sProfile, &ConfigHubProfile)) {
            if (ConfigHubProfile.m_bAutoConnect ||
                    DCConfig::Instance()->GetAutoConnectToAllHubs()) {
                Connect(hubitem->m_sName, hubitem->m_sHost);
            }
        }
    }
}

/** */
void DCConnectionManager::Connect(CString hubname, CString server, bool sslconnect) {
    if (server.IsEmpty()) {
        return;
    }

    if (hubname.IsEmpty()) {
        hubname = server;
    }

    m_Mutex.Lock();

    DCClient * client = dynamic_cast<DCClient*> (GetHub(hubname, server));

    if (client) {
        client->setFocus();
    }

    m_Mutex.UnLock();

    if (client) {
        return;
    }

    QString s;
    int i = server.Find(':');
    if (i >= 0) {
        s = QString::fromAscii(server.Left(i).Data());
    } else {
        s = QString::fromAscii(server.Data());
    }

    client = new DCClient(m_pMdiArea, DCConfig::Instance()->GetRemoteEncoding(hubname, server));
    client->setServer(s);
    client->setObjectName(s);
    client->setAttribute(Qt::WA_DeleteOnClose);

    s = QString::fromAscii(client->GetHubName().Data());
    if (s.length() > 20)
        s = s.left(20) + "...";

    if (m_pTabBar->count() == 0)
        m_pTabBar->show();

    connect(client, SIGNAL(onDie(QWidget*)), DCGuiApp::Instance(), SLOT(slotChildOnDie(QWidget*)));
    addTab((QWidget*) client->GetMdiSubWindow(), QString::fromAscii(hubname.Data()));

    client->setWindowTitle(QString::fromAscii(hubname.Data()));

    switch (DCConfig::Instance()->GetOpenClientWindows()) {
        case eClientMinimized:
            client->showMinimized();
            break;
        case eClientMaximized:
            client->showMaximized();
            client->GetMdiSubWindow()->showMaximized();
            // update tabbar
            slotMdiSubWindowActivated(client->GetMdiSubWindow());
            break;
        case eClientNormal:
        default:
            client->show();
            client->GetMdiSubWindow()->show();
            client->centreOnMdiArea();
            // update tabbar
            slotMdiSubWindowActivated(client->GetMdiSubWindow());
            break;
    }

    CConnectionManager::Connect(hubname, server, client, sslconnect);
}

/** */
void DCConnectionManager::OpenPrivateChat(QString hubname, QString hubhost, QString nick) {
    m_Mutex.Lock();

    DCClient * Client = dynamic_cast<DCClient*> (GetHub(hubname.toAscii().constData(), hubhost.toAscii().constData()));

    if (Client) {
        Client->DC_PrivateChat(nick, QString(), QString(), true);
    }

    m_Mutex.UnLock();
}

/** */
void DCConnectionManager::CloseAllChats(bool onlyOffline) {
    CClient * Client = 0;

    m_Mutex.Lock();

    while ((Client = m_pClientList->Next(Client)) != 0) {
        DCClient * dcc = dynamic_cast<DCClient*> (Client);
        if (dcc) {
            dcc->CloseAllChats(onlyOffline);
        }
    }

    m_Mutex.UnLock();
}

/** */
void DCConnectionManager::DisconnectAllClients() {
    CClient * Client = 0;

    m_Mutex.Lock();

    while ((Client = m_pClientList->Next(Client)) != 0) {
        m_Mutex.UnLock();
        Client->Disconnect(true);
        m_Mutex.Lock();
    }

    m_Mutex.UnLock();
}

/** */
void DCConnectionManager::CloseDisconnectedHubs() {
    CClient * Client = 0, * oClient = 0;

    m_Mutex.Lock();

    while ((Client = m_pClientList->Next(Client)) != 0) {
        if (Client->GetConnectionState() == estNONE) {
            m_Mutex.UnLock();
            DCClient * dcc = dynamic_cast<DCClient*> (Client);
            if (dcc) {
                dcc->GetMdiSubWindow()->close();
            }
            m_Mutex.Lock();

            Client = oClient;
        } else {
            oClient = Client;
        }
    }

    m_Mutex.UnLock();
}

/** */
void DCConnectionManager::CloseAllTabs() {
    windowsList.clear();

    while (m_pTabBar->count() > 0) {
        m_pTabBar->removeTab(0);
    }
}

/** */
void DCConnectionManager::OPKick(QString hubname, QString hubhost, QString nick, QString message) {
    m_Mutex.Lock();

    DCClient * Client = dynamic_cast<DCClient*> (GetHub(hubname.toAscii().constData(), hubhost.toAscii().constData()));

    if (Client) {
        Client->OPKick(nick, message);
    }

    m_Mutex.UnLock();
}

/** */
void DCConnectionManager::OPForceMove(QString hubname, QString hubhost, QString nick, QString message, QString host) {
    m_Mutex.Lock();

    DCClient * Client = dynamic_cast<DCClient*> (GetHub(hubname.toAscii().constData(), hubhost.toAscii().constData()));

    if (Client) {
        Client->OPForceMove(nick, message, host);
    }

    m_Mutex.UnLock();
}

/** */
DCClient * DCConnectionManager::GetClientForHub(CString hubname, CString hubhost) {
    /* using dynamic_cast because there could be non gui CClients
     * present if doing bookmark/public hub search
     */
    return dynamic_cast<DCClient*> (GetHub(hubname, hubhost));
}

void DCConnectionManager::addTab(QWidget *wgt, QString name) {
    if (!wgt || name == "")
        return;
    if (windowsList.contains(wgt) && (windowsList[wgt] != -1))
        return;

    if (name.length() > 20)
        name = name.left(20) + "...";

    const int i = m_pTabBar->addTab(wgt->windowIcon(), name);

    windowsList[wgt] = i;
    m_pTabBar->setCurrentIndex(i);

}

void DCConnectionManager::remTab(QWidget *wgt, bool disc, QWidget *par) {
    if (!wgt || !windowsList.contains(wgt))
        return;

    QList<int> list = windowsList.values();
    int i = windowsList.take(wgt);
    m_pTabBar->removeTab(i);

    qSort(list.begin(), list.end());

    int index = list.indexOf(i);
    index++;

    for (; index < list.count(); index++) {
        QWidget *t = windowsList.key(list.at(index));
        windowsList[t] = list.at(index) - 1;
    }

    if (disc) {
        if (!disconnect(wgt, SIGNAL(onDie(QWidget*)), par, SLOT(slotChildOnDie(QWidget*)))) {
            QMdiSubWindow *mdi_wind;

            try {
                mdi_wind = dynamic_cast<QMdiSubWindow*> (wgt);
            }            catch (std::exception &e) {
                std::cout << e.what() << std::endl;
                return;
            }

            if (!mdi_wind)
                return;

            QWidget *sig_sender = mdi_wind->widget();

            disconnect(sig_sender, SIGNAL(onDie(QWidget*)), par, SLOT(slotChildOnDie(QWidget*)));
        }
    }
}

QWidget *DCConnectionManager::GetWidgetForTab(int tab) const {
    if (tab < 0 || tab > (m_pTabBar->count() - 1))
        return NULL;

    QMapIterator<QWidget*, int> it(windowsList);
    QWidget *wgt = NULL;

    while (it.hasNext()) {
        it.next();
        if (it.value() == tab) {

            wgt = it.key();

            return wgt;
        }
    }

    return wgt;
}

void DCConnectionManager::repaintTabbarIcons() {
    QMapIterator<QWidget*, int> it(windowsList);

    m_pTabBar->drawBase();

    while (it.hasNext()) {
        it.next();

        m_pTabBar->setTabIcon(it.value(), it.key()->windowIcon());
    }
}

//#ifdef QT_NEW

void DCConnectionManager::slotCloseTabButtonClicked(int index)
{
    QWidget *wgt = GetWidgetForTab(index);

    if (wgt)
        wgt->close();

}

//#endif
