//
// This file is part of the imule Project.
//
// Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
// Copyright (c) 2002-2011 Merkur ( devs@emule-project.net / http://www.emule-project.net )
//
// Any parts of this program derived from the xMule, lMule or eMule project,
// or contributed by third-party developers are copyrighted by their
// respective authors.
//
// 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.
//
// 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.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
//


#include <cmath>		// Needed for std::ceil

#include "ColorFrameCtrl.h"	// Needed for CColorFrameCtrl
#include "OtherFunctions.h"	// Needed for CastChild()
#include "OScopeCtrl.h"		// Needed for COScopeCtrl
#include "Preferences.h"	// Needed for thePrefs
#include "muuli_wdr.h"		// Needed for statsDlg()
#include "StatisticsDlg.h"	// Interface declarations
#include "Statistics.h"


class CTreeItemData : public wxTreeItemData
{
public:
        explicit CTreeItemData(uint32_t uniqueid)
                : m_uniqueid(uniqueid)
        {}

        uint32_t GetUniqueId() const throw() { return m_uniqueid; }
        void SetUniqueId(uint32_t val) throw() { m_uniqueid = val; }

private:
        uint32_t m_uniqueid;
};


// CStatisticsDlg panel

const wxColour& CStatisticsDlg::getColors(unsigned num)
{
        wxCHECK(num < 15, *wxBLACK);

        return acrStat[num];
}

CStatisticsDlg::CStatisticsDlg(wxWindow* pParent, CStatistics* stats)
        : wxPanel(pParent, -1)
{
        wxSizer* content=statsDlg(this,TRUE);
        content->Show(this,TRUE);

        pscopeDL	= CastChild( wxT("dloadScope"), COScopeCtrl );
        pscopeDL->graph_type = GRAPH_DOWN;
        pscopeUL	= CastChild( wxT("uloadScope"), COScopeCtrl );
        pscopeUL->graph_type = GRAPH_UP;
        pscopeConn	= CastChild( wxT("otherScope"), COScopeCtrl );
        pscopeConn->graph_type = GRAPH_CONN;
        stattree	= CastChild( wxT("statTree"),	wxTreeCtrl  );

        m_stats = stats;
}



CStatisticsDlg::~CStatisticsDlg()
{

}

void CStatisticsDlg::Init()
{
        InitGraphs();
        InitTree();
}


void CStatisticsDlg::InitGraphs()
{
        // called after preferences get initialised
        for (int index=0; index<=10; ++index) {
                ApplyStatsColor(index);
        }

        pscopeDL->SetRanges(0.0, (float)(thePrefs::GetMaxGraphDownloadRate()+4));
        pscopeDL->SetYUnits(_("kB/s"));
        pscopeUL->SetRanges(0.0, (float)(thePrefs::GetMaxGraphUploadRate()+4));
        pscopeUL->SetYUnits(_("kB/s"));
        pscopeConn->SetRanges(0.0, (float)(thePrefs::GetStatsMax()));
        pscopeConn->SetYUnits(wxEmptyString);

        SetUpdatePeriod(thePrefs::GetTrafficOMeterInterval());
}


// this array is now used to store the current color settings and to define the defaults
wxColour CStatisticsDlg::acrStat[cntStatColors] = {
        wxColour(0,0,64), wxColour(192,192,255), wxColour(128, 255, 128), wxColour(0, 210, 0),
        wxColour(0, 128, 0), wxColour(255, 128, 128), wxColour(200, 0, 0), wxColour(140, 0, 0),
        wxColour(150, 150, 255), wxColour(192, 0, 192), wxColour(255, 255, 128), wxColour(0, 0, 0),
        wxColour(128, 255, 128), wxColour(0, 210, 0), wxColour(0, 128, 0)
};



