#include <qlayout.h>
#include <qpushbutton.h>
#include <qbuttongroup.h>
#include <qlabel.h>
#include <qcursor.h>
#include <qsqldatabase.h>
#include <qdatetime.h>
#include <qapplication.h>
#include <qregexp.h>
#include <qheader.h>

#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;

#include "dialogbox.h"
#include "infostructs.h"
#include "viewrankings.h"

ViewRankings::ViewRankings(QSqlDatabase *ldb, 
                                       MythMainWindow *parent, const char *name)
                  : MythDialog(parent, name)
{
    db = ldb;

    curitem = NULL;
    pageDowner = false;
    bgTransBackup = NULL;

    listCount = 0;
    dataCount = 0;

    fullRect = QRect(0, 0, size().width(), size().height());
    listRect = QRect(0, 0, 0, 0);
    infoRect = QRect(0, 0, 0, 0);

    theme = new XMLParse();
    theme->SetWMult(wmult);
    theme->SetHMult(hmult);
    if (!theme->LoadTheme(xmldata, "programrankview"))
    {
        DialogBox diag(gContext->GetMainWindow(), "The theme you are using "
                       "does not contain a 'programrankview' element.  "
                       "Please contact the theme creator and ask if they could "
                       "please update it.<br><br>The next screen will be empty."
                       "  Escape out of it to return to the menu.");
        diag.AddButton("OK");
        diag.exec();

        return;
    }

    LoadWindow(xmldata);

    LayerSet *container = theme->GetSet("selector");
    if (container)
    {
        UIListType *ltype = (UIListType *)container->GetType("ranklist");
        if (ltype)
        {
            listsize = ltype->GetItems();
        }
    }
    else
    {
        cerr << "MythFrontEnd: ViewRankings - Failed to get selector.\n";
        exit(0);
    }

    bgTransBackup = gContext->LoadScalePixmap("trans-backup.png");
    if (!bgTransBackup)
        bgTransBackup = new QPixmap();

    updateBackground();

    FillList();
    sortType = (SortType)gContext->GetNumSetting("ViewRankingsSorting",
                                                 (int)byTitle);

    SortList(); 
    inList = 0;
    inData = 0;
    setNoErase();

    gContext->addListener(this);
}

ViewRankings::~ViewRankings()
{
    gContext->removeListener(this);
    delete theme;
    if (bgTransBackup)
        delete bgTransBackup;
    if (curitem)
        delete curitem;
}

void ViewRankings::keyPressEvent(QKeyEvent *e)
{
    bool handled = false;
    QStringList actions;
    if (gContext->GetMainWindow()->TranslateKeyPress("TV Frontend", e, actions))
    {
        for (unsigned int i = 0; i < actions.size() && !handled; i++)
        {
            QString action = actions[i];
            handled = true;

            if (action == "UP")
                cursorUp();
            else if (action == "DOWN")
                cursorDown();
            else if (action == "PAGEUP")
                pageUp();
            else if (action == "PAGEDOWN")
                pageDown();
            else if (action == "RIGHT")
                changeRank(1);
            else if (action == "LEFT")
                changeRank(-1);
            else if (action == "ESCAPE")
            {
                saveRank(); 
                gContext->SaveSetting("ViewRankingsSorting",
                                      (int)sortType);
                done(MythDialog::Accepted);
            }
            else if (action == "1")
            {
                if (sortType != byTitle)
                {
                    sortType = byTitle; 
                    SortList();     
                    update(fullRect);
                }
            }
            else if (action == "2")
            {
                if (sortType != byRank)
                {
                    sortType = byRank; 
                    SortList(); 
                    update(fullRect);
                }
            }
            else
                handled = false;
        }
    }

    if (!handled)
        MythDialog::keyPressEvent(e);
}

