/*
===========================================================================
Copyright (C) 2015 the OpenMoHAA team

This file is part of OpenMoHAA source code.

OpenMoHAA source code 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.

OpenMoHAA source code 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 OpenMoHAA source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/

#include "ui_local.h"
#include "../qcommon/localization.h"

static UISize2D s_columnpadding(2.0, 2.0);

bool              UIListCtrl::s_qsortreverse;
int               UIListCtrl::s_qsortcolumn;
class UIListCtrl *UIListCtrl::s_qsortobject;

CLASS_DECLARATION(UIListBase, UIListCtrl, NULL) {
    {&W_SizeChanged,      &UIListCtrl::OnSizeChanged},
    {&W_LeftMouseDown,    &UIListCtrl::MousePressed },
    {&W_LeftMouseUp,      &UIListCtrl::MouseReleased},
    {&W_LeftMouseDragged, &UIListCtrl::MouseDragged },
    {&W_MouseEntered,     &UIListCtrl::MouseEntered },
    {NULL,                NULL                      }
};

UIListCtrl::UIListCtrl()
{
    m_clickState.point.x = m_clickState.point.y = 0;
    m_sizestate.column                          = 0;
    m_iLastSortColumn                           = 0;
    m_bDrawHeader                               = qtrue;
    m_headerfont                                = NULL;

    Connect(this, W_SizeChanged, W_SizeChanged);
    m_background_color = UColor(0.02f, 0.07f, 0.005f, 1.0f);
    m_foreground_color = UHudColor;
}

UIListCtrl::~UIListCtrl()
{
    if (m_headerfont) {
        delete m_headerfont;
        m_headerfont = NULL;
    }
}

int UIListCtrl::StringCompareFunction(const UIListCtrlItem *i1, const UIListCtrlItem *i2, int columnname)
{
    if (i1->getListItemType(columnname) != TYPE_STRING || i2->getListItemType(columnname) != TYPE_STRING) {
        return 0;
    }

    const char *s1 = i1->getListItemString(columnname);
    const char *s2 = i2->getListItemString(columnname);

    if (*s1 == '.') {
        if (*s2 != '.') {
            return -1;
        }
    } else if (*s2 == '.') {
        return 1;
    }

    if (*s1 == '[') {
        if (*s2 != '[') {
            return -1;
        }
    } else if (*s2 == '[') {
        return 1;
    }

    return Q_stricmp(s1, s2);
}

int UIListCtrl::StringNumberCompareFunction(const UIListCtrlItem *i1, const UIListCtrlItem *i2, int columnname)
{
    int val1, val2;

    val1 = i1->getListItemValue(columnname);
    val2 = i2->getListItemValue(columnname);

    if (val1 > val2) {
        return 1;
    } else if (val1 < val2) {
        return -1;
    } else {
        return 0;
    }
}

int UIListCtrl::QsortCompare(const void *e1, const void *e2)
{
    int val =
        s_qsortobject->m_comparefunction(*(const UIListCtrlItem **)e1, *(const UIListCtrlItem **)e2, s_qsortcolumn);
    return s_qsortreverse ? -val : val;
}

void UIListCtrl::Draw(void)
{
    if (m_bDrawHeader) {
        DrawColumns();
    }

    DrawContent();
}

int UIListCtrl::getHeaderHeight(void)
{
    if (!m_bDrawHeader) {
        return 0;
    }

    if (m_headerfont) {
        return (int)(m_headerfont->getHeight(getVirtualScale())
                     + (s_columnpadding.height + s_columnpadding.height) * getVirtualScale()[1]);
    }

    if (m_font) {
        return (int)(m_font->getHeight(getVirtualScale())
                     + (s_columnpadding.height + s_columnpadding.height) * getVirtualScale()[1]);
    }

    return (int)((s_columnpadding.height + s_columnpadding.height) * getVirtualScale()[1]);
}

void UIListCtrl::MousePressed(Event *ev)
{
    UIPoint2D p;

    p = MouseEventToClientPoint(ev);

    if (m_clicksound.length()) {
        uii.Snd_PlaySound(m_clicksound.c_str());
    }

    if (getHeaderHeight() > p.y) {
        int closesep    = 0;
        int atleft      = 0;
        int closeatleft = 0;
        int insep       = 0;

        for (int i = 1; i <= m_columnlist.NumObjects(); i++) {
            const columndef_t& column = m_columnlist.ObjectAt(i);
            atleft += column.width;

            if (abs(atleft - p.x) > 2) {
                if (atleft - column.width < p.x && atleft > p.x) {
                    insep = i;
                }
            } else {
                closesep    = i;
                closeatleft = atleft;
            }
        }

        if (closesep) {
            m_sizestate.column = closesep;
            m_sizestate.min    = closeatleft - m_columnlist.ObjectAt(closesep).width;
            uWinMan.setFirstResponder(this);
        } else if (insep) {
            SortByColumn(m_columnlist.ObjectAt(insep).name);
        }
    } else {
        if (m_vertscroll) {
            TrySelectItem(
                (m_vertscroll->getTopItem() + 1) + (p.y - getHeaderHeight()) / m_font->getHeight(getVirtualScale())
            );
        } else {
            TrySelectItem(1.0 + (p.y - getHeaderHeight()) / m_font->getHeight(getVirtualScale()));
        }

        if (m_clickState.time + 500 > uid.time && m_currentItem == m_clickState.selected
            && fabs(m_clickState.point.x - p.x) <= 2.0 && fabs(m_clickState.point.y - p.y) <= 2.0) {
            Event newev(EV_UIListBase_ItemDoubleClicked);
            newev.AddInteger(m_currentItem);
            SendSignal(newev);
            m_clickState.time = 0;
        } else {
            m_clickState.time     = uid.time;
            m_clickState.selected = m_currentItem;
            m_clickState.point.x  = p.x;
            m_clickState.point.y  = p.y;
        }
    }
}

void UIListCtrl::MouseDragged(Event *ev)
{
    UIPoint2D p;
    int       newwidth;

    p = MouseEventToClientPoint(ev);
    if (m_sizestate.column) {
        columndef_t& column = m_columnlist.ObjectAt(m_sizestate.column);

        newwidth = p.x - m_sizestate.min;
        if (newwidth > 600) {
            newwidth = 600;
        } else if (newwidth < 0) {
            newwidth = 0;
        }

        column.width = newwidth;
    }
}

void UIListCtrl::MouseReleased(Event *ev)
{
    m_sizestate.column = 0;

    if (uWinMan.getFirstResponder() == this) {
        uWinMan.setFirstResponder(NULL);
    }
}

void UIListCtrl::MouseEntered(Event *ev)
{
    uWinMan.ActivateControl(this);
}

void UIListCtrl::OnSizeChanged(Event *ev)
{
    if (!m_vertscroll) {
        return;
    }

    m_vertscroll->InitFrame(this, m_frame.size.width - 16.0, 0.0, 16.0, m_frame.size.height, -1);
    m_vertscroll->setPageHeight((m_frame.size.height - getHeaderHeight()) / m_font->getHeight(getVirtualScale()));
    m_vertscroll->setNumItems(m_itemlist.NumObjects());
}

void UIListCtrl::DrawColumns(void)
{
    int     atleft;
    int     i;
    int     height;
    UIFont *pFont;
    UColor  columnColor, textColor;

    atleft      = 0;
    columnColor = UColor(0.07f, 0.06f, 0.005f, 1.0f);
    textColor   = UHudColor;

    pFont = m_headerfont;
    if (!pFont) {
        pFont = m_font;
    }

    height =
        (s_columnpadding.height + s_columnpadding.height) * getVirtualScale()[1] + pFont->getHeight(getVirtualScale());
    pFont->setColor(textColor);

    for (i = 1; i <= m_columnlist.NumObjects(); i++) {
        const columndef_t& column = m_columnlist.ObjectAt(i);

        DrawBoxWithSolidBorder(
            UIRect2D(atleft, 0, column.width + getVirtualScale()[1], height),
            columnColor,
            textColor,
            1,
            3,
            m_local_alpha
        );

        pFont->Print(
            atleft / getVirtualScale()[0] + s_columnpadding.width,
            s_columnpadding.height,
            Sys_LV_CL_ConvertString(column.title.c_str()),
            -1,
            getVirtualScale()
        );

        atleft += column.width;
    }

    if (m_frame.size.width > atleft) {
        DrawBoxWithSolidBorder(
            UIRect2D(atleft, 0, m_frame.size.width - atleft + 1.0, height), columnColor, textColor, 1, 3, m_local_alpha
        );
    }
}

void UIListCtrl::DrawContent(void)
{
    int     item;
    int     height, headerheight, iItemHeight;
    UIFont *pFont;
    int     top;
    UColor  selColor, selText;
    UColor  backColor, textColor;

    height    = m_font->getHeight(getVirtualScale());
    selColor  = UColor(0.21f, 0.18f, 0.015f, 1.0f);
    selText   = UColor(0.9f, 0.8f, 0.6f, 1.0f);
    backColor = m_background_color;
    textColor = m_foreground_color;

    if (m_headerfont) {
        headerheight = m_headerfont->getHeight(getVirtualScale());
    } else {
        headerheight = 0;
    }

    selColor.a  = m_local_alpha;
    selText.a   = m_local_alpha;
    backColor.a = m_local_alpha;
    textColor.a = m_local_alpha;
    top         = getHeaderHeight();
    if (m_vertscroll) {
        item = m_vertscroll->getTopItem() + 1;
    } else {
        item = 1;
    }

    for (; item <= m_itemlist.NumObjects(); item++) {
        UIListCtrlItem *theitem;
        int             col;
        int             atleft;
        UColor         *itemBg;
        UColor         *itemText;

        theitem = m_itemlist.ObjectAt(item);
        atleft  = 0;

        if (item == m_currentItem) {
            itemBg   = &selColor;
            itemText = &selText;
        } else {
            itemBg   = &backColor;
            itemText = &textColor;
        }

        if (theitem->IsHeaderEntry() && m_headerfont) {
            pFont       = m_headerfont;
            iItemHeight = headerheight;
        } else {
            pFont       = m_font;
            iItemHeight = height;
        }

        pFont->setColor(*itemText);
        for (col = 1; col <= m_columnlist.NumObjects(); col++) {
            columndef_t&   column = m_columnlist.ObjectAt(col);
            UIRect2D       drawRect(atleft, top, column.width, iItemHeight);
            griditemtype_t itemtype = theitem->getListItemType(column.name);

            switch (itemtype) {
            case TYPE_STRING:
                DrawBox(drawRect, *itemBg, m_local_alpha);
                pFont->Print(
                    drawRect.pos.x / getVirtualScale()[0] + 1.0,
                    drawRect.pos.y / getVirtualScale()[1],
                    Sys_LV_CL_ConvertString(theitem->getListItemString(column.name)),
                    -1,
                    getVirtualScale()
                );
                break;
            case TYPE_OWNERDRAW:
                theitem->DrawListItem(column.name, drawRect, item == m_currentItem, pFont);
                break;
            }

            atleft += column.width;
        }

        if (m_frame.size.width > atleft) {
            DrawBox(atleft, top, m_frame.size.width - atleft, height, *itemBg, m_local_alpha);
        }

        top += iItemHeight;
        if (iItemHeight + top > m_frame.size.height) {
            break;
        }
    }
}

void UIListCtrl::FrameInitialized(void)
{
    UIListBase::FrameInitialized();
    OnSizeChanged(NULL);
}

void UIListCtrl::SetDrawHeader(qboolean bDrawHeader)
{
    m_bDrawHeader = bDrawHeader;
}

void UIListCtrl::AddItem(UIListCtrlItem *item)
{
    m_itemlist.AddObject(item);
    if (m_vertscroll) {
        m_vertscroll->setNumItems(m_itemlist.NumObjects());
    }
}

void UIListCtrl::InsertItem(UIListCtrlItem *item, int where)
{
    if (where > 0 && where <= m_itemlist.NumObjects()) {
        int i;

        m_itemlist.AddObject(NULL);

        for (i = m_itemlist.NumObjects(); i > where; i++) {
            m_itemlist.SetObjectAt(i, m_itemlist.ObjectAt(i - 1));
        }

        m_itemlist.SetObjectAt(where, item);
    } else {
        m_itemlist.AddObject(item);
    }

    if (m_vertscroll) {
        m_vertscroll->setNumItems(m_itemlist.NumObjects());
    }
}

int UIListCtrl::FindItem(UIListCtrlItem *item)
{
    return m_itemlist.IndexOfObject(item);
}

UIListCtrlItem *UIListCtrl::GetItem(int item)
{
    return m_itemlist.ObjectAt(item);
}

void UIListCtrl::AddColumn(str title, int name, int width, bool numeric, bool reverse_sort)
{
    columndef_t column;

    column.title        = title;
    column.name         = name;
    column.width        = width;
    column.numeric      = numeric;
    column.reverse_sort = reverse_sort;

    m_columnlist.AddObject(column);
}

void UIListCtrl::RemoveAllColumns(void)
{
    m_columnlist.ClearObjectList();
}

int UIListCtrl::getNumItems(void)
{
    return m_itemlist.NumObjects();
}

void UIListCtrl::DeleteAllItems(void)
{
    m_itemlist.ClearObjectList();
    m_currentItem = 0;

    if (m_vertscroll) {
        m_vertscroll->setNumItems(0);
        m_vertscroll->setTopItem(0);
    }
}

void UIListCtrl::DeleteItem(int which)
{
    m_itemlist.RemoveObjectAt(which);

    if (m_vertscroll) {
        m_vertscroll->setNumItems(m_itemlist.NumObjects());
    }

    if (m_currentItem > getNumItems()) {
        TrySelectItem(getNumItems());
    }
}

void UIListCtrl::SortByColumn(int column)
{
    int             i;
    bool            exists;
    bool            numeric;
    bool            reverse;
    UIListCtrlItem *selected;
    bool            selvisible;

    exists     = 0;
    numeric    = 0;
    reverse    = 0;
    selected   = NULL;
    selvisible = 0;

    for (i = 1; i <= m_columnlist.NumObjects(); i++) {
        columndef_t& def = m_columnlist.ObjectAt(i);

        if (def.name == column) {
            numeric = def.numeric;
            reverse = def.reverse_sort;
            exists  = true;
            break;
        }
    }

    if (exists) {
        m_iLastSortColumn = column;
        s_qsortcolumn     = column;
        s_qsortobject     = this;
        s_qsortreverse    = reverse;

        if (m_currentItem) {
            selected = m_itemlist.ObjectAt(m_currentItem);
            if (m_vertscroll) {
                selvisible = false;
                if (m_currentItem >= m_vertscroll->getTopItem() + 1) {
                    selvisible = m_currentItem <= m_vertscroll->getPageHeight() + m_vertscroll->getTopItem();
                }
            } else {
                selvisible = false;
                // FIXME: selvisible when there is no vertical scroll?
                //
                //if (m_currentItem > 0) {
                //	selvisible = m_currentItem <= m_vertscroll->m_pageheight + m_vertscroll->getTopItem();
                //}
            }
        }

        if (numeric) {
            m_comparefunction = &UIListCtrl::StringNumberCompareFunction;
        } else {
            m_comparefunction = &UIListCtrl::StringCompareFunction;
        }

        if (m_itemlist.NumObjects()) {
            m_itemlist.Sort(&UIListCtrl::QsortCompare);
        }

        if (selected) {
            if (selvisible) {
                TrySelectItem(FindItem(selected));
            } else {
                m_currentItem = FindItem(selected);
            }
        }
    } else {
        m_iLastSortColumn = 0;
    }
}

void UIListCtrl::SortByLastSortColumn(void)
{
    SortByColumn(m_iLastSortColumn);
}

void UIListCtrl::setCompareFunction(int (*func)(const UIListCtrlItem *i1, const UIListCtrlItem *i2, int columnname))
{
    m_comparefunction = func;
}

void UIListCtrl::setHeaderFont(const char *name)
{
    if (m_headerfont) {
        delete m_headerfont;
    }

    if (name) {
        m_headerfont = new UIFont(name);
    } else {
        m_headerfont = NULL;
    }
}