void CStatisticsDlg::ApplyStatsColor(int index)
{
        static char aTrend[] = { 0,0,  2,     1,     0,           2,     1,     0,          1,    2,    0 };
        static int aRes[] = { 0,0, IDC_C0,IDC_C0_3,IDC_C0_2,  IDC_C1,IDC_C1_3,IDC_C1_2,  IDC_S0,IDC_S3,IDC_S1 };
        static COScopeCtrl** apscope[] = { NULL, NULL, &pscopeDL,&pscopeDL,&pscopeDL, &pscopeUL,&pscopeUL,&pscopeUL, &pscopeConn,&pscopeConn,&pscopeConn };

        const wxColour& cr = acrStat[index];

        int iRes = aRes[index];
        int iTrend = aTrend[index];
        COScopeCtrl** ppscope = apscope[index];
        CColorFrameCtrl* ctrl;
        switch (index) {
        case 0:
                pscopeDL->SetBackgroundColor(cr);
                pscopeUL->SetBackgroundColor(cr);
                pscopeConn->SetBackgroundColor(cr);
                break;
        case 1:
                pscopeDL->SetGridColor(cr);
                pscopeUL->SetGridColor(cr);
                pscopeConn->SetGridColor(cr);
                break;
        case 2:  case 3:  case 4:
        case 5:  case 6:  case 7:
        case 8:  case 9:  case 10:
                (*ppscope)->SetPlotColor(cr, iTrend);
                if ((ctrl= CastChild(iRes, CColorFrameCtrl)) == NULL) {
                        throw wxString(CFormat(wxT("CStatisticsDlg::ApplyStatsColor: control missing (%d)\n")) % iRes);
                }
                ctrl->SetBackgroundBrushColour(cr);
                ctrl->SetFrameBrushColour(*wxBLACK);
                break;
        default:
                break; // ignore unknown index, like SysTray speedbar color
        }
}

void CStatisticsDlg::UpdateStatGraphs(const uint32 peakconnections, const GraphUpdateInfo& update)
{

        std::vector<float *> v1(3);
        v1[0] = const_cast<float *>(&update.downloads[0]);
        v1[1] = const_cast<float *>(&update.downloads[1]);
        v1[2] = const_cast<float *>(&update.downloads[2]);
        const std::vector<float *> &apfDown(v1);
        std::vector<float *> v2(3);
        v2[0] = const_cast<float *>(&update.uploads[0]);
        v2[1] = const_cast<float *>(&update.uploads[1]);
        v2[2] = const_cast<float *>(&update.uploads[2]);
        const std::vector<float *> &apfUp(v2);
        std::vector<float *> v3(3);
        v3[0] = const_cast<float *>(&update.connections[0]);
        v3[1] = const_cast<float *>(&update.connections[1]);
        v3[2] = const_cast<float *>(&update.connections[2]);
        const std::vector<float *> &apfConn(v3);

        if (!IsShownOnScreen()) {
                pscopeDL->DelayPoints();
                pscopeUL->DelayPoints();
                pscopeConn->DelayPoints();
        }

        static unsigned nScalePrev=1;
        unsigned nScale = (unsigned)std::ceil((float)peakconnections / pscopeConn->GetUpperLimit());
        if (nScale != nScalePrev) {
                nScalePrev = nScale;
                wxStaticText* label = CastChild( ID_ACTIVEC, wxStaticText );

                label->SetLabel(CFormat(_("Active connections (1:%u)")) % nScale);
                label->GetParent()->Layout();

                pscopeConn->SetRange(0.0, (float)nScale*pscopeConn->GetUpperLimit(), 1);
        }

        if (!IsShownOnScreen()) {
                return;
        }

        pscopeDL->AppendPoints(update.timestamp, apfDown);
        pscopeUL->AppendPoints(update.timestamp, apfUp);
        pscopeConn->AppendPoints(update.timestamp, apfConn);
}


void CStatisticsDlg::SetUpdatePeriod(int step)
{
        // this gets called after the value in Preferences/Statistics/Update delay has been changed
        if (step == 0) {
                pscopeDL->Stop();
                pscopeUL->Stop();
                pscopeConn->Stop();
        } else {
                pscopeDL->Reset(step);
                pscopeUL->Reset(step);
                pscopeConn->Reset(step);
        }
}


void CStatisticsDlg::ResetAveragingTime()
{
        // this gets called after the value in Preferences/Statistics/time for running avg has been changed
        pscopeDL->InvalidateGraph();
        pscopeUL->InvalidateGraph();
}


void CStatisticsDlg::SetARange(bool SetDownload,int maxValue)
{
        if ( SetDownload ) {
                pscopeDL->SetRanges( 0, maxValue + 4 );
        } else {
                pscopeUL->SetRanges( 0, maxValue + 4 );
        }
}


void  CStatisticsDlg::InitTree()
{
#ifndef CLIENT_GUI
        wxTreeItemId root =
#endif
                stattree->AddRoot(theStats::GetTreeRoot()->GetDisplayString());

        ShowStatistics(true);

#ifndef CLIENT_GUI
        // Expand root
        stattree->Expand(root);

        // Expand main items
        wxTreeItemIdValue cookie;
        wxTreeItemId expand_it = stattree->GetFirstChild(root,cookie);

        while(expand_it.IsOk()) {
                stattree->Expand(expand_it);
                // Next on this level
                expand_it = stattree->GetNextSibling(expand_it);
        }
#endif
}