void ViewRankings::LoadWindow(QDomElement &element)
{
    for (QDomNode child = element.firstChild(); !child.isNull();
         child = child.nextSibling())
    {
        QDomElement e = child.toElement();
        if (!e.isNull())
        {
            if (e.tagName() == "font")
            {
                theme->parseFont(e);
            }
            else if (e.tagName() == "container")
            {
                parseContainer(e);
            }
            else
            {
                cerr << "Unknown element: " << e.tagName() << endl;
                exit(0);
            }
        }
    }
}

void ViewRankings::parseContainer(QDomElement &element)
{
    QRect area;
    QString name;
    int context;
    theme->parseContainer(element, name, context, area);

    if (name.lower() == "selector")
        listRect = area;
}

void ViewRankings::updateBackground(void)
{
    QPixmap bground(size());
    bground.fill(this, 0, 0);

    QPainter tmp(&bground);

    LayerSet *container = theme->GetSet("background");
        container->Draw(&tmp, 0, 0);

    tmp.end();
    myBackground = bground;

    setPaletteBackgroundPixmap(myBackground);
}

void ViewRankings::paintEvent(QPaintEvent *e)
{
    QRect r = e->rect();
    QPainter p(this);
 
    if (r.intersects(listRect))
    {
        updateList(&p);
    }
}

void ViewRankings::cursorDown(bool page)
{
    if (page == false)
    {
        if (inList > (int)((int)(listsize / 2) - 1)
            && ((int)(inData + listsize) <= (int)(dataCount - 1))
            && pageDowner == true)
        {
            inData++;
            inList = (int)(listsize / 2);
        }
        else
        {
            inList++;

            if (inList >= listCount)
                inList = listCount - 1;
        }
    }
    else if (page == true && pageDowner == true)
    {
        if (inList >= (int)(listsize / 2) || inData != 0)
        {
            inData = inData + listsize;
        }
        else if (inList < (int)(listsize / 2) && inData == 0)
        {
            inData = (int)(listsize / 2) + inList;
            inList = (int)(listsize / 2);
        }
    }
    else if (page == true && pageDowner == false)
    {
        inList = listsize - 1;
    }

    if ((int)(inData + inList) >= (int)(dataCount))
    {
        inData = dataCount - listsize;
        inList = listsize - 1;
    }
    else if ((int)(inData + listsize) >= (int)dataCount)
    {
        inData = dataCount - listsize;
    }

    if (inData < 0) 
        inData = 0;

    if (inList >= listCount)
        inList = listCount - 1;

    update(fullRect);
}

void ViewRankings::cursorUp(bool page)
{
    if (page == false)
    {
        if (inList < ((int)(listsize / 2) + 1) && inData > 0)
        {
            inList = (int)(listsize / 2);
            inData--;
            if (inData < 0)
            {
                inData = 0;
                inList--;
            }
         }
         else
         {
             inList--;
         }
     }
     else if (page == true && inData > 0)
     {
         inData = inData - listsize;
         if (inData < 0)
         {
             inList = inList + inData;
             inData = 0;
             if (inList < 0)
                 inList = 0;
         }

         if (inList > (int)(listsize / 2))
         {
             inList = (int)(listsize / 2);
             inData = inData + (int)(listsize / 2) - 1;
         }
     }
     else if (page == true)
     {
         inData = 0;
         inList = 0;
     }

     if (inList > -1)
     {
         update(fullRect);
     }
     else
         inList = 0;
}

void ViewRankings::changeRank(int howMuch) 
{
    int tempRank, cnt;
    QPainter p(this);
    QMap<QString, PrgRankInfo>::Iterator it;
    PrgRankInfo *prgrankInfo;

    // iterate through programData till we hit the line where
    // the cursor currently is
    for (cnt=0, it = programData.begin(); cnt < inList+inData; cnt++, ++it);

    prgrankInfo = &(it.data());

    // inc/dec rank
    tempRank = prgrankInfo->m_strRank.toInt() + howMuch;
    if(tempRank >= MINRANKALLOWED && tempRank <= MAXRANKALLOWED)
    {
        prgrankInfo->m_strRank = QString::number(tempRank);

        // order may change if sorting by rank, so resort
        if (sortType == byRank)
            SortList();
        updateList(&p);
    }
}

