/*
===========================================================================
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"

CLASS_DECLARATION(UIWidget, UIListBase, NULL) {
    {NULL, NULL}
};

UIListBase::UIListBase()
{
    m_currentItem    = 0;
    m_vertscroll     = 0;
    m_bUseVertScroll = true;
    AllowActivate(true);
}

void UIListBase::TrySelectItem(int which)
{
    int numitems;

    numitems = getNumItems();
    if (!numitems) {
        m_currentItem = 0;
        return;
    }

    if (m_currentItem != which) {
        int previous = m_currentItem;

        m_currentItem = which;
        if (which > numitems) {
            m_currentItem = numitems;
        }

        if (m_currentItem <= 0) {
            m_currentItem = 1;
        }

        if (previous != m_currentItem) {
            Event ev(EV_UIListBase_ItemSelected);
            ev.AddInteger(m_currentItem);
            SendSignal(ev);
        }
    }

    if (m_vertscroll) {
        if (m_currentItem < m_vertscroll->getTopItem() + 1) {
            if (m_currentItem - 1 >= 0) {
                m_vertscroll->setTopItem(m_currentItem - 1);
            } else {
                m_vertscroll->setTopItem(0);
            }
        }

        if (m_currentItem >= m_vertscroll->getTopItem() + m_vertscroll->getPageHeight() + 1) {
            if (m_currentItem - m_vertscroll->getPageHeight() >= 0) {
                m_vertscroll->setTopItem(m_currentItem - m_vertscroll->getPageHeight());
            } else {
                m_vertscroll->setTopItem(0);
            }
        }
    }
}

qboolean UIListBase::KeyEvent(int key, unsigned int time)
{
    int      offsetitem;
    qboolean key_rec;
    int      itemindex;

    offsetitem = 0;
    key_rec    = qfalse;

    switch (key) {
    case K_UPARROW:
    case K_LEFTARROW:
        offsetitem = -1;
        key_rec    = qtrue;
        break;
    case K_DOWNARROW:
    case K_RIGHTARROW:
        offsetitem = 1;
        key_rec    = qtrue;
        break;
    case K_PGDN:
        if (GetScrollBar()) {
            itemindex = GetScrollBar()->getTopItem() + GetScrollBar()->getPageHeight();
            if (getCurrentItem() == itemindex) {
                offsetitem = GetScrollBar()->getPageHeight();
            } else {
                TrySelectItem(itemindex);
            }
        }
        key_rec = qtrue;
        break;
    case K_PGUP:
        if (GetScrollBar()) {
            if (getCurrentItem() - 1 == GetScrollBar()->getTopItem()) {
                offsetitem = -GetScrollBar()->getPageHeight();
            } else {
                TrySelectItem(GetScrollBar()->getTopItem() + 1);
            }
        }
        key_rec = qtrue;
        break;
    case K_HOME:
        TrySelectItem(1);
        key_rec = qtrue;
        break;
    case K_END:
        TrySelectItem(getNumItems());
        key_rec = qtrue;
        break;
    case K_MWHEELDOWN:
        if (GetScrollBar()) {
            GetScrollBar()->AttemptScrollTo(GetScrollBar()->getTopItem() + 2);
        }
        key_rec = qtrue;
        break;
    case K_MWHEELUP:
        if (GetScrollBar()) {
            GetScrollBar()->AttemptScrollTo(GetScrollBar()->getTopItem() - 2);
        }
        key_rec = qtrue;
        break;
    default:
        break;
    }

    if (offsetitem) {
        TrySelectItem(getCurrentItem() + offsetitem);
    }

    return key_rec;
}

void UIListBase::FrameInitialized(void)
{
    if (m_vertscroll) {
        delete m_vertscroll;
    }

    m_vertscroll = new UIVertScroll();
    m_vertscroll->InitFrame(this, m_frame.size.width - 16.0, 0.0, 16.0, m_frame.size.height, -1);
    m_vertscroll->setTopItem(0);
}

int UIListBase::getCurrentItem(void)
{
    return m_currentItem;
}

int UIListBase::getNumItems(void)
{
    return -1;
}

void UIListBase::DeleteAllItems(void) {}

void UIListBase::DeleteItem(int which) {}

UIVertScroll *UIListBase::GetScrollBar(void)
{
    return m_vertscroll;
}

void UIListBase::SetUseScrollBar(qboolean bUse)
{
    m_bUseVertScroll = bUse;

    if (bUse) {
        if (!m_vertscroll) {
            m_vertscroll = new UIVertScroll();
        }

        m_vertscroll->InitFrame(this, m_frame.size.width - 16.0, 0.0, 16.0, m_frame.size.height, -1);
    } else {
        if (m_vertscroll) {
            delete m_vertscroll;
            m_vertscroll = NULL;
        }
    }
}

ListItem::ListItem()
{
    index = -1;
}

ListItem::ListItem(str string, int index, str command)
{
    this->string  = string;
    this->index   = index;
    this->command = command;
}

Event EV_UIListBase_ItemSelected
(
    "listbase_item_selected",
    EV_DEFAULT,
    "i",
    "index",
    "Signaled when an item is selected"
);
Event EV_UIListBase_ItemDoubleClicked
(
    "listbase_item_doubleclicked",
    EV_DEFAULT,
    "i",
    "index",
    "Signaled when an item is double clicked"

);
Event EV_Layout_AddListItem
(
    "additem",
    EV_DEFAULT,
    "sS",
    "itemname command",
    "Add an item to the list"
);
Event EV_Layout_AddConfigstringListItem
(
    "addconfigstringitem",
    EV_DEFAULT,
    "iS",
    "index command",
    "Add an item to the list that uses a configstring"

);
Event EV_UIListBox_DeleteAllItems
(
    "deleteallitems",
    EV_DEFAULT,
    NULL,
    NULL,
    "Delete all the items from the widget"
);

CLASS_DECLARATION(UIListBase, UIListBox, NULL) {
    {&W_LeftMouseDown,                   &UIListBox::MousePressed                 },
    {&W_LeftMouseUp,                     &UIListBox::MouseReleased                },
    {&EV_Layout_AddListItem,             &UIListBox::LayoutAddListItem            },
    {&EV_Layout_AddConfigstringListItem, &UIListBox::LayoutAddConfigstringListItem},
    {&EV_UIListBox_DeleteAllItems,       &UIListBox::DeleteAllItems               },
    {&EV_Layout_Font,                    &UIListBox::SetListFont                  },
    {NULL,                               NULL                                     }
};

UIListBox::UIListBox()
{
    m_clickState.point.x = 0.0;
    m_clickState.point.y = 0.0;
}

void UIListBox::Draw(void)
{
    float       aty;
    int         i;
    const char *str;
    //UColor      selectedBG(0, 1, 1, 1);
    //UColor      selectedColor  = UBlack;
    //UColor      selectedBorder = UWhite;
    // Changed in 2.0
    UColor      selectedBG(0.2, 0.18, 0.015);
    UColor      selectedColor(0.9, 0.8, 0.6);
    UColor      selectedBorder(0.315, 0.27, 0.0225);

    aty = 0;

    if (m_vertscroll) {
        i = m_vertscroll->getTopItem() + 1;
    } else {
        i = 1;
    }

    while (1) {
        ListItem *li;

        if (aty >= m_frame.size.height || i > m_itemlist.NumObjects()) {
            break;
        }

        if (i == m_currentItem) {
            DrawBox(
                0,
                aty * m_vVirtualScale[1],
                m_frame.size.width - m_vVirtualScale[0] * 16,
                m_font->getHeight(getVirtualScale()),
                selectedBG,
                1.f
            );
            m_font->setColor(selectedColor);
        } else {
            m_font->setColor(m_foreground_color);
        }

        li = m_itemlist.ObjectAt(i);
        if (li->index > 0) {
            str = Sys_LV_CL_ConvertString(uii.GetConfigstring(li->index));
        } else {
            str = li->string;
        }
        m_font->Print(m_indent, aty, str, -1, m_bVirtual ? m_vVirtualScale : NULL);

        if (i == m_currentItem) {
            DrawBoxWithSolidBorder(
                UIRect2D(
                    0,
                    aty * m_vVirtualScale[1],
                    m_frame.size.width - m_vVirtualScale[0] * 16,
                    m_font->getHeight(getVirtualScale())
                ),
                UWhite,
                selectedBorder,
                1,
                2,
                0.5f
            );
        }

        aty += m_font->getHeight(getHighResScale());
        i++;
    }
}

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

    p.x = ev->GetFloat(1) - m_screenframe.pos.x;
    p.y = ev->GetFloat(2) - m_screenframe.pos.y;

    if (m_vertscroll) {
        TrySelectItem((m_vertscroll->getTopItem() + 1) + p.y / m_font->getHeight(getVirtualScale()));
    } else {
        TrySelectItem(p.y / m_font->getHeight(getVirtualScale()) + 1);
    }

    if (m_clickState.time + 500 > uid.time && m_currentItem == m_clickState.selected) {
        UIPoint2D p2;

        p2.x = m_clickState.point.x - p.x;
        if (fabs(p2.x <= 2)) {
            p2.y = m_clickState.point.y - p.y;
            if (fabs(p2.y) <= 2.f) {
                Event event(EV_UIListBase_ItemDoubleClicked);
                event.AddInteger(m_currentItem);
                SendSignal(event);

                ListItem *li = m_itemlist.ObjectAt(m_currentItem);

                if (!m_currentItem || !li) {
                    return;
                }

                if (m_commandhandler) {
                    (*m_commandhandler)(li->string, NULL);
                }

                if (li->command.length()) {
                    uii.Cmd_Stuff(va("%s \"%s\"\n", li->command.c_str(), li->string.c_str()));
                } else {
                    uii.Cmd_Stuff(va("%s \"%s\"\n", m_command.c_str(), li->string.c_str()));
                }
                m_clickState.time = 0;
                return;
            }
        }
    }

    m_clickState.time     = uid.time;
    m_clickState.selected = m_currentItem;
    m_clickState.point    = p;
}

void UIListBox::MouseReleased(Event *ev) {}

void UIListBox::DeleteAllItems(Event *ev)
{
    m_itemlist.ClearObjectList();
    m_currentItem = 0;

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

void UIListBox::SetListFont(Event *ev)
{
    LayoutFont(ev);

    if (m_vertscroll) {
        m_vertscroll->setPageHeight(m_frame.size.height / m_font->getHeight(getVirtualScale()));
    }

    FrameInitialized();
}

void UIListBox::TrySelectItem(int which)
{
    UIListBase::TrySelectItem(which);

    if (!getNumItems()) {
        return;
    }

    if (m_cvarname.length()) {
        uii.Cvar_Set(m_cvarname.c_str(), m_itemlist.ObjectAt(m_currentItem)->string.c_str());
    }
}

void UIListBox::AddItem(const char *item, const char *command)
{
    ListItem *li;

    li = new ListItem();
    if (!li) {
        uii.Sys_Error(ERR_DROP, "Couldn't create list item\n");
    }

    li->string = item;
    if (command) {
        li->command = command;
    }

    m_itemlist.AddObject(li);

    if (!m_currentItem) {
        m_currentItem = 1;
    }

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

void UIListBox::AddItem(int index, const char *command)
{
    ListItem *li;

    li = new ListItem();
    if (!li) {
        uii.Sys_Error(ERR_DROP, "Couldn't create list item\n");
    }

    li->index = index;
    if (command) {
        li->command = command;
    }

    m_itemlist.AddObject(li);

    if (!m_currentItem) {
        m_currentItem = 1;
    }

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

void UIListBox::FrameInitialized(void)
{
    UIListBase::FrameInitialized();

    if (m_vertscroll) {
        m_vertscroll->setPageHeight(m_frame.size.height / m_font->getHeight(getVirtualScale()));
        m_vertscroll->setNumItems(m_itemlist.NumObjects());
    }
}

void UIListBox::LayoutAddListItem(Event *ev)
{
    if (ev->NumArgs() != 2) {
        AddItem(ev->GetString(1), NULL);
        return;
    }

    AddItem(ev->GetString(1), ev->GetString(2).c_str());
}

void UIListBox::LayoutAddConfigstringListItem(Event *ev)
{
    if (ev->NumArgs() != 2) {
        AddItem(ev->GetInteger(1), NULL);
        return;
    }

    AddItem(ev->GetInteger(1), ev->GetString(2).c_str());
}

str UIListBox::getItemText(int which)
{
    return m_itemlist.ObjectAt(which)->string;
}

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

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

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

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

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

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

void UIListBox::InsertItem(const char *string, int which, const char *command)
{
    ListItem *item;
    int       i;

    item = new ListItem();
    if (!item) {
        Com_Error(ERR_DROP, "Couldn't create list item\n");
    }

    item->string = string;
    if (command) {
        item->command = command;
    }

    if (which >= 1 && which <= m_itemlist.NumObjects()) {
        m_itemlist.AddObject({});
        for (i = m_itemlist.NumObjects(); i > which; i--) {
            m_itemlist.SetObjectAt(i, m_itemlist.ObjectAt(i - 1));
        }

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

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

void UIListBox::setCurrentItem(int which)
{
    if (which < 1) {
        m_currentItem = 0;
    } else if (which > m_itemlist.NumObjects()) {
        m_currentItem = m_itemlist.NumObjects();
    } else {
        m_currentItem = which;
    }
}