void CStatisticsDlg::GetExpandedNodes(NodeIdSet& nodeset, const wxTreeItemId& root)
{
        wxTreeItemIdValue cookie;
        wxTreeItemId temp_it = stattree->GetFirstChild(root,cookie);

        while (temp_it.IsOk()) {
                if (stattree->IsExpanded(temp_it)) {
                        nodeset.insert(dynamic_cast<CTreeItemData*>(stattree->GetItemData(temp_it))->GetUniqueId());
                }
                if (stattree->ItemHasChildren(temp_it)) {
                        GetExpandedNodes(nodeset, temp_it);
                }
                temp_it = stattree->GetNextSibling(temp_it);
        }
}

void CStatisticsDlg::ShowStatistics(bool init)
{
        NodeIdSet ExpandedNodes;

        // If it's not the first initialization of the tree, i.e. application startup
        if (!init) {
                GetExpandedNodes(ExpandedNodes, stattree->GetRootItem());
                // Update sorting / get tree via EC
                m_stats->UpdateStatsTree();
        }

        CStatTreeItemBase* treeRoot = theStats::GetTreeRoot();
        wxTreeItemId root = stattree->GetRootItem();
        FillTree(treeRoot, root, ExpandedNodes);
#ifdef CLIENT_GUI
        if (!init) {
                static bool firstUpdate = true;
                if (firstUpdate) {
                        // Expand root
                        root = stattree->GetRootItem();
                        stattree->Expand(root);

                        // Expand main items
                        wxTreeItemIdValue cookie;
                        wxTreeItemId expand_it = stattree->GetFirstChild(root,cookie);

                        while(expand_it.IsOk()) {
                                stattree->Expand(expand_it);
                                // Next on this level
                                expand_it = stattree->GetNextSibling(expand_it);
                        }
                        firstUpdate = false;
                }
        }
#endif
}
#ifdef CLIENT_GUI
void CStatisticsDlg::RebuildStatTreeRemote(const CECTag * tag)
{
        m_stats->RebuildStatTreeRemote(tag);
}
#endif


void CStatisticsDlg::FillTree(CStatTreeItemBase* statssubtree, wxTreeItemId& StatsGUITree, const NodeIdSet& expandednodes)
{
        wxMutexLocker lock(statssubtree->GetLock());

#ifndef CLIENT_GUI
        StatTreeItemIterator temp_it = statssubtree->GetFirstVisibleChild(thePrefs::GetMaxClientVersions());
#else
        StatTreeItemIterator temp_it = statssubtree->GetFirstVisibleChild();
#endif

        wxTreeItemIdValue cookie;
        wxTreeItemId temp_GUI_it = stattree->GetFirstChild(StatsGUITree,cookie);

        while (!statssubtree->IsAtEndOfList(temp_it)) {
                wxTreeItemId temp_item;
                if (temp_GUI_it.IsOk()) {
                        // There's already a child there, update it.
                        stattree->SetItemText(temp_GUI_it, (*temp_it)->GetDisplayString());
                        temp_item = temp_GUI_it;
                        uint32_t uid = (*temp_it)->GetUniqueId();
                        dynamic_cast<CTreeItemData*>(stattree->GetItemData(temp_GUI_it))->SetUniqueId(uid);
                        if (expandednodes.find(uid) != expandednodes.end()) {
                                stattree->Expand(temp_GUI_it);
                        } else {
                                stattree->Collapse(temp_GUI_it);
                        }
                        temp_GUI_it = stattree->GetNextSibling(temp_GUI_it);
                } else {
                        // No more child on GUI, add them.
                        temp_item = stattree->AppendItem(StatsGUITree,(*temp_it)->GetDisplayString());
                        stattree->SetItemData(temp_item, new CTreeItemData((*temp_it)->GetUniqueId()));
                }
                // Has childs?
                if ((*temp_it)->HasVisibleChildren()) {
                        FillTree((*temp_it), temp_item, expandednodes);
                } else {
                        stattree->DeleteChildren(temp_item);
                }
                statssubtree->GetNextVisibleChild(temp_it);
        }

        // What if GUI has more items than tree?
        // Delete the extra items.
        while (temp_GUI_it.IsOk()) {
                wxTreeItemId backup_node = stattree->GetNextSibling(temp_GUI_it);
                stattree->DeleteChildren(temp_GUI_it);
                stattree->Delete(temp_GUI_it);
                temp_GUI_it = backup_node;
        }
}
// File_checked_for_headers