void ViewRankings::applyViewRankingsChange(QSqlDatabase *db, 
                                                       QString title, 
                                                       QString category, 
                                                 const QString &newRank) 
{
    QString sqltitle = title;
    QString sqlcategory = category;
                                                                                
    sqltitle.replace(QRegExp("\""), QString("\\\""));
    sqlcategory.replace(QRegExp("\""), QString("\\\""));

    QString query = QString("UPDATE prgRank SET rank = %3 WHERE title = \"%1\""
                         " AND category = \"%2\"")
                                   .arg(sqltitle.utf8())
                                   .arg(sqlcategory.utf8())
                                   .arg(newRank);

    QSqlQuery result = db->exec(query);
    if (!result.isActive())
        MythContext::DBError("Save prgRank update", query);
}

void ViewRankings::saveRank(void) 
{
    QMap<QString, PrgRankInfo>::Iterator it;

    for (it = programData.begin(); it != programData.end(); ++it) 
    {
        PrgRankInfo *prgrankInfo = &(it.data());
        QString key = prgrankInfo->m_strTitle + prgrankInfo->m_strCategory;

        // if this channel's rank changed from when we entered
        // save new value out to db
        if (prgrankInfo->m_strRank != origRankData[key])
            applyViewRankingsChange(db, 
                                   prgrankInfo->m_strTitle,
                                   prgrankInfo->m_strCategory,
                                   prgrankInfo->m_strRank);
    }
}

void ViewRankings::FillList(void)
{
    int cnt = 999;

    programData.clear();

    QString query = QString("SELECT title, category, rank FROM prgRank;");

    QSqlQuery result = db->exec(query);

    if (result.isActive() && result.numRowsAffected() > 0)
    {
        while (result.next()) {
            PrgRankInfo *prankinfo = new PrgRankInfo;
            prankinfo->m_strTitle = result.value(0).toString();
            prankinfo->m_strCategory = result.value(1).toString();
            prankinfo->m_strRank = result.value(2).toString();
            programData[QString::number(cnt)] = *prankinfo;

            // save rank value in map so we don't have to save 
            // all program's rank values when we exit
            origRankData[prankinfo->m_strTitle + prankinfo->m_strCategory] = 
                    prankinfo->m_strRank;

            cnt--;
            dataCount++;
        }
    }
    else if (!result.isActive())
        MythContext::DBError("Get program rank query", query);
}

typedef struct RankInfo 
{
    PrgRankInfo *pri;
    int cnt;
};

class prgSort 
{
    public:
        bool operator()(const RankInfo a, const RankInfo b) 
        {
            return(a.pri->m_strTitle > b.pri->m_strTitle);
        }
};

class prgRankSort 
{
    public:
        bool operator()(const RankInfo a, const RankInfo b) 
        {
            if (a.pri->m_strRank.toInt() == b.pri->m_strRank.toInt())
                return (a.pri->m_strTitle > b.pri->m_strTitle);
            return (a.pri->m_strRank.toInt() < b.pri->m_strRank.toInt());
        }
};

