/*
===========================================================================
Copyright (C) 2023 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 "cl_ui.h"
#include "../qcommon/tiki.h"

static inventory_t            s_processed_inv;
static SafePtr<FakkInventory> s_main_inv;

Event FakkInventory_Timeout("_timeout", EV_DEFAULT, NULL, NULL, "Timeout of the menu from inactivity");

CLASS_DECLARATION(UIWidget, FakkInventory, NULL) {
    {&W_MouseMoved,          &FakkInventory::OnMouseMove },
    {&W_MouseEntered,        &FakkInventory::OnMouseEnter},
    {&W_MouseExited,         &FakkInventory::OnMouseLeave},
    {&W_LeftMouseDown,       &FakkInventory::OnMouseDown },
    {&W_RightMouseDown,      &FakkInventory::OnMouseDown },
    {&FakkInventory_Timeout, &FakkInventory::Timeout     },
    {NULL,                   NULL                        }
};

CLASS_DECLARATION(UIWidget, FakkItemList, NULL) {
    {&W_LeftMouseDown,  &FakkItemList::OnLeftMouseDown },
    {&W_RightMouseDown, &FakkItemList::OnRightMouseDown},
    {&W_LeftMouseUp,    &FakkItemList::OnLeftMouseUp   },
    {&W_RightMouseUp,   &FakkItemList::OnRightMouseUp  },
    {&W_MouseMoved,     &FakkItemList::OnMouseMove     },
    {&W_MouseEntered,   &FakkItemList::OnMouseEnter    },
    {&W_MouseExited,    &FakkItemList::OnMouseLeave    },
    {&W_Destroyed,      &FakkItemList::OnMenuKilled    },
    {NULL,              NULL                           }
};

void CL_Draw3DModel(
    float     x,
    float     y,
    float     w,
    float     h,
    qhandle_t model,
    vec3_t    origin,
    vec3_t    rotateoffset,
    vec3_t    offset,
    vec3_t    angle,
    vec3_t    color,
    str       anim
)
{
    refdef_t    inv_refdef {};
    refEntity_t ent {};
    vec3_t      unprojoffset;
    vec3_t      entaxis[3];

    inv_refdef.fov_x   = 30;
    inv_refdef.fov_y   = 30;
    inv_refdef.x       = x;
    inv_refdef.y       = y;
    inv_refdef.width   = w;
    inv_refdef.rdflags = RDF_HUD | RDF_NOWORLDMODEL;
    inv_refdef.height  = h;
    inv_refdef.time    = cl.serverTime;
    re.ClearScene();

    AnglesToAxis(angle, entaxis);

    MatrixTransformVector(rotateoffset, entaxis, unprojoffset);

    ent.scale        = 1.0;
    ent.hModel       = model;
    ent.entityNumber = 1023;
    VectorAdd(origin, offset, ent.origin);
    VectorAdd(ent.origin, unprojoffset, ent.origin);

    ent.tiki = re.R_Model_GetHandle(model);
    if (!ent.tiki) {
        return;
    }

    if (anim.length()) {
        static qboolean bInited = false;
        static int      hModel[8];
        static int      iUpdateTime[8];
        static int      iIndex[8];
        static float    fAnimTime[8];
        static char     szAnimName[8][256];
        int             i;
        int             iCurrNum;

        if (!bInited) {
            for (i = 0; i < ARRAY_LEN(hModel); i++) {
                iUpdateTime[i] = 0;
                iIndex[i]      = -1;
                hModel[i]      = -1;
            }
            bInited = true;
        }

        for (i = 0; i < ARRAY_LEN(hModel); i++) {
            if (hModel[i] == model) {
                break;
            }
        }

        if (i == ARRAY_LEN(hModel)) {
            iCurrNum = 0;

            // Find a free slot
            for (i = 0; i < ARRAY_LEN(hModel); i++) {
                if (hModel[i] == -1) {
                    iCurrNum = i;
                    break;
                }
            }

            if (i == ARRAY_LEN(hModel)) {
                int iOldestNum, iOldestTime;

                iOldestTime = iUpdateTime[0];
                iOldestNum  = 0;

                for (i = 1; i < ARRAY_LEN(hModel); i++) {
                    if (iUpdateTime[i] < iOldestTime) {
                        iOldestNum  = i;
                        iOldestTime = iUpdateTime[i];
                    }
                }

                iCurrNum = iOldestNum;
            }

            hModel[iCurrNum] = model;
            iIndex[iCurrNum] = -1;
            Q_strncpyz(szAnimName[iCurrNum], anim, sizeof(szAnimName[iCurrNum]));
        } else {
            iCurrNum = i;

            if (!Q_stricmp(szAnimName[iCurrNum], anim)) {
                fAnimTime[iCurrNum] += (cls.realtime - iUpdateTime[i]) * 0.001f;
                iUpdateTime[iCurrNum] = cls.realtime;

                if (TIKI_Anim_Time(ent.tiki, iIndex[iCurrNum]) < fAnimTime[iCurrNum]) {
                    // Added in 2.0
                    //  Clear tunak easter egg
                    Cvar_Set("tunak", "0");
                    iIndex[iCurrNum] = -1;
                }
            } else {
                iIndex[iCurrNum] = -1;
                Q_strncpyz(szAnimName[iCurrNum], anim, sizeof(szAnimName[iCurrNum]));
            }
        }

        if (iIndex[iCurrNum] == -1) {
            iIndex[iCurrNum]      = TIKI_Anim_NumForName(ent.tiki, szAnimName[iCurrNum]);
            fAnimTime[iCurrNum]   = 0;
            iUpdateTime[iCurrNum] = cls.realtime;
        }

        ent.frameInfo[0].index  = iIndex[iCurrNum];
        ent.frameInfo[0].time   = fAnimTime[iCurrNum];
        ent.frameInfo[0].weight = 1.f;
        ent.actionWeight        = 1.f;
    }

    ent.shaderRGBA[0] = color[0] * 255;
    ent.shaderRGBA[1] = color[1] * 255;
    ent.shaderRGBA[2] = color[2] * 255;
    ent.shaderRGBA[3] = color[3] * 255;

    if (cge) {
        vec3_t vViewAngles;

        cge->CG_EyePosition(&inv_refdef.vieworg);
        cge->CG_EyeAngles(&vViewAngles);
        AnglesToAxis(vViewAngles, inv_refdef.viewaxis);

        VectorCopy(ent.origin, unprojoffset);
        MatrixTransformVector(unprojoffset, inv_refdef.viewaxis, ent.origin);
        MatrixMultiply(entaxis, inv_refdef.viewaxis, ent.axis);
        VectorAdd(ent.origin, inv_refdef.vieworg, ent.origin);
    } else {
        AnglesToAxis(angle, ent.axis);
        AxisClear(inv_refdef.viewaxis);
    }

    re.AddRefEntityToScene(&ent, ENTITYNUM_NONE);
    re.RenderScene(&inv_refdef);
}

bool CL_DoIHaveThisItem(const char *name)
{
    return true;
}

static void ProcessType(inventory_type_t *type)
{
    for (int i = 1; i < type->items.NumObjects(); i++) {
        const inventory_item_t *item = type->items.ObjectAt(i);

        if (!CL_DoIHaveThisItem(item->name)) {
            type->items.RemoveObjectAt(i--);
        }
    }
}

static void ProcessInventory()
{
    for (int i = 1; i <= s_processed_inv.types.NumObjects(); i++) {
        inventory_type_t *type = s_processed_inv.types.ObjectAt(i);

        ProcessType(type);
        if (!type->items.NumObjects()) {
            delete type;
            s_processed_inv.types.RemoveObjectAt(i--);
        }
    }
}

void UI_NextInventory()
{
    UI_DoInventory(false);

    if (s_main_inv) {
        s_main_inv->WarpTo(1);
        s_main_inv->NextItem();
    }
}

void UI_PrevInventory()
{
    UI_DoInventory(false);

    if (s_main_inv) {
        s_main_inv->WarpTo(1);
        s_main_inv->PrevItem();
    }
}

void UI_WarpInventory()
{
    UI_DoInventory(0);

    if (Cmd_Argc() != 2) {
        Com_Printf("Usage: warpinv <slot_name>");
    }

    if (s_main_inv) {
        s_main_inv->WarpTo(Cmd_Argv(1));
    }
}

void UI_DoInventory_f()
{
    UI_DoInventory(1);

    if (s_main_inv) {
        s_main_inv->WarpTo(1);
    }
}

void UI_DoInventory(qboolean activate_mouse)
{
    if ((cl.snap.ps.stats[STAT_CINEMATIC] & 1) || s_main_inv) {
        return;
    }

    ProcessInventory();

    if (!s_processed_inv.types.NumObjects()) {
        UI_UpdateCenterPrint("- No inventory -", 1);
        return;
    }

    s_main_inv = new FakkInventory();
    s_main_inv->setInventory(&s_processed_inv);

    if (client_inv.align == INV_ALIGN_RIGHT) {
        s_main_inv->InitFrame(
            NULL,
            client_inv.horizoffset + uid.vidWidth - client_inv.typewidth,
            client_inv.vertoffset,
            client_inv.typewidth,
            client_inv.typeheight * client_inv.types.NumObjects(),
            0
        );
    } else if (client_inv.align == INV_ALIGN_LEFT) {
        s_main_inv->InitFrame(
            NULL,
            client_inv.horizoffset,
            client_inv.vertoffset,
            client_inv.typewidth,
            client_inv.typeheight * client_inv.types.NumObjects(),
            0
        );
    }

    uWinMan.ActivateControl(s_main_inv);

    if (activate_mouse) {
        IN_MouseOn();
    }

    s_main_inv->m_mouse_active = true;
}

qboolean UI_CloseInventory()
{
    menuManager.PassEventToAllWidgets(EV_ClearInvItemReference);

    if (!s_main_inv || s_main_inv->isDying()) {
        return false;
    }

    delete s_main_inv;

    UI_FocusMenuIfExists();
    return true;
}

FakkInventory::FakkInventory()
{
    m_currenttype = NULL;
    AllowActivate(false);
    UIWidget::setBackgroundColor(UColor(), true);
}

FakkInventory::~FakkInventory()
{
    if (!uWinMan.IsDead()) {
        VerifyTypeUp(NULL, false);
    }
}

int FakkInventory::FindFirstItem(int itemindex, int sign)
{
    int i;

    for (i = itemindex; i > 0 && i <= m_currenttype->items.NumObjects(); i += sign) {
        const inventory_item_t *item = m_currenttype->items.ObjectAt(i);

        if (CL_HasInventoryItem(item->name)) {
            break;
        }
    }

    return i;
}

void FakkInventory::ChangeItem(int sign)
{
    int               itemindex;
    int               groupindex;
    inventory_item_t *selitem;

    if (!m_currenttype && !m_currentlist) {
        WarpTo(1);
    }

    itemindex = m_currenttype->IndexOfItemPtr(m_currentlist->getHoverItem());
    if (itemindex) {
        itemindex = FindFirstItem(itemindex + sign, sign);
        if (itemindex <= 0 || itemindex > m_currenttype->items.NumObjects()) {
            groupindex = m_inv->types.IndexOfObject(m_currenttype);
            if (groupindex) {
                groupindex += sign;
                if (groupindex > m_inv->types.NumObjects()) {
                    groupindex = 1;
                } else if (groupindex < 1) {
                    groupindex = m_inv->types.NumObjects();
                }

                VerifyTypeUp(m_inv->types.ObjectAt(groupindex), false);

                if (sign > 0) {
                    itemindex = FindFirstItem(1, sign);
                } else {
                    itemindex = FindFirstItem(m_inv->types.ObjectAt(groupindex)->items.NumObjects(), sign);
                }

                if (itemindex > m_inv->types.ObjectAt(groupindex)->items.NumObjects()) {
                    itemindex = 1;
                }
            } else {
                VerifyTypeUp(m_inv->types.ObjectAt(1), false);
                itemindex = FindFirstItem(1, sign);
            }
        }
    } else {
        itemindex = 1;
    }

    selitem = m_currenttype->items.ObjectAt(itemindex);
    return m_currentlist->VerifyItemUp(selitem, false);
}

void FakkInventory::NextItem(void)
{
    ChangeItem(1);
}

void FakkInventory::PrevItem(void)
{
    ChangeItem(-1);
}

void FakkInventory::Timeout(Event *ev)
{
    UI_CloseInventory();
}

void FakkInventory::PlaySound(invsound_t type)
{
    switch (type) {
    case invsound_t::selectsound:
        S_StartLocalSound(m_inv->selectsound, qfalse);
        break;
    case invsound_t::rejectsound:
        S_StartLocalSound(m_inv->rejectsound, qfalse);
        break;
    case invsound_t::changesound:
        S_StartLocalSound(m_inv->changesound, qfalse);
        break;
    default:
        break;
    }
}

void FakkInventory::Draw(void)
{
    int       i;
    bool      mouse_in;
    UIPoint2D clientPoint;

    if (!m_inv) {
        return;
    }

    clientPoint = UIPoint2D(uid.mouseX, uid.mouseY);
    mouse_in    = uWinMan.FindResponder(clientPoint) == this;
    clientPoint = getLocalPoint(clientPoint);

    for (i = 1; i <= m_inv->types.NumObjects(); i++) {
        inventory_type_t *type;
        float             top;
        UIReggedMaterial *torender;

        type = m_inv->types.ObjectAt(i);
        top  = m_inv->typeheight * (i - 1);
        if (type == m_currenttype) {
            if (mouse_in) {
                torender = type->hoverTexture;
            } else {
                torender = type->selTexture;
            }
        } else {
            torender = type->texture;
        }

        if (torender) {
            uii.Rend_DrawPicStretched(
                0, top, m_inv->typewidth, m_inv->typeheight, 0, 0, 1.f, 1.f, torender->GetMaterial()
            );
        }
    }
}

void FakkInventory::OnMouseDown(Event *ev)
{
    inventory_type_t *oldtype;
    UIPoint2D         p;
    int               i;

    p = MouseEventToClientPoint(ev);
    i = (int)(p.y / m_inv->typeheight);

    if (i + 1 > m_inv->types.NumObjects()) {
        return;
    }

    oldtype = m_currenttype;
    VerifyTypeUp(m_inv->types.ObjectAt(i + 1), true);

    if (m_currentlist && m_currenttype == oldtype) {
        m_currentlist->VerifyItemUp(m_currenttype->items.ObjectAt(1), true);
    }
}

void FakkInventory::OnMouseMove(Event *ev) {}

void FakkInventory::OnMouseLeave(Event *ev) {}

void FakkInventory::OnMouseEnter(Event *ev)
{
    SetHovermaterialActive(true);
}

void FakkInventory::VerifyTypeUp(inventory_type_t *t, qboolean warpmouse)
{
    CancelEventsOfType(FakkInventory_Timeout);
    PostEvent(new Event(FakkInventory_Timeout), 5);

    if (m_currenttype != t) {
        if (m_currenttype) {
            delete m_currenttype;
        }

        m_currenttype = t;
        if (t) {
            m_currentlist = new FakkItemList();
            m_currentlist->setType(m_currenttype);

            switch (m_inv->cascade) {
            case INV_CASCADE_LEFT:
                m_currentlist->Create(m_screenframe.pos.x, m_screenframe.pos.y, this);
                break;
            case INV_CASCADE_RIGHT:
                m_currentlist->Create(m_screenframe.pos.x + m_screenframe.size.width, m_screenframe.pos.y, this);
                break;
            default:
                break;
            }
        }

        if (m_currenttype) {
            PlaySound(invsound_t::changesound);
        }
    }

    if (warpmouse && m_currentlist) {
        int i;
        int height = 0;

        for (i = 1; i <= m_inv->types.NumObjects() && m_inv->types.ObjectAt(i) != t; i++) {
            height += m_inv->typeheight;
        }

        CL_SetMousePos(
            (int)((m_inv->typewidth / 2) + m_screenframe.pos.x),
            (int)((m_inv->typeheight / 2 + height) + m_screenframe.pos.y)
        );
    }
}

void FakkInventory::WarpTo(int slotnum)
{
    int i;

    for (i = slotnum; i > 0 && i <= m_inv->types.NumObjects(); i++) {
        inventory_type_t *t = m_inv->types.ObjectAt(i);
        if (t) {
            VerifyTypeUp(t, true);
        }
    }
}

void FakkInventory::WarpTo(const char *name)
{
    int i;
    int count;

    count = m_inv->types.NumObjects();
    for (i = 1; i <= count; i++) {
        inventory_type_t *t = m_inv->types.ObjectAt(i);

        if (name && str::icmp(t->name, name)) {
            continue;
        }

        VerifyTypeUp(t, true);
        if (name && m_currentlist && m_currenttype) {
            m_currentlist->VerifyItemUp(m_currenttype->items.ObjectAt(1), true);
        }
    }
}

void FakkInventory::setInventory(inventory_t *i)
{
    m_inv = i;
}

qboolean FakkInventory::KeyEvent(int key, unsigned int time)
{
    char        bind[1024];
    const char *cmd;
    const char *name;
    int         index;
    bool        actionitemlist;
    str         command;

    if (cl.snap.ps.stats[STAT_CINEMATIC] & 1) {
        UI_CloseInventory();
        return false;
    }

    if (!m_inv->types.NumObjects()) {
        return true;
    }

    actionitemlist = m_currenttype && m_currentlist && m_currentlist->getHoverItem() != NULL;

    cmd = Key_GetBinding(key);
    if (cmd) {
        Q_strncpyz(bind, cmd, sizeof(bind));
    } else {
        bind[0] = 0;
    }

    if (bind[0]) {
        name = strtok(bind, " ");
        if (!str::icmp(name, "+attackleft")) {
            command = "attackleft";
        } else if (!str::icmp(name, "+attackright")) {
            command = "attackright";
        } else if (!str::icmp(name, "warpinv")) {
            name = strtok(NULL, " ");
        } else {
            if (!str::icmp(name, "inventory")) {
                UI_CloseInventory();
                return qtrue;
            } else if (!str::icmp(name, "nextinv")) {
                NextItem();
                return true;
            } else if (!str::icmp(name, "previnv")) {
                PrevItem();
                return true;
            }
            name = NULL;
        }
    }

    if (actionitemlist) {
        if (m_mouse_active && (key == K_UPARROW || key == K_DOWNARROW) || (key == K_MWHEELUP || key == K_MWHEELDOWN)
            || (name && !str::icmp(name, m_currenttype->name))) {
            inventory_item_t *hoverItem = m_currentlist->getHoverItem();

            if (m_currenttype->items.NumObjects()) {
                index = m_currenttype->IndexOfItemPtr(hoverItem);

                if (!index) {
                    index = 1;
                } else if (key == K_UPARROW || key == K_MWHEELUP) {
                    index--;
                    if (index < 1) {
                        index = m_currenttype->items.NumObjects();
                    }
                } else {
                    index++;
                    if (index > m_currenttype->items.NumObjects()) {
                        index = 1;
                    }
                }

                m_currentlist->VerifyItemUp(m_currenttype->items.ObjectAt(index), true);
                return true;
            }
        } else {
            if (command.length()) {
                if (!str::icmp(command, "attackleft")) {
                    if (m_currentlist) {
                        m_currentlist->OnLeftMouseDown(NULL);
                    }
                } else if (!str::icmp(command, "attackright")) {
                    if (m_currentlist) {
                        m_currentlist->OnRightMouseDown(NULL);
                    }
                }

                PostponeEvent(FakkInventory_Timeout, 5.f);
                return true;
            }

            if (name) {
                WarpTo(name);
                PostponeEvent(FakkInventory_Timeout, 5.f);
                return true;
            }

            CL_Key_ForceCommand(key, time);
            return true;
        }
    }

    if (m_mouse_active && (key == K_UPARROW || key == K_DOWNARROW) || (key == K_MWHEELUP || key == K_MWHEELDOWN)) {
        index = m_inv->types.IndexOfObject(m_currenttype);
        if (!index) {
            index = 1;
        } else if (key == K_UPARROW || key == K_MWHEELUP) {
            index--;
            if (index < 1) {
                index = m_inv->types.NumObjects();
            }
        } else {
            index++;
            if (index > m_inv->types.NumObjects()) {
                index = 1;
            }
        }

        VerifyTypeUp(m_inv->types.ObjectAt(index), true);
        return qtrue;
    }

    if (m_mouse_active && (key == K_RIGHTARROW || key == K_LEFTARROW)) {
        if (!m_currenttype || !m_currentlist) {
            return qtrue;
        }

        if ((m_inv->cascade == INV_CASCADE_LEFT && key == K_LEFTARROW)
            || (m_inv->cascade == INV_CASCADE_RIGHT && key == K_RIGHTARROW)) {
            m_currentlist->VerifyItemUp(m_currenttype->items.ObjectAt(1), true);
            return qtrue;
        }

        PostponeEvent(FakkInventory_Timeout, 5.f);
        return true;
    }

    if (!name) {
        PostponeEvent(FakkInventory_Timeout, 5.f);
        return true;
    }

    WarpTo(name);
    PostponeEvent(FakkInventory_Timeout, 5.f);
    return true;
}

qboolean FakkInventory::isDying(void)
{
    return m_dying;
}

FakkItemList::FakkItemList()
{
    m_hoveritem    = NULL;
    m_lastmenuitem = NULL;
    m_parent       = NULL;
    UIWidget::setBackgroundColor(UColor(), true);
}

FakkItemList::~FakkItemList() {}

void FakkItemList::Create(float x, float y, FakkInventory *parent)
{
    UIRect2D rect;
    int      i;
    float    maxWidth = 0;

    for (i = 1; i <= type->items.NumObjects(); i++) {
        inventory_item_t *item = type->items.ObjectAt(i);
        if (maxWidth <= item->width) {
            maxWidth = item->width;
        }

        rect.size.height += item->height;
    }

    if (client_inv.cascade == INV_CASCADE_RIGHT) {
        rect.pos.x      = x;
        rect.pos.y      = y;
        rect.size.width = maxWidth;

        InitFrame(NULL, rect, 0);
    } else if (client_inv.cascade == INV_CASCADE_LEFT) {
        rect.pos.x      = x - maxWidth;
        rect.pos.y      = y;
        rect.size.width = maxWidth;

        InitFrame(NULL, rect, 0);
    }

    Com_DPrintf("FakkItemList::Create : Invalid cascade for inventory list\n");
    if (parent) {
        m_parent = parent;
    }
}

bool FakkItemList::HasAnyItems(void)
{
    int i;

    for (i = 1; i <= type->items.NumObjects(); i++) {
        inventory_item_t *item = type->items.ObjectAt(i);

        if (CL_HasInventoryItem(item->name)) {
            return true;
        }
    }

    return false;
}

void FakkItemList::Draw(void) {}

void FakkItemList::OnMouseEnter(Event *ev)
{
    SetHovermaterialActive(true);
}

void FakkItemList::OnLeftMouseDown(Event *ev)
{
    char        bind[1024];
    const char *cmd;
    const char *name;

    if ((cl.snap.ps.stats[STAT_CINEMATIC] & 1)) {
        return;
    }

    if (m_parent) {
        m_parent->CancelEventsOfType(FakkInventory_Timeout);
        m_parent->PostEvent(new Event(FakkInventory_Timeout), 5);
    }

    if (ev) {
        cmd = Key_GetBinding(K_MOUSE1);
        if (cmd) {
            Q_strncpyz(bind, cmd, sizeof(bind));
        } else {
            bind[0] = 0;
        }

        if (bind[0]) {
            name = strtok(bind, " ");
            if (str::icmp(name, "+attackleft")) {
                if (!str::icmp(name, "+attackright")) {
                    OnRightMouseDown(NULL);
                }
                return;
            }
        }
    }

    if (!CL_HasInventoryItem(m_lastmenuitem->name) || (m_lastmenuitem->equip & 16)) {
        if (m_parent) {
            m_parent->PlaySound(invsound_t::rejectsound);
        }
        return;
    }

    if (m_lastmenuitem->checkammo) {
        int count;
        int max;

        CL_AmmoCount(m_lastmenuitem->ammoname, &count, &max);

        if (!count) {
            if (m_parent) {
                m_parent->PlaySound(invsound_t::rejectsound);
            }
            return;
        }
    }

    if (m_lastmenuitem->command.length()) {
        uii.Cmd_Stuff(va("%s\n", m_lastmenuitem->command.c_str()));
    } else if (m_lastmenuitem->equip & 4) {
        uii.Cmd_Stuff(va("use \"%s\" dualhand\n", m_lastmenuitem->name.c_str()));
    } else if (m_lastmenuitem->equip & 1) {
        uii.Cmd_Stuff(va("use \"%s\" lefthand\n", m_lastmenuitem->name.c_str()));
    } else if (m_lastmenuitem->equip & 8) {
        uii.Cmd_Stuff(va("use \"%s\"\n", m_lastmenuitem->name.c_str()));
    } else {
        if (m_parent) {
            m_parent->PlaySound(invsound_t::rejectsound);
        }
    }

    if (m_parent) {
        m_parent->PlaySound(invsound_t::selectsound);
    }

    if (m_parent && !m_parent->m_mouse_active) {
        UI_CloseInventory();
    }
}

void FakkItemList::OnRightMouseDown(Event *ev)
{
    char        bind[1024];
    const char *cmd;
    const char *name;

    if ((cl.snap.ps.stats[STAT_CINEMATIC] & 1)) {
        return;
    }

    if (m_parent) {
        m_parent->CancelEventsOfType(FakkInventory_Timeout);
        m_parent->PostEvent(new Event(FakkInventory_Timeout), 5);
    }

    if (ev) {
        cmd = Key_GetBinding(K_MOUSE2);
        if (cmd) {
            Q_strncpyz(bind, cmd, sizeof(bind));
        } else {
            bind[0] = 0;
        }

        if (bind[0]) {
            name = strtok(bind, " ");
            if (str::icmp(name, "+attackright")) {
                if (!str::icmp(name, "+attackleft")) {
                    OnLeftMouseDown(NULL);
                }
                return;
            }
        }
    }

    if (!CL_HasInventoryItem(m_lastmenuitem->name) || (m_lastmenuitem->equip & 16)) {
        if (m_parent) {
            m_parent->PlaySound(invsound_t::rejectsound);
        }
        return;
    }

    if (m_lastmenuitem->checkammo) {
        int count;
        int max;

        CL_AmmoCount(m_lastmenuitem->ammoname, &count, &max);

        if (!count) {
            if (m_parent) {
                m_parent->PlaySound(invsound_t::rejectsound);
            }
            return;
        }
    }

    if (m_lastmenuitem->command.length()) {
        uii.Cmd_Stuff(va("%s\n", m_lastmenuitem->command.c_str()));
    } else if (m_lastmenuitem->equip & 4) {
        uii.Cmd_Stuff(va("use \"%s\" dualhand\n", m_lastmenuitem->name.c_str()));
    } else if (m_lastmenuitem->equip & 1) {
        uii.Cmd_Stuff(va("use \"%s\" righthand\n", m_lastmenuitem->name.c_str()));
    } else if (m_lastmenuitem->equip & 8) {
        uii.Cmd_Stuff(va("use \"%s\"\n", m_lastmenuitem->name.c_str()));
    } else {
        if (m_parent) {
            m_parent->PlaySound(invsound_t::rejectsound);
        }
    }

    if (m_parent) {
        m_parent->PlaySound(invsound_t::selectsound);
    }

    if (m_parent && !m_parent->m_mouse_active) {
        UI_CloseInventory();
    }
}

void FakkItemList::OnLeftMouseUp(Event *ev) {}

void FakkItemList::OnRightMouseUp(Event *ev) {}

void FakkItemList::OnMouseLeave(Event *ev) {}

void FakkItemList::VerifyItemUp(inventory_item_t *item, qboolean warpmouse)
{
    int               i;
    int               height = 0;
    inventory_item_t *ii     = NULL;

    if (m_parent) {
        m_parent->CancelEventsOfType(FakkInventory_Timeout);
        m_parent->PostEvent(new Event(FakkInventory_Timeout), 5);
    }

    m_hoveritem    = item;
    m_lastmenuitem = item;

    if (m_parent) {
        m_parent->PlaySound(changesound);
    }

    if (warpmouse) {
        for (i = 1; i <= type->items.NumObjects(); i++) {
            ii = type->items.ObjectAt(i);
            if (ii == item) {
                break;
            }

            height += ii->height;
        }

        if (ii) {
            CL_SetMousePos(
                (int)((ii->width / 2) + m_screenframe.pos.x), (int)((ii->height / 2 + height) + m_screenframe.pos.y)
            );
        }
    }
}

void FakkItemList::OnMouseMove(Event *ev)
{
    int               i;
    float             top;
    inventory_item_t *newitem;
    UIPoint2D         p;

    top     = 0;
    newitem = NULL;
    p       = MouseEventToClientPoint(ev);

    for (i = 1; i <= type->items.NumObjects(); i++) {
        inventory_item_t *item = type->items.ObjectAt(i);
        if (p.x >= 0 && item->width > p.x && p.y >= top && item->height + top > p.y) {
            newitem = item;
            break;
        }

        top += item->height;
    }

    if (newitem != m_hoveritem) {
        VerifyItemUp(newitem, false);
    }
}

void FakkItemList::OnMenuKilled(Event *ev)
{
    VerifyItemUp(NULL, false);
}

void FakkItemList::setType(inventory_type_t *t)
{
    type = t;
}

void FakkItemList::EquipItem(Event *ev) {}

inventory_item_t *FakkItemList::getHoverItem() const
{
    return m_hoveritem;
}