void ViewRankings::SortList() 
{
    typedef vector<RankInfo> sortList;
    typedef QMap<QString, PrgRankInfo> prgMap;
    
    int i, j;
    bool cursorChanged = false;
    sortList sortedList;
    prgMap::Iterator pit;
    sortList::iterator sit;
    PrgRankInfo *prgrankInfo;
    RankInfo *prgRankInfo;
    prgMap cdCopy;

    // copy programData into sortedList and make a copy
    // of programData in cdCopy
    for (i = 0, pit = programData.begin(); pit != programData.end(); ++pit, i++)
    {
        prgrankInfo = &(pit.data());
        RankInfo tmp = {prgrankInfo, i};
        sortedList.push_back(tmp);
        cdCopy[pit.key()] = pit.data();
    }

    switch(sortType) 
    {
        case byTitle : sort(sortedList.begin(), sortedList.end(), 
                              prgSort());
                         break;
        case byRank : sort(sortedList.begin(), sortedList.end(), 
                           prgRankSort());
                      break;
    }

    programData.clear();

    // rebuild programData in sortedList order from cdCopy
    for(i = 0, sit = sortedList.begin(); sit != sortedList.end(); i++, ++sit ) 
    {
        prgRankInfo = &(*sit);

        // find prgRankInfo[i] in cdCopy
        for (j = 0, pit = cdCopy.begin(); j !=prgRankInfo->cnt; j++, ++pit);

        prgrankInfo = &(pit.data());

        // put back into programData
        programData[QString::number(999-i)] = pit.data();

        // if prgRankInfo[i] is the title where the cursor
        // was pre-sort then we need to update to cursor
        // to the ith position
        if (!cursorChanged && prgRankInfo->cnt == inList+inData) 
        {
            inList = dataCount - i - 1;
            if (inList > (int)((int)(listsize / 2) - 1)) 
            {
                inList = (int)(listsize / 2);
                inData = dataCount - i - 1 - inList;
            }
            else
                inData = 0;

            if (dataCount > listsize && inData > dataCount - listsize) 
            {
                inList += inData - (dataCount - listsize);
                inData = dataCount - listsize;
            }
            cursorChanged = true;
        }
    }
}

void ViewRankings::updateList(QPainter *p)
{
    QRect pr = listRect;
    QPixmap pix(pr.size());
    pix.fill(this, pr.topLeft());
    QPainter tmp(&pix);
    
    int pastSkip = (int)inData;
    pageDowner = false;
    listCount = 0;

    LayerSet *container = NULL;
    container = theme->GetSet("selector");
    if (container)
    {
        UIListType *ltype = (UIListType *)container->GetType("ranklist");
        if (ltype)
        {
            int cnt = 0;
            ltype->ResetList();
            ltype->SetActive(true);

            QMap<QString, PrgRankInfo>::Iterator it;
            for (it = programData.begin(); it != programData.end(); ++it)
            {
                if (cnt < listsize)
                {
                    if (pastSkip <= 0)
                    {
                        PrgRankInfo *prgrankInfo = &(it.data());
                        int prgRank = prgrankInfo->m_strRank.toInt();

                        if (cnt == inList)
                        {
                            if (curitem)
                                delete curitem;
                            curitem = new PrgRankInfo(*prgrankInfo);
                            ltype->SetItemCurrent(cnt);
                        }
                        
                        ltype->SetItemText(cnt, 1, prgrankInfo->m_strTitle);
                        ltype->SetItemText(cnt, 2, prgrankInfo->m_strCategory);
                        if (prgrankInfo->m_strRank.toInt() > 0)
                            ltype->SetItemText(cnt, 3, "+");
                        else if (prgrankInfo->m_strRank.toInt() < 0)
                            ltype->SetItemText(cnt, 3, "-");
                        ltype->SetItemText(cnt, 4, 
                                           QString::number(abs(prgRank)));

                        cnt++;
                        listCount++;
                    }
                    pastSkip--;
                }
                else
                    pageDowner = true;
            }

            ltype->SetDownArrow(pageDowner);
            if (inData > 0)
                ltype->SetUpArrow(true);
            else
                ltype->SetUpArrow(false);
        }
    }

    if (programData.count() <= 0)
        container = theme->GetSet("norankings_list");

    if (container)
    {
       container->Draw(&tmp, 0, 0);
       container->Draw(&tmp, 1, 0);
       container->Draw(&tmp, 2, 0);
       container->Draw(&tmp, 3, 0);
       container->Draw(&tmp, 4, 0);
       container->Draw(&tmp, 5, 0);
       container->Draw(&tmp, 6, 0);
       container->Draw(&tmp, 7, 0);
       container->Draw(&tmp, 8, 0);
    }

    tmp.end();
    p->drawPixmap(pr.topLeft(), pix);
}

