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

Event W_Destroyed
(
    "widget_destroyed",
    EV_DEFAULT,
    NULL,
    NULL,
    "Specifies that the widget has been destroyed." 
);

Event W_SizeChanged
(
    "size_changed",
    EV_DEFAULT,
    NULL,
    NULL,
    "Specifies that the size of the widget has changed." 
);

Event W_FrameChanged
(
    "frame_changed",
    EV_DEFAULT,
    NULL,
    NULL,
    "Specifies that the frame has changed." 
);

Event W_OriginChanged
(
    "origin_changed",
    EV_DEFAULT,
    NULL,
    NULL,
    "Specifies that the origin f the widget has changed." 
);

Event W_Activated
(
    "activated",
    EV_DEFAULT,
    NULL,
    NULL,
    "Signals that the widget has been activated." 
);

Event EV_Widget_Activate
(
    "activate",
    EV_DEFAULT,
    NULL,
    NULL,
    "Activate this widget." 
);

Event W_Deactivated
(
    "deactivated",
    EV_DEFAULT,
    NULL,
    NULL,
    "Signals that the widget has been deactivated." 
);

Event EV_Widget_Hide
(
    "hide",
    EV_DEFAULT,
    NULL,
    NULL,
    "Hides the widget." 
);

Event EV_Widget_Enable
(
    "enable",
    EV_DEFAULT,
    NULL,
    NULL,
    "enable the widget." 
);

Event EV_Widget_Disable
(
    "disable",
    EV_DEFAULT,
    NULL,
    NULL,
    "enable the widget." 
);

Event W_RealignWidget
(
    "realign_widget",
    EV_DEFAULT,
    NULL,
    NULL,
    "Specifies that the widget has been realigned." 
);

Event W_Draw
(
    "draw_widget",
    EV_DEFAULT,
    NULL,
    NULL,
    "Draw the widget." 
);

Event EV_Layout_Size
(
    "size",
    EV_DEFAULT,
    "ffff",
    "x y width height",
    "Set the size of the widget" 
);

Event EV_Layout_Rect
(
    "rect",
    EV_DEFAULT,
    "ffff",
    "x y width height",
    "Set the rect of the widget" 
);

Event EV_Layout_Name
(
    "name",
    EV_DEFAULT,
    "s",
    "name",
    "Set the name of the widget"
);

Event EV_Layout_Title
(
    "title",
    EV_DEFAULT,
    "s",
    "title",
    "Set the title of the widget to be displayed in the center of it"
);

Event EV_Layout_Font
(
    "font",
    EV_DEFAULT,
    "s",
    "title",
    "Set the font of the widget"
);

Event EV_Layout_BGColor
(
    "bgcolor",
    EV_DEFAULT,
    "ffff",
    "red green blue alpha",
    "Set the background color of the widget"
);

Event EV_Layout_FGColor
(
    "fgcolor",
    EV_DEFAULT,
    "ffff",
    "red green blue alpha",
    "Set the foreground color of the widget"
);

Event EV_Layout_Borderstyle
(
    "borderstyle",
    EV_DEFAULT,
    "s",
    "style",
    "Set the borderstyle of the widget(NONE, 3D_BORDER, INDENT_BORDER) "
);

Event EV_Layout_BorderColor
(
    "bordercolor",
    EV_DEFAULT,
    "ffff",
    "red green blue alpha",
    "Set the border color of the widget"
);

Event EV_Layout_Shader
(
    "shader",
    EV_DEFAULT,
    "s",
    "shaderName",
    "Set the shader to draw on the background of the widget"
);

Event EV_Layout_TileShader
(
    "tileshader",
    EV_DEFAULT,
    "s",
    "shaderName",
    "Set the shader to draw tiled on the background of the widget"
);

Event EV_Layout_HoverShader
(
    "hovershader",
    EV_DEFAULT,
    "s",
    "shaderName",
    "Set the shader to on the background of the widget when the mouse is over the widget"
);

Event EV_Layout_PressedShader
(
    "pressedshader",
    EV_DEFAULT,
    "s",
    "shaderName",
    "Set the shader to on the background of the widget when it is pressed in"
);

Event EV_Layout_FadeIn
(
    "fadein",
    EV_DEFAULT,
    "f",
    "fadetime",
    "Set the amount of time it takes for the widget to fade in"
);

Event EV_Layout_StopSound
(
    "stopsound",
    EV_DEFAULT,
    "s",
    "soundName",
    "Set the sound to be played when the widget stops moving"
);

Event EV_Layout_ClickSound
(
    "clicksound",
    EV_DEFAULT,
    "s",
    "soundName",
    "Set the sound to be played when the widget is clicked"
);

Event EV_Layout_Stretch
(
    "stretch",
    EV_DEFAULT,
    "s",
    "stretchdir",
    "Set the widget to be stretched horizontally or vertically across the screen"
);

Event EV_Layout_VirtualRes
(
    "virtualres",
    EV_DEFAULT,
    "i",
    "usevirtualres",
    "Set the widget to be modified horizontally or vertically across the screen to account for resolutions != 640x480"
);

Event EV_Layout_InitData
(
    "initdata",
    EV_DEFAULT,
    "s",
    "datastring",
    "Set the linkcvar to be initialized to this value"
);

Event EV_Layout_Direction
(
    "direction",
    EV_DEFAULT,
    "sF",
    "direction time",
    "Set the direction the widget will appear from on the screen\n(from_top, from_bottom, from_left, or from_right)\nTime is specified to determine how fast the widget will scroll in\n"
);

Event EV_Layout_Align
(
    "align",
    EV_DEFAULT,
    "s",
    "alignment",
    "Set the side of the screen to align the widget(left right top bottom centerx centery)"
);

Event EV_Layout_StuffCommand
(
    "stuffcommand",
    EV_DEFAULT,
    "s",
    "command",
    "Set the command to be stuffed when the widget is clicked"
);

Event EV_Layout_LinkCvar
(
    "linkcvar",
    EV_DEFAULT,
    "s",
    "cvarname",
    "Set the cvar that will be updated when the widget is changed"
);

Event EV_Layout_NoParentClipping
(
    "noparentclip",
    EV_DEFAULT,
    NULL,
    NULL,
    "Set the widget to not be clipped by the parent widget"
);

Event EV_Layout_NoParentAdjustment
(
    "noparentadjust",
    EV_DEFAULT,
    NULL,
    NULL,
    "Set the widget to not be adjusted by the parent widget"
);

Event EV_Widget_HideCommand
(
    "hidecommand",
    EV_DEFAULT,
    "s",
    "command",
    "Set a command to be executed when the widget is hidden"
);

Event EV_Widget_ShowCommand
(
    "showcommand",
    EV_DEFAULT,
    "s",
    "command",
    "Set a command to be executed when the widget is shown"
);

Event EV_Widget_OrderNumber
(
    "ordernumber",
    EV_DEFAULT,
    "d",
    "num",
    "Set the order the widget should be activated in"
);

Event EV_Widget_TextAlignment
(
    "textalign",
    EV_DEFAULT,
    "s",
    "alignment",
    "Sets the alignment of text for this widget"
);

Event EV_Widget_EnabledCvar
(
    "enabledcvar",
    EV_DEFAULT,
    "s",
    "cvar_name",
    "Set the cvar to check to see if this button is enabled or not"
);

Event EV_Widget_ScaleCvar
(
    "scalecvar",
    EV_DEFAULT,
    "s",
    "cvar_name",
    "Set the cvar to check to scale this component"
);

Event EV_Layout_AliasCache
(
    "aliascache",
    EV_DEFAULT,
    "ssSSSSSS",
    "alias realPath arg1 arg2 arg3 arg4 arg5 arg6",
    "Create an alias to the specified path and cache the resource"
);

Event EV_Layout_DontLocalize
(
    "dontlocalize",
    EV_DEFAULT,
    NULL,
    NULL,
    "Disables localization for this widget"
);

// Added in 2.0
Event EV_Layout_FadeSequence
(
    "fadesequence",
    EV_DEFAULT,
    "ffff",
    "delay fadein hold fadeout",
    "Set up for a delay/fadein/hold/fadeout"
);

Event EV_Layout_BGFill
(
    "bgfill",
    EV_DEFAULT,
    "ffff",
    "red green blue alpha",
    "Set a color to fill the entire background"
);

Event EV_Layout_Fullscreen
(
    "fullscreen",
    EV_DEFAULT,
    "b",
    "fullscreen",
    "Set container to be fullscreen.  This will disable the game rendering if it's active"
);

Event EV_Layout_VidMode
(
    "vidmode",
    EV_DEFAULT,
    "i",
    "vidmode",
    "Set container's vidmode to change to when it's active"
);

CLASS_DECLARATION(USignal, UIWidget, NULL) {
    {&EV_Widget_Hide,               &UIWidget::Hide                    },
    {&EV_Widget_Activate,           &UIWidget::Activate                },
    {&EV_Layout_Size,               &UIWidget::LayoutSize              },
    {&EV_Layout_Rect,               &UIWidget::LayoutRect              },
    {&EV_Layout_Name,               &UIWidget::LayoutName              },
    {&EV_Layout_Title,              &UIWidget::LayoutTitle             },
    {&EV_Layout_Font,               &UIWidget::LayoutFont              },
    {&EV_Layout_BGColor,            &UIWidget::LayoutBgColor           },
    {&EV_Layout_FGColor,            &UIWidget::LayoutFgColor           },
    {&EV_Layout_Borderstyle,        &UIWidget::LayoutBorderStyle       },
    {&EV_Layout_BorderColor,        &UIWidget::LayoutBorderColor       },
    {&EV_Layout_Shader,             &UIWidget::LayoutShader            },
    {&EV_Layout_TileShader,         &UIWidget::LayoutTileShader        },
    {&EV_Layout_HoverShader,        &UIWidget::LayoutHoverShader       },
    {&EV_Layout_PressedShader,      &UIWidget::LayoutPressedShader     },
    {&EV_Layout_FadeIn,             &UIWidget::LayoutFadeIn            },
    {&EV_Layout_StopSound,          &UIWidget::LayoutStopSound         },
    {&EV_Layout_ClickSound,         &UIWidget::LayoutClickSound        },
    {&EV_Layout_Stretch,            &UIWidget::LayoutStretch           },
    {&EV_Layout_VirtualRes,         &UIWidget::LayoutVirtualRes        },
    {&EV_Layout_InitData,           &UIWidget::LayoutInitData          },
    {&EV_Layout_Direction,          &UIWidget::LayoutDirection         },
    {&EV_Layout_Align,              &UIWidget::LayoutAlign             },
    {&EV_Layout_NoParentClipping,   &UIWidget::LayoutNoParentClipping  },
    {&EV_Layout_NoParentAdjustment, &UIWidget::LayoutNoParentAdjustment},
    {&EV_Layout_StuffCommand,       &UIWidget::LayoutStuffCommand      },
    {&EV_Layout_LinkCvar,           &UIWidget::LayoutLinkCvar          },
    {&EV_Layout_AliasCache,         &UIWidget::LayoutAliasCache        },
    {&EV_Widget_ShowCommand,        &UIWidget::ShowCommand             },
    {&EV_Widget_HideCommand,        &UIWidget::HideCommand             },
    {&EV_Widget_OrderNumber,        &UIWidget::LayoutOrderNumber       },
    {&EV_Widget_TextAlignment,      &UIWidget::TextAlignment           },
    {&EV_Widget_Enable,             &UIWidget::EnableEvent             },
    {&EV_Widget_Disable,            &UIWidget::DisableEvent            },
    {&EV_Widget_EnabledCvar,        &UIWidget::SetEnabledCvar          },
    {&EV_Widget_ScaleCvar,          &UIWidget::SetScaleCvar            },

    //
    // Added in 2.0
    //

    {&EV_Layout_DontLocalize,       &UIWidget::SetDontLocalize         },
    {&EV_Layout_FadeSequence,       &UIWidget::EventFadeSequence       },

    {NULL,                          NULL                               }
};

void SetColor(const UColor& color, float alpha)
{
    const vec4_t col = {color.r, color.g, color.b, color.a * alpha};

    uii.Rend_SetColor(col);
}

void DrawBox(const UIRect2D& rect, const UColor& color, float alpha)
{
    DrawBox(rect.pos.x, rect.pos.y, rect.size.width, rect.size.height, color, alpha);
}

void Draw3DBox(const UIRect2D& rect, bool indent, const UBorderColor& color, float alpha)
{
    Draw3DBox(rect.pos.x, rect.pos.y, rect.size.width, rect.size.height, indent, color, alpha);
}

void DrawBox(float x, float y, float width, float height, const UColor& color, float alpha)
{
    SetColor(color, alpha);
    uii.Rend_DrawBox(x, y, width, height);
}

void Draw3DBox(float x, float y, float width, float height, bool indent, const UBorderColor& color, float alpha)
{
    if (indent) {
        DrawBox(x, y, 1.0, height - 1.0, color.dark, alpha);
        DrawBox(x + 1.0, y + 1.0, 1.0, height - 3.0, color.reallydark, alpha);
        DrawBox(x, y, width - 1.0, 1.0, color.dark, alpha);
        DrawBox(x + 1.0, y + 1.0, width - 1.0, 1.0, color.reallydark, alpha);
        DrawBox(x + width - 1.0, y, 1.0, height, color.light, alpha);
        DrawBox(x, y + height - 1.0, width, 1.0, color.light, alpha);
    } else {
        DrawBox(x + width - 2.0, y + 1.0, 1.0, height - 2.0, color.dark, alpha);
        DrawBox(x + width - 1.0, y, 1.0, height, color.reallydark, alpha);
        DrawBox(x + 1.0, y + height - 2.0, width - 2.0, 1.0, color.dark, alpha);
        DrawBox(x, y + height - 1.0, width, 1.0, color.reallydark, alpha);
        DrawBox(x, y, 1.0, height - 1.0, color.light, alpha);
        DrawBox(x, y, width - 1.0, 1.0, color.light, alpha);
    }
}

void DrawMac3DBox(const UIRect2D& rect, bool indent, const UBorderColor& color, int inset, float alpha)
{
    float x, y;
    float width, height;

    x      = rect.pos.x + inset;
    y      = rect.pos.y + inset;
    width  = rect.size.width - inset * 2;
    height = rect.size.height - inset * 2;

    if (indent) {
        DrawBox(x + 1.0, y + 1.0, width - 2.0, height - 2.0, color.dark, alpha);
        DrawBox(x, y, width, 1.0, color.reallydark, alpha);
        DrawBox(x, y + 1.0, 1.0, width - 2.0, color.reallydark, alpha);
        DrawBox(x + width - 1.0, y + 1.0, 1.0, height - 1.0, color.light, alpha);
        DrawBox(x, y + height - 1.0, width - 1.0, 1.0, color.light, alpha);
    } else {
        DrawBox(x, y, width, 1.0, color.light, alpha);
        DrawBox(x, y + 1.0, 1.0, height - 2.0, color.light, alpha);
        DrawBox(x + width - 1.0, y + 1.0, 1.0, height - 1.0, color.dark, alpha);
        DrawBox(x, y + height - 1.0, width - 1.0, 1.0, color.dark, alpha);
    }
}

void DrawBoxWithSolidBorder(
    const UIRect2D& rect, const UColor& inside, const UColor& outside, int size, int flags, float alpha
)
{
    if (flags & 1) {
        DrawBox(
            rect.pos.x + size, rect.pos.y + size, rect.size.width - size * 2, rect.size.height - size * 2, inside, alpha
        );
    }

    if (size && flags & 2) {
        DrawBox(rect.pos.x, rect.pos.y, size, rect.size.height - size, outside, alpha);
        DrawBox(rect.pos.x + size, rect.pos.y, rect.size.width - size, size, outside, alpha);
        DrawBox(rect.pos.x + rect.size.width - size, rect.pos.y + size, size, rect.size.height - size, outside, alpha);
        DrawBox(rect.pos.x, rect.pos.y + rect.size.height - size, rect.size.width - size, size, outside, alpha);
    }
}

static bool scaleFrameVirtualRes(UIRect2D& frame, vec3_t outScale, const vec3_t newScale)
{
    if (!VectorCompare2D(outScale, newScale)) {
        frame.pos.x       = frame.pos.x / outScale[0] * newScale[0];
        frame.pos.y       = frame.pos.y / outScale[1] * newScale[1];
        frame.size.width  = frame.size.width / outScale[0] * newScale[0];
        frame.size.height = frame.size.height / outScale[1] * newScale[1];
        outScale[0]       = newScale[0];
        outScale[1]       = newScale[1];

        return true;
    }

    return false;
}

UIReggedMaterial::UIReggedMaterial()
{
    hMat  = 0;
    isSet = true;
    isGot = false;
}

uihandle_t UIReggedMaterial::GetMaterial()
{
    if (!isGot) {
        isGot      = true;
        this->hMat = uii.Rend_RegisterMaterial(name.c_str());
    }
    return hMat;
}

void UIReggedMaterial::ReregisterMaterial(void)
{
    if (isGot) {
        this->hMat = uii.Rend_RegisterMaterial(name.c_str());
    }
}

void UIReggedMaterial::CleanupMaterial(void)
{
    isGot = false;
}

void UIReggedMaterial::SetMaterial(const str& name)
{
    this->name = name;
}

void UIReggedMaterial::RefreshMaterial(void)
{
    if (isGot) {
        this->hMat = uii.Rend_RefreshMaterial(name.c_str());
    }
}

str UIReggedMaterial::GetName(void)
{
    return name;
}

UIWidget::UIWidget()
{
    VectorSet2D(m_vVirtualScale, 1, 1);
    m_parent = NULL;
    m_background_color.set(0.075f, 0.097f, 0.139f, 1.0f);
    m_foreground_color         = UColor(UHudColor);
    m_border_color             = UColor(0.15f, 0.195f, 0.278f, 1.0f);
    m_indent                   = 10.0f;
    m_bottomindent             = 5.0f;
    m_borderStyle              = border_indent;
    m_visible                  = true;
    m_enabled                  = true;
    m_canactivate              = false;
    m_commandhandler           = NULL;
    m_font                     = NULL;
    m_material                 = NULL;
    m_hovermaterial            = NULL;
    m_pressedmaterial          = NULL;
    m_hovermaterial_active     = false;
    m_pressedmaterial_active   = false;
    m_direction                = D_NONE;
    m_fadetime                 = 0.0f;
    m_alpha                    = 1.0f;
    m_motiontime               = 0.0f;
    m_starttime                = 0.0f;
    m_direction_orig           = D_NONE;
    m_motiontype               = MOTION_IN;
    m_align                    = 0;
    m_flags                    = 0;
    m_dying                    = false;
    m_ordernum                 = 0;
    m_configstring_index       = -1;
    m_iFontAlignmentHorizontal = FONT_JUSTHORZ_CENTER;
    m_iFontAlignmentVertical   = FONT_JUSTVERT_CENTER;
    m_bVirtual                 = false;
    m_scaleCvar                = NULL;
    m_enabledCvar              = "";

    m_fadeSequenceDelay    = 0;
    m_fadeSequenceFadeIn   = 0;
    m_fadeSequenceHold     = 0;
    m_fadeSequenceFadeOut  = 0;
    m_fadeSequenceState    = fadesequence_t::SEQUENCE_NONE;
    m_fadeSequenceLastTime = 0;

    UIRect2D frame(6.0f, 6.0f, 100.0f, 13.0f);
    setFrame(frame);
}

UIWidget::~UIWidget()
{
    //int i;

    m_dying = true;

    if (IsActive()) {
        uWinMan.DeactivateCurrentSmart();
    }

    //for( i = m_children.NumObjects(); i > 0; i-- ) {
    //
    //}

    // remove this child from our parent
    if (m_parent) {
        m_parent->removeChild(this);
    }

    // delete font
    if (m_font) {
        delete m_font;
        m_font = NULL;
    }

    SendSignal(W_Destroyed);
}

bool UIWidget::addChild(UIWidget *widget)
{
    if (m_children.ObjectInList(widget)) {
        return false;
    }

    m_children.AddObject(widget);
    if (m_children.NumObjects() != 1) {
        UIWidget *child = m_children.ObjectAt(m_children.NumObjects() - 1);
        if ((child->m_flags & (WF_ALWAYS_TOP | WF_ALWAYS_BOTTOM))
            != (widget->m_flags & (WF_ALWAYS_TOP | WF_ALWAYS_BOTTOM))) {
            ArrangeWidgetList(m_children);
        }
    }

    return true;
}

bool UIWidget::removeChild(UIWidget *widget)
{
    if (m_children.ObjectInList(widget)) {
        m_children.RemoveObject(widget);
        return true;
    }

    return false;
}

void UIWidget::PropogateCoordinateSystem(void)
{
    int       i;
    int       n;
    UIWidget *subview;

    if (m_parent) {
        if (m_parent->m_scaleCvar) {
            m_scaleCvar = m_parent->m_scaleCvar;
        }
    }

    if (!m_parent || (m_flags & WF_NOPARENTADJUST)) {
        m_screenorigin  = m_origin;
        m_clippedorigin = m_origin;
        m_screenframe   = m_frame;
        m_clippedframe  = m_frame;
    } else {
        m_screenframe.pos.x = m_frame.pos.x + m_parent->m_screenorigin.x;
        m_screenframe.pos.y = m_frame.pos.y + m_parent->m_screenorigin.y;
        m_screenframe.size  = m_frame.size;
        m_screenorigin.x    = m_screenframe.pos.x - m_origin.x;
        m_screenorigin.y    = m_screenframe.pos.y - m_origin.y;
        m_clippedframe      = m_screenframe;
        m_clippedorigin.x   = -m_origin.x;
        m_clippedorigin.y   = -m_origin.y;
    }

    if (m_parent && !(m_flags & WF_DIRECTED)) {
        if (m_parent->m_clippedframe.pos.x > m_clippedframe.pos.x) {
            m_clippedframe.size.width -= (m_parent->m_clippedframe.pos.x - m_clippedframe.pos.x);

            if (m_clippedframe.size.width < 0.0) {
                m_clippedframe.size.width = 0.0;
            }

            m_clippedframe.pos.x = m_parent->m_clippedframe.pos.x;
        }

        if (m_parent->m_clippedframe.pos.y > m_clippedframe.pos.y) {
            m_clippedframe.size.height -= (m_parent->m_clippedframe.pos.y - m_clippedframe.pos.y);

            if (m_clippedframe.size.height < 0.0) {
                m_clippedframe.size.height = 0.0;
            }

            m_clippedframe.pos.y = m_parent->m_clippedframe.pos.y;
        }

        if (m_parent->m_clippedframe.getMaxX() < m_clippedframe.getMaxX()) {
            m_clippedframe.size.width = m_parent->m_clippedframe.getMaxX() - m_clippedframe.pos.x;

            if (m_clippedframe.size.width < 0.0) {
                m_clippedframe.size.width = 0.0;
            }
        }

        if (m_parent->m_clippedframe.getMaxY() < m_clippedframe.getMaxY()) {
            m_clippedframe.size.height = m_parent->m_clippedframe.getMaxY() - m_clippedframe.pos.y;

            if (m_clippedframe.size.height < 0.0) {
                m_clippedframe.size.height = 0.0;
            }
        }
    }

    n = m_children.NumObjects();
    for (i = 1; i <= n; i++) {
        subview = m_children.ObjectAt(i);
        subview->PropogateCoordinateSystem();
    }
}

void UIWidget::set2D(void)
{
    uii.Rend_Set2D(
        m_screenframe.pos.x,
        uid.vidHeight - (int)(m_screenframe.size.height + m_screenframe.pos.y),
        m_screenframe.size.width,
        m_screenframe.size.height,
        m_clippedorigin.x,
        m_clippedorigin.x + m_screenframe.size.width,
        m_clippedorigin.y + m_screenframe.size.height,
        m_clippedorigin.y,
        -1.0,
        1.0
    );

    uii.Rend_Scissor(
        m_clippedframe.pos.x,
        uid.vidHeight - (int)(m_clippedframe.size.height + m_clippedframe.pos.y),
        m_clippedframe.size.width,
        m_clippedframe.size.height
    );
}

void UIWidget::Draw(void) {}

void UIWidget::FrameInitialized(void)
{
    if (m_fadeSequenceDelay || m_fadeSequenceFadeIn || m_fadeSequenceHold || m_fadeSequenceFadeOut) {
        m_fadeSequenceRemainingTime = m_fadeSequenceDelay;
        m_fadeSequenceState         = fadesequence_t::SEQUENCE_STARTING;
    }
}

void UIWidget::DrawTitle(float x, float y)
{
    m_font->setColor(m_foreground_color);
    m_font->Print(x, y, Sys_LV_CL_ConvertString(m_title), -1, getVirtualScale());
}

void UIWidget::Motion(void)
{
    float t;
    float frac;
    int   i;
    float nx, ny;

    t = uid.time / 1000.0;

    if (m_fadeSequenceState != fadesequence_t::SEQUENCE_NONE) {
        // Added in 2.0
        //  Process fade sequence
        if (!paused->integer) {
            switch (m_fadeSequenceState) {
            case fadesequence_t::SEQUENCE_STARTING:
                m_alpha = 0.0;

                m_fadeSequenceRemainingTime -= t - m_fadeSequenceLastTime;
                if (m_fadeSequenceRemainingTime < 0) {
                    m_fadeSequenceState         = fadesequence_t::SEQUENCE_FADEIN;
                    m_fadeSequenceRemainingTime = m_fadeSequenceFadeIn;
                }
                break;
            case fadesequence_t::SEQUENCE_FADEIN:
                m_alpha = 1.0 - m_fadeSequenceRemainingTime / m_fadeSequenceFadeIn;

                m_fadeSequenceRemainingTime -= t - m_fadeSequenceLastTime;
                if (m_fadeSequenceRemainingTime < 0) {
                    m_fadeSequenceState         = fadesequence_t::SEQUENCE_HOLD;
                    m_fadeSequenceRemainingTime = m_fadeSequenceHold;
                }
                break;
            case fadesequence_t::SEQUENCE_HOLD:
                m_alpha = 1.0;

                m_fadeSequenceRemainingTime -= t - m_fadeSequenceLastTime;
                if (m_fadeSequenceRemainingTime < 0) {
                    m_fadeSequenceState         = fadesequence_t::SEQUENCE_FADEOUT;
                    m_fadeSequenceRemainingTime = m_fadeSequenceFadeOut;
                }
                break;
            case fadesequence_t::SEQUENCE_FADEOUT:
                m_alpha = m_fadeSequenceRemainingTime / m_fadeSequenceFadeOut;

                m_fadeSequenceRemainingTime -= (t - m_fadeSequenceLastTime);
                if (m_fadeSequenceRemainingTime < 0) {
                    // Fade sequence has finished fading
                    m_alpha             = 0;
                    m_fadeSequenceState = fadesequence_t::SEQUENCE_NONE;
                }
                break;
            default:
                break;
            }
        }

        m_fadeSequenceLastTime = t;
    } else if (m_fadetime >= 0.0) {
        frac = (t - m_starttime) / m_fadetime;

        m_alpha = frac;
        if (m_alpha > 1.0) {
            m_alpha = 1.0;
        }

        if (m_motiontype == MOTION_OUT) {
            m_alpha = 1.0 - m_alpha;
        }
    }

    if (m_direction) {
        frac = (t - m_starttime) / m_motiontime;

        if (m_motiontype == MOTION_OUT) {
            frac = 1.0 - frac;
            if (frac <= 0.0) {
                switch (m_direction) {
                case D_FROM_BOTTOM:
                    setFrame(UIRect2D(m_frame.pos.x, uid.vidHeight, m_frame.size.width, m_frame.size.height));
                    break;
                case D_FROM_TOP:
                    setFrame(UIRect2D(
                        m_frame.pos.x,
                        -m_frame.size.height - m_parent->m_screenorigin.y,
                        m_frame.size.width,
                        m_frame.size.height
                    ));
                    break;
                case D_FROM_LEFT:
                    setFrame(UIRect2D(
                        -m_frame.size.width - m_parent->m_screenorigin.x,
                        m_frame.pos.y,
                        m_frame.size.width,
                        m_frame.size.height
                    ));
                    break;
                case D_FROM_RIGHT:
                    setFrame(UIRect2D(uid.vidWidth, m_frame.pos.y, m_frame.size.width, m_frame.size.height));
                    break;
                default:
                    setFrame(UIRect2D(m_frame.pos.x, m_frame.pos.y, m_frame.size.width, m_frame.size.height));
                    break;
                }

                m_direction = D_NONE;
            }
        } else {
            if (frac >= 1.0) {
                // done moving
                setFrame(UIRect2D(m_startingpos, m_frame.size));
                FrameInitialized();

                for (i = 1; i <= m_children.NumObjects(); i++) {
                    m_children.ObjectAt(i)->FrameInitialized();
                }

                m_direction = D_NONE;
            }
        }
    }

    switch (m_direction) {
    case D_FROM_LEFT:
        nx = 0;
        if (m_parent) {
            nx = m_frame.size.width + m_parent->m_screenorigin.x;
        }
        nx = (m_startingpos.x + nx) * frac - nx;
        ny = m_frame.pos.y;

        setFrame(UIRect2D(nx, ny, m_frame.size.width, m_frame.size.height));
        break;
    case D_FROM_RIGHT:
        nx = uid.vidWidth - (uid.vidWidth - m_startingpos.x) * frac;
        ny = m_frame.pos.y;

        setFrame(UIRect2D(nx, ny, m_frame.size.width, m_frame.size.height));
        break;
    case D_FROM_BOTTOM:
        nx = m_frame.pos.x;
        ny = uid.vidHeight - (uid.vidHeight - m_startingpos.y) * frac;

        setFrame(UIRect2D(nx, ny, m_frame.size.width, m_frame.size.height));
        break;
    case D_FROM_TOP:
        nx = m_frame.pos.y;
        ny = 0;
        if (m_parent) {
            ny = m_frame.size.height + m_parent->m_screenorigin.y;
        }
        ny = (m_startingpos.y + ny) * frac - ny;

        setFrame(UIRect2D(nx, ny, m_frame.size.width, m_frame.size.height));
        break;
    default:
        break;
    }

    if (m_direction == D_NONE) {
        //
        // play a sound to indicate the motion has finished
        if (m_stopsound.length()) {
            uii.Snd_PlaySound(m_stopsound);
        }
    }
}

void UIWidget::AlignPosition(void)
{
    if (m_bVirtual) {
        vec2_t vNewVirtualScale;

        SetVirtualScale(vNewVirtualScale);

        scaleFrameVirtualRes(m_frame, m_vVirtualScale, vNewVirtualScale);
    } else if (uid.bHighResScaling) {
        scaleFrameVirtualRes(m_frame, m_vVirtualScale, uid.scaleRes);
    } else {
        vec2_t vNewVirtualScale = {1.0, 1.0};
        scaleFrameVirtualRes(m_frame, m_vVirtualScale, vNewVirtualScale);
    }

    if (m_flags & WF_STRETCH_VERTICAL) {
        m_frame.pos.y       = 0;
        m_frame.size.height = uid.vidHeight;
    }
    if (m_flags & WF_STRETCH_HORIZONTAL) {
        m_frame.pos.x      = 0;
        m_frame.size.width = uid.vidWidth;
    }

    if (m_align & WA_CENTERX) {
        m_frame.pos.x = uid.vidWidth * 0.5 - m_frame.size.width * 0.5;
    }

    if (m_align & WA_RIGHT) {
        m_frame.pos.x = uid.vidWidth - m_frame.size.width;
    } else if (m_align & WA_LEFT) {
        m_frame.pos.x = 0;
    }

    if (m_align & WA_CENTERY) {
        m_frame.pos.y = uid.vidHeight * 0.5 - m_frame.size.height * 0.5;
    }

    if (m_align & WA_BOTTOM) {
        m_frame.pos.y = uid.vidHeight - m_frame.size.height;
    } else if (m_align & WA_TOP) {
        m_frame.pos.y = 0;
    }

    setFrame(m_frame);
}

void UIWidget::Hide(Event *ev)
{
    setShow(false);
}

void UIWidget::Activate(Event *ev)
{
    uWinMan.ActivateControl(this);
}

void UIWidget::BringToFront(void)
{
    UIWidget *me;

    if (m_parent->m_children.NumObjects() > 1) {
        me = m_parent->m_children.ObjectAt(m_parent->m_children.NumObjects());
        if (me != this) {
            m_parent->m_children.RemoveObject(this);
            m_parent->m_children.AddObject(this);

            if ((m_parent->m_children.ObjectAt(m_parent->m_children.NumObjects() - 1)->m_flags & 0x30)
                != (m_flags & 0x30)) {
                ArrangeWidgetList(m_parent->m_children);
            }
        }
    }
}

void UIWidget::SplitWidgets(
    Container<UIWidget *>& src, Container<UIWidget *>& bottom, Container<UIWidget *>& normal, Container<UIWidget *>& top
)
{
    int i;

    bottom.FreeObjectList();
    normal.FreeObjectList();
    top.FreeObjectList();

    for (i = 1; i <= src.NumObjects(); i++) {
        UIWidget *wid = src.ObjectAt(i);

        if (wid->m_flags & 0x10) {
            bottom.AddObject(wid);
        } else if (wid->m_flags & 0x20) {
            top.AddObject(wid);
        } else {
            normal.AddObject(wid);
        }
    }
}

void UIWidget::CombineWidgets(
    Container<UIWidget *>& dst, Container<UIWidget *>& bottom, Container<UIWidget *>& normal, Container<UIWidget *>& top
)
{
    Container<UIWidget *> *lists[3];
    int                    list;
    int                    i;

    dst.ClearObjectList();

    lists[0] = &bottom;
    lists[1] = &normal;
    lists[2] = &top;

    for (list = 0; list < 3; list++) {
        for (i = 1; i <= lists[list]->NumObjects(); i++) {
            dst.AddObject(lists[list]->ObjectAt(i));
        }
    }
}

void UIWidget::ArrangeWidgetList(Container<UIWidget *>& list)
{
    Container<UIWidget *> bottom;
    Container<UIWidget *> normal;
    Container<UIWidget *> top;

    SplitWidgets(list, bottom, normal, top);
    CombineWidgets(list, bottom, normal, top);
}

void UIWidget::Shutdown(void)
{
    int i;

    if (IsActive()) {
        uWinMan.DeactivateCurrentSmart();
    }

    // notify childs
    for (i = m_children.NumObjects(); i > 0; i--) {
        UIWidget *wid = m_children.ObjectAt(i);

        wid->Shutdown();
        delete wid;
    }

    if (m_font) {
        delete m_font;
        m_font = NULL;
    }
}

void UIWidget::InitFrame(
    UIWidget *parentview, float x, float y, float width, float height, int border, const char *fontname
)
{
    if (border != -1) {
        setBorderStyle((borderstyle_t)border);
    }

    setFrame(UIRect2D(x, y, width, height));

    if (parentview) {
        setParent(parentview);
    } else if (this != &uWinMan) {
        setParent(&uWinMan);
    }

    m_startingpos = m_frame.pos;

    setFont(fontname);

    FrameInitialized();
}

void UIWidget::InitFrame(UIWidget *parentview, const UIRect2D& r, int border, const char *fontname)
{
    InitFrame(parentview, r.pos.x, r.pos.y, r.size.width, r.size.height, border, fontname);
}

void UIWidget::LayoutSize(Event *ev)
{
    LayoutRect(ev);
}

void UIWidget::LayoutRect(Event *ev)
{
    UIPoint2D pos;
    UISize2D  size;

    if (ev->NumArgs() == 4) {
        pos.x       = ev->GetFloat(1);
        pos.y       = ev->GetFloat(2);
        size.width  = ev->GetFloat(3);
        size.height = ev->GetFloat(4);

        setFrame(UIRect2D(pos, size));

        m_startingpos = pos;
    }
}

void UIWidget::LayoutName(Event *ev)
{
    setName(ev->GetString(1));
}

void UIWidget::LayoutTitle(Event *ev)
{
    setTitle(ev->GetString(1));
}

void UIWidget::LayoutFont(Event *ev)
{
    setFont(ev->GetString(1));
}

void UIWidget::LayoutBgColor(Event *ev)
{
    setBackgroundColor(UColor(ev->GetFloat(1), ev->GetFloat(2), ev->GetFloat(3), ev->GetFloat(4)), true);
}

void UIWidget::LayoutFgColor(Event *ev)
{
    setForegroundColor(UColor(ev->GetFloat(1), ev->GetFloat(2), ev->GetFloat(3), ev->GetFloat(4)));
}

void UIWidget::LayoutBorderStyle(Event *ev)
{
    str style = ev->GetString(1);

    if (!stricmp(style, "NONE")) {
        setBorderStyle(border_none);
    } else if (!stricmp(style, "3D_BORDER")) {
        setBorderStyle(border_3D);
    } else if (!stricmp(style, "INDENT_BORDER")) {
        setBorderStyle(border_indent);
    }
}

void UIWidget::LayoutBorderColor(Event *ev)
{
    setBorderColor(UColor(ev->GetFloat(1), ev->GetFloat(2), ev->GetFloat(3), ev->GetFloat(4)));
}

void UIWidget::LayoutShader(Event *ev)
{
    setMaterial(uWinMan.RegisterShader(ev->GetString(1)));
    m_flags &= ~WF_TILESHADER;
}

void UIWidget::LayoutTileShader(Event *ev)
{
    setMaterial(uWinMan.RegisterShader(ev->GetString(1)));
    m_flags |= WF_TILESHADER;
}

void UIWidget::LayoutHoverShader(Event *ev)
{
    setHoverMaterial(uWinMan.RegisterShader(ev->GetString(1)));
}

void UIWidget::LayoutPressedShader(Event *ev)
{
    setPressedMaterial(uWinMan.RegisterShader(ev->GetString(1)));
}

void UIWidget::LayoutFadeIn(Event *ev)
{
    m_fadetime = ev->GetFloat(1);
    if (m_motiontime != 0.0) {
        setMotionTime(m_fadetime);
    }
}

void UIWidget::LayoutStopSound(Event *ev)
{
    m_stopsound = ev->GetString(1);
    uii.Snd_RegisterSound(m_stopsound, false);
}

void UIWidget::LayoutClickSound(Event *ev)
{
    m_clicksound = ev->GetString(1);
    uii.Snd_RegisterSound(m_clicksound, false);
}

void UIWidget::LayoutStretch(Event *ev)
{
    str stretch = ev->GetString(1);

    if (!stricmp(stretch, "vertical")) {
        m_flags |= WF_STRETCH_VERTICAL;
    } else if (!stricmp(stretch, "horizontal")) {
        m_flags |= WF_STRETCH_HORIZONTAL;
    } else if (!stricmp(stretch, "both")) {
        m_flags |= WF_STRETCH_VERTICAL | WF_STRETCH_HORIZONTAL;
    }

    m_flags |= WF_NOPARENTADJUST | WF_DIRECTED;
}

void UIWidget::LayoutVirtualRes(Event *ev)
{
    if (ev->GetInteger(1)) {
        m_bVirtual = true;
    }
}

void UIWidget::LayoutInitData(Event *ev)
{
    if (m_cvarname.length()) {
        uii.Cvar_Set(m_cvarname, ev->GetString(1));
    }
}

void UIWidget::LayoutDirection(Event *ev)
{
    str direction = ev->GetString(1);

    if (!stricmp(direction, "from_bottom")) {
        setDirection(D_FROM_BOTTOM);
    } else if (!stricmp(direction, "from_top")) {
        setDirection(D_FROM_TOP);
    } else if (!stricmp(direction, "from_left")) {
        setDirection(D_FROM_LEFT);
    } else if (!stricmp(direction, "from_right")) {
        setDirection(D_FROM_RIGHT);
    } else {
        setDirection(D_NONE);
    }

    if (ev->NumArgs() > 1) {
        setMotionTime(ev->GetFloat(2));
    }

    m_flags |= WF_DIRECTED;
}

void UIWidget::LayoutAlign(Event *ev)
{
    for (int i = 1; i <= ev->NumArgs(); i++) {
        str align = ev->GetString(i);

        if (!stricmp(align, "left")) {
            m_align |= WA_LEFT;
        } else if (!stricmp(align, "right")) {
            m_align |= WA_RIGHT;
        } else if (!stricmp(align, "top")) {
            m_align |= WA_TOP;
        } else if (!stricmp(align, "bottom")) {
            m_align |= WA_BOTTOM;
        } else if (!stricmp(align, "centerx")) {
            m_align |= WA_CENTERX;
        } else if (!stricmp(align, "centery")) {
            m_align |= WA_CENTERY;
        }
    }

    if (!isSubclassOf(UIWidgetContainer)) {
        m_flags |= WF_NOPARENTADJUST | WF_DIRECTED;
    }
}

void UIWidget::LayoutStuffCommand(Event *ev)
{
    LinkCommand(ev->GetString(1));
}

void UIWidget::LayoutLinkCvar(Event *ev)
{
    LinkCvar(ev->GetString(1));
}

void UIWidget::LayoutNoParentClipping(Event *ev)
{
    m_flags |= WF_DIRECTED;
}

void UIWidget::LayoutNoParentAdjustment(Event *ev)
{
    m_flags |= WF_NOPARENTADJUST;
}

void UIWidget::LayoutOrderNumber(Event *ev)
{
    m_ordernum = ev->GetInteger(1);
}

void UIWidget::TextAlignment(Event *ev)
{
    for (int i = 1; i <= ev->NumArgs(); i++) {
        str align = ev->GetString(1);

        if (!stricmp(align, "left")) {
            setFontHorizontalAlignment(FONT_JUSTHORZ_LEFT);
        } else if (!stricmp(align, "right")) {
            setFontHorizontalAlignment(FONT_JUSTHORZ_RIGHT);
        } else if (!stricmp(align, "top")) {
            setFontVerticalAlignment(FONT_JUSTVERT_TOP);
        } else if (!stricmp(align, "bottom")) {
            setFontVerticalAlignment(FONT_JUSTVERT_BOTTOM);
        } else if (!stricmp(align, "centerx")) {
            setFontHorizontalAlignment(FONT_JUSTHORZ_CENTER);
        } else if (!stricmp(align, "centery")) {
            setFontVerticalAlignment(FONT_JUSTVERT_CENTER);
        }
    }
}

void UIWidget::LayoutAliasCache(Event *ev)
{
    int i;
    str parms;
    str realSound;

    if (ev->NumArgs() <= 1) {
        return;
    }

    for (i = 3; i <= ev->NumArgs(); i++) {
        parms += ev->GetToken(i);
        parms += " ";
    }

    realSound = ev->GetString(2);

    uii.Alias_Add(parms, realSound, parms);

    if (strstr(realSound, ".wav") || strstr(realSound, ".mp3")) {
        uii.Snd_RegisterSound(realSound, false);
    }
}

void UIWidget::setParent(UIWidget *parent)
{
    if (m_parent) {
        m_parent->removeChild(this);
    }

    m_parent = parent;

    if (m_parent) {
        m_parent->addChild(this);
    }

    PropogateCoordinateSystem();
}

UIWidget *UIWidget::getParent(void)
{
    return m_parent;
}

UIWidget *UIWidget::getFirstChild(void)
{
    if (m_children.NumObjects() > 0) {
        return m_children.ObjectAt(1);
    }

    return NULL;
}

UIWidget *UIWidget::getNextSibling(void)
{
    if (m_parent) {
        return m_parent->getNextChild(this);
    }

    return NULL;
}

UIWidget *UIWidget::getNextChild(UIWidget *child)
{
    int index = m_children.IndexOfObject(child);

    if (index && m_children.NumObjects() != index) {
        return m_children.ObjectAt(index + 1);
    }

    return NULL;
}

UIWidget *UIWidget::getPrevChild(UIWidget *child)
{
    int index;

    if (child) {
        index = m_children.IndexOfObject(child);
    } else {
        index = m_children.NumObjects();
    }

    if (index > 1) {
        return m_children.ObjectAt(index - 1);
    }

    return NULL;
}

UIWidget *UIWidget::getPrevSibling(void)
{
    if (m_parent) {
        return m_parent->getPrevChild(this);
    }

    return NULL;
}

UIWidget *UIWidget::getPrevSibling(UIWidget *curr)
{
    if (m_parent) {
        return m_parent->getPrevChild(curr);
    }

    return NULL;
}

UIWidget *UIWidget::getLastSibling(void)
{
    if (m_parent && m_children.NumObjects()) {
        UIWidget *wid = m_parent->m_children.ObjectAt(m_parent->m_children.NumObjects());
        if (wid == this) {
            return getPrevSibling();
        } else {
            return wid;
        }
    } else {
        return NULL;
    }
}

UIWidget *UIWidget::findSibling(str name)
{
    UIWidget *wid;

    for (wid = getLastSibling(); wid != NULL; wid = getPrevSibling(wid)) {
        if (wid->getName() == name) {
            return wid;
        }
    }

    return NULL;
}

void UIWidget::Enable(void)
{
    int n;
    int i;

    setShow(true);
    m_enabled = true;

    n = m_children.NumObjects();
    for (i = 1; i <= n; i++) {
        m_children.ObjectAt(i)->Enable();
    }
}

void UIWidget::Disable(void)
{
    int n;
    int i;

    PostEvent(EV_Widget_Hide, 0);
    m_enabled = false;

    n = m_children.NumObjects();
    for (i = 1; i <= n; i++) {
        m_children.ObjectAt(i)->Disable();
    }
}

bool UIWidget::isEnabled(void)
{
    if (!strcmp(m_enabledCvar.c_str(), "")) {
        return m_enabled;
    }

    if (m_enabledCvar[0] == '!') {
        //
        // Added in 2.30
        //  True if the cvar is 0
        if (m_enabled) {
            return !UI_GetCvarInt(m_enabledCvar.c_str() + 1, 0);
        }
    } else {
        if (m_enabled) {
            return UI_GetCvarInt(m_enabledCvar.c_str(), 0) != 0;
        }
    }

    return false;
}

bool UIWidget::IsDying(void)
{
    return m_dying;
}

UIWidget *UIWidget::FindResponder(const UIPoint2D& pos)
{
    UIWidget *subview;
    UIWidget *responder;
    int       i;

    if (!m_visible || !m_clippedframe.contains(pos)) {
        return NULL;
    }

    for (i = m_children.NumObjects(); i >= 1; i--) {
        subview   = m_children.ObjectAt(i);
        responder = subview->FindResponder(pos);
        if (responder) {
            return responder;
        }
    }

    return this;
}

void UIWidget::setFont(const char *name)
{
    if (m_font) {
        delete m_font;
    }

    m_font = new UIFont(name);
}

void UIWidget::setFontHorizontalAlignment(fonthorzjustify_t alignment)
{
    m_iFontAlignmentHorizontal = alignment;
}

void UIWidget::setFontVerticalAlignment(fontvertjustify_t alignment)
{
    m_iFontAlignmentVertical = alignment;
}

void UIWidget::setShow(bool visible)
{
    m_visible = visible;

    if (!visible) {
        if (IsThisOrChildActive()) {
            uWinMan.DeactivateCurrentSmart();
        }
    }
}

bool UIWidget::getShow(void)
{
    return m_visible;
}

UColor UIWidget::getBackgroundColor(void)
{
    return m_background_color;
}

void UIWidget::setBackgroundColor(const UColor& color, bool setbordercolor)
{
    m_background_color = color;

    if (setbordercolor) {
        setBorderColor(color);
    }
}

UBorderColor UIWidget::getBorderColor(void)
{
    return m_border_color;
}

void UIWidget::setBorderColor(const UBorderColor& color)
{
    m_border_color = color;
}

UColor UIWidget::getForegroundColor(void)
{
    return m_foreground_color;
}

void UIWidget::setForegroundColor(const UColor& color)
{
    m_foreground_color = color;
}

borderstyle_t UIWidget::getBorderStyle(void)
{
    return m_borderStyle;
}

void UIWidget::setBorderStyle(borderstyle_t style)
{
    m_borderStyle = style;
}

UISize2D UIWidget::getSize(void)
{
    return m_frame.size;
}

void UIWidget::setSize(const UISize2D& newsize)
{
    if (newsize.width != m_frame.size.width || newsize.height != m_frame.size.height) {
        m_frame.size = newsize;
        PropogateCoordinateSystem();
        SendSignal(W_SizeChanged);
    }
}

UIRect2D UIWidget::getFrame(void)
{
    return m_frame;
}

void UIWidget::setFrame(const UIRect2D& newframe)
{
    bool bNewSize;

    bNewSize = newframe.size != m_frame.size;

    m_frame = newframe;

    PropogateCoordinateSystem();
    SendSignal(W_FrameChanged);

    if (bNewSize) {
        SendSignal(W_SizeChanged);
    }
}

UIPoint2D UIWidget::getOrigin(void)
{
    return m_origin;
}

void UIWidget::setOrigin(const UIPoint2D& neworigin)
{
    if (neworigin != m_origin) {
        m_origin = neworigin;
        PropogateCoordinateSystem();
    }
}

void UIWidget::setName(str name)
{
    m_name = name;
}

const char *UIWidget::getName(void)
{
    return m_name;
}

void UIWidget::setTitle(str title)
{
    m_title = title;
}

const char *UIWidget::getTitle(void)
{
    return m_title;
}

void UIWidget::setDirection(direction_t dir)
{
    m_direction      = dir;
    m_direction_orig = dir;
}

direction_t UIWidget::getDirection(void)
{
    return m_direction;
}

void UIWidget::setMotionType(motion_t type)
{
    m_motiontype = type;
}

motion_t UIWidget::getMotionType(void)
{
    return m_motiontype;
}

void UIWidget::setMotionTime(float time)
{
    m_motiontime = time;
}

float UIWidget::getMotionTime(void)
{
    return m_motiontime;
}

void UIWidget::setAlign(int align)
{
    m_align = align;
}

int UIWidget::getAlign(void)
{
    return m_align;
}

void UIWidget::setMaterial(UIReggedMaterial *mat)
{
    m_material = mat;
}

void UIWidget::setHoverMaterial(UIReggedMaterial *mat)
{
    m_hovermaterial = mat;
}

void UIWidget::setPressedMaterial(UIReggedMaterial *mat)
{
    m_pressedmaterial = mat;
}

UIPoint2D UIWidget::getLocalPoint(const UIPoint2D& pos)
{
    if (m_parent) {
        return UIPoint2D(pos.x - m_screenframe.pos.x, pos.y - m_screenframe.pos.y);
    } else {
        return pos;
    }
}

UIPoint2D UIWidget::getGlobalPoint(const UIPoint2D& pos)
{
    if (m_parent) {
        return UIPoint2D(pos.x + m_screenframe.pos.x, pos.y + m_screenframe.pos.y);
    } else {
        return pos;
    }
}

void UIWidget::setBackgroundAlpha(float f)
{
    m_alpha = f;
}

float UIWidget::getBackgroundAlpha(void)
{
    return m_alpha;
}

void UIWidget::Display(const UIRect2D& drawframe, float parent_alpha)
{
    int    i;
    int    n;
    vec4_t col = {1, 1, 1, 1};

    if (!isEnabled()) {
        return;
    }

    if (!m_enabledCvar.length() && !IsVisible()) {
        return;
    }

    if (m_direction || m_fadetime || m_fadeSequenceState != fadesequence_t::SEQUENCE_NONE) {
        Motion();
    }

    m_local_alpha = m_alpha * parent_alpha;
    set2D();

    col[3] = m_local_alpha;
    uii.Rend_SetColor(col);

    if (m_background_color.a != 0.0) {
        DrawBox(
            0, 0, m_frame.size.width, m_frame.size.height, m_background_color, m_background_color.a * m_local_alpha
        );
    }

    if (m_borderStyle == border_outline) {
        DrawBoxWithSolidBorder(
            UIRect2D(0, 0, m_frame.size.width, m_frame.size.height),
            UWhite,
            m_border_color.original,
            2,
            2,
            m_local_alpha
        );
    } else if (m_borderStyle) {
        Draw3DBox(
            0, 0, m_frame.size.width, m_frame.size.height, m_borderStyle == border_indent, m_border_color, m_local_alpha
        );
    }

    if (m_material) {
        col[0] = m_foreground_color.r;
        col[1] = m_foreground_color.g;
        col[2] = m_foreground_color.b;
        col[3] = m_foreground_color.a * m_local_alpha;
        uii.Rend_SetColor(col);

        if (m_flags & WF_TILESHADER) {
            if (m_bVirtual) {
                float fvWidth =
                    m_frame.size.width / m_vVirtualScale[0] / uii.Rend_GetShaderWidth(m_material->GetMaterial());
                float fvHeight =
                    m_frame.size.height / m_vVirtualScale[1] / uii.Rend_GetShaderHeight(m_material->GetMaterial());

                uii.Rend_DrawPicStretched(
                    0, 0, m_frame.size.width, m_frame.size.height, 0, 0, fvWidth, fvHeight, m_material->GetMaterial()
                );
            } else {
                uii.Rend_DrawPicTiled(0, 0, m_frame.size.width, m_frame.size.height, m_material->GetMaterial());
            }
        } else {
            uii.Rend_DrawPicStretched(
                0, 0, m_frame.size.width, m_frame.size.height, 0, 0, 1, 1, m_material->GetMaterial()
            );
        }
    }

    if (m_pressedmaterial_active && m_pressedmaterial != NULL) {
        uii.Rend_DrawPicStretched(
            0, 0, m_frame.size.width, m_frame.size.height, 0, 0, 1, 1, m_pressedmaterial->GetMaterial()
        );
    } else if (m_hovermaterial_active && m_hovermaterial != NULL) {
        uii.Rend_DrawPicStretched(
            0, 0, m_frame.size.width, m_frame.size.height, 0, 0, 1, 1, m_hovermaterial->GetMaterial()
        );
    }

    Draw();

    n = m_children.NumObjects();
    for (i = 1; i <= m_children.NumObjects(); i++) {
        m_children.ObjectAt(i)->Display(m_frame, m_local_alpha);
    }
}

qboolean UIWidget::KeyEvent(int key, unsigned int time)
{
    return qfalse;
}

void UIWidget::CharEvent(int ch) {}

void UIWidget::UpdateData(void) {}

void UIWidget::UpdateUIElement(void) {}

float UIWidget::getTitleWidth(void)
{
    return m_font->getWidth(getTitle(), -1);
}

float UIWidget::getTitleHeight(void)
{
    return m_font->getHeight(getVirtualScale());
}

bool UIWidget::CanActivate(void)
{
    if (!m_canactivate || m_dying) {
        return false;
    }

    for (UIWidget *wid = this; wid != NULL; wid = wid->getParent()) {
        if (!wid->getShow()) {
            return false;
        }
    }

    return true;
}

void UIWidget::AllowActivate(bool canactivate)
{
    m_canactivate = canactivate;
}

bool UIWidget::IsActive(void)
{
    return uWinMan.ActiveControl() == this;
}

bool UIWidget::IsVisible(void)
{
    return m_visible;
}

void UIWidget::LinkCvar(str cvarname)
{
    m_cvarname = cvarname;
}

void UIWidget::LinkCommand(str cmd)
{
    m_command = cmd;
}

void UIWidget::ExecuteShowCommands(void)
{
    for (int i = 1; i <= m_showcommands.NumObjects(); i++) {
        //uii.Cmd_Stuff( m_showcommands.ObjectAt( i )->c_str() );
        // Fixed in 2.0
        Event event(m_showcommands.ObjectAt(i)->c_str());
        ProcessEvent(event);
    }
}

void UIWidget::ExecuteHideCommands(void)
{
    for (int i = 1; i <= m_hidecommands.NumObjects(); i++) {
        //uii.Cmd_Stuff( m_hidecommands.ObjectAt( i )->c_str() );
        // Fixed in 2.0
        Event event(m_hidecommands.ObjectAt(i)->c_str());
        ProcessEvent(event);
    }
}

void UIWidget::InitializeCommandHandler(void (*fcn)(const char *, void *))
{
    m_commandhandler = fcn;
}

UIWidget *UIWidget::FindWidget(str name)
{
    int i;
    int n;

    n = m_children.NumObjects();
    for (i = 1; i <= n; i++) {
        UIWidget *wid = m_children.ObjectAt(i);
        if (wid->getName() == name) {
            return wid;
        }

        UIWidget *w = wid->FindWidget(name);
        if (w) {
            return w;
        }
    }

    return NULL;
}

void UIWidget::ResetMotion(motion_t type)
{
    AlignPosition();

    m_direction  = m_direction_orig;
    m_motiontype = type;

    if (type == MOTION_IN) {
        if (m_direction == D_FROM_BOTTOM) {
            UIPoint2D point(m_frame.pos.x, uid.vidHeight);
            setFrame(UIRect2D(point, m_frame.size));
        } else if (m_direction == D_FROM_TOP) {
            UIPoint2D point(m_frame.pos.x, -m_frame.size.height - m_parent->m_screenorigin.y);
            setFrame(UIRect2D(point, m_frame.size));
        } else if (m_direction == D_FROM_LEFT) {
            UIPoint2D point(-m_frame.size.width - m_parent->m_screenorigin.x, m_frame.pos.y);
            setFrame(UIRect2D(point, m_frame.size));
        } else if (m_direction == D_FROM_RIGHT) {
            UIPoint2D point(uid.vidWidth, m_frame.pos.y);
            setFrame(UIRect2D(point, m_frame.size));
        } else {
            setFrame(m_frame);
            FrameInitialized();
        }
    }

    m_starttime = uid.time / 1000.0;
    // Added in 2.0
    m_fadeSequenceLastTime = uid.time / 1000.0;
}

void UIWidget::Realign(void)
{
    bool bScaled = false;

    if (m_bVirtual) {
        vec2_t vNewVirtualScale;

        SetVirtualScale(vNewVirtualScale);

        scaleFrameVirtualRes(m_frame, m_vVirtualScale, vNewVirtualScale);
        bScaled = true;
    } else if (uid.bHighResScaling) {
        scaleFrameVirtualRes(m_frame, m_vVirtualScale, uid.scaleRes);
        bScaled = true;
    } else {
        vec2_t vNewVirtualScale = {1.0, 1.0};
        bScaled                 = scaleFrameVirtualRes(m_frame, m_vVirtualScale, vNewVirtualScale);
    }

    if (m_flags & WF_STRETCH_VERTICAL) {
        m_frame.pos.y       = 0;
        m_frame.size.height = uid.vidHeight;
    }
    if (m_flags & WF_STRETCH_HORIZONTAL) {
        m_frame.pos.x      = 0;
        m_frame.size.width = uid.vidWidth;
    }

    if (m_align & WA_CENTERX) {
        m_frame.pos.x = uid.vidWidth * 0.5 - m_frame.size.width * 0.5;
    }

    if (m_align & WA_RIGHT) {
        m_frame.pos.x = uid.vidWidth - m_frame.size.width;
    } else if (m_align & WA_LEFT) {
        m_frame.pos.x = 0;
    }

    if (m_align & WA_CENTERY) {
        m_frame.pos.y = uid.vidHeight - m_frame.size.height;
    }

    if (m_align & WA_BOTTOM) {
        m_frame.pos.y = uid.vidHeight - m_frame.size.height;
    } else if (m_align & WA_TOP) {
        m_frame.pos.y = 0;
    }

    if ((m_align & WA_FULL) || (m_flags & (WF_STRETCH_HORIZONTAL | WF_STRETCH_VERTICAL)) || bScaled) {
        setFrame(m_frame);
        m_startingpos = m_frame.pos;
    }
}

void UIWidget::BringToFrontPropogated(void)
{
    for (UIWidget *at = this; at->getParent() != NULL; at = at->getParent()) {
        at->BringToFront();
    }
}

UIWidget *UIWidget::IsThisOrChildActive(void)
{
    UIWidget *child = this;

    if (child->IsActive()) {
        return child;
    }

    for (int i = 1; i <= m_children.NumObjects(); i++) {
        child = m_children.ObjectAt(i);
        if (child->IsThisOrChildActive()) {
            return child;
        }
    }

    return NULL;
}

UIPoint2D UIWidget::MouseEventToClientPoint(Event *ev)
{
    UIPoint2D p;

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

    return p;
}

UIRect2D UIWidget::getClientFrame(void)
{
    // don't return the position
    return UIRect2D(UIPoint2D(0, 0), m_frame.size);
}

void UIWidget::setAlwaysOnBottom(bool b)
{
    if (getAlwaysOnBottom() != b) {
        if (b) {
            m_flags &= ~WF_ALWAYS_TOP;
            m_flags |= WF_ALWAYS_BOTTOM;
        } else {
            m_flags &= ~WF_ALWAYS_BOTTOM;
        }

        if (m_parent) {
            ArrangeWidgetList(m_parent->m_children);
        }
    }
}

void UIWidget::setAlwaysOnTop(bool b)
{
    if (getAlwaysOnTop() != b) {
        if (b) {
            m_flags &= ~WF_ALWAYS_BOTTOM;
            m_flags |= WF_ALWAYS_TOP;
        } else {
            m_flags &= ~WF_ALWAYS_TOP;
        }

        if (m_parent) {
            ArrangeWidgetList(m_parent->m_children);
        }
    }
}

bool UIWidget::getAlwaysOnBottom(void)
{
    return (m_flags & WF_ALWAYS_BOTTOM) ? true : false;
}

bool UIWidget::getAlwaysOnTop(void)
{
    return (m_flags & WF_ALWAYS_TOP) ? true : false;
}

bool UIWidget::SendSignal(Event& event)
{
    if (uWinMan.IsDead()) {
        return false;
    } else {
        return USignal::SendSignal(event);
    }
}

void UIWidget::ShowCommand(Event *ev)
{
    int  i;
    str *command = new str;

    for (i = 1; i <= ev->NumArgs(); i++) {
        *command += ev->GetToken(i);
    }

    m_showcommands.AddObject(command);
}

void UIWidget::HideCommand(Event *ev)
{
    int  i;
    str *command = new str;

    for (i = 1; i <= ev->NumArgs(); i++) {
        *command += ev->GetToken(i);
    }

    m_hidecommands.AddObject(command);
}

void UIWidget::AddFlag(int flag)
{
    m_flags |= flag;
}

void UIWidget::SetHovermaterialActive(bool a)
{
    m_hovermaterial_active = a;
}

void UIWidget::SetPressedmaterialActive(bool a)
{
    m_pressedmaterial_active = a;
}

int UIWidget::getOrderNum(void)
{
    return m_ordernum;
}

str UIWidget::getCommand(void)
{
    return m_command;
}

void UIWidget::ActivateOrder(void)
{
    UIWidget *selwidget;

    for (selwidget = this; selwidget != NULL; selwidget = selwidget->getParent()) {
        if (selwidget->getParent() == &uWinMan) {
            if (selwidget->isSubclassOf(UIWidgetContainer)) {
                UIWidgetContainer *widcon = (UIWidgetContainer *)selwidget;
                widcon->SetActiveWidgetOrderNum(this);
            }

            return;
        }
    }
}

void UIWidget::EnableEvent(Event *ev)
{
    Enable();
}

void UIWidget::DisableEvent(Event *ev)
{
    Disable();
}

void UIWidget::setConfigstringIndex(int cs)
{
    m_configstring_index = cs;
}

int UIWidget::getConfigstringIndex(void)
{
    return m_configstring_index;
}

bool UIWidget::PassEventToWidget(str name, Event *ev)
{
    int i;
    int n;

    if (!isEnabled()) {
        return false;
    }

    if (!str::cmp(name, m_name)) {
        ProcessEvent(ev);
        return true;
    }

    n = m_children.NumObjects();
    for (i = 1; i <= n; i++) {
        if (m_children.ObjectAt(i)->PassEventToWidget(name, ev)) {
            return true;
        }
    }

    return false;
}

UIFont *UIWidget::getFont() const
{
    return m_font;
}

bool UIWidget::isVirtual() const
{
    return m_bVirtual;
}

const vec2_t& UIWidget::getVirtualScale() const
{
    if (m_bVirtual) {
        return m_vVirtualScale;
    } else {
        return uid.scaleRes;
    }
}

const vec2_t& UIWidget::getHighResScale() const
{
    return uid.scaleRes;
}

void UIWidget::SetEnabledCvar(Event *ev)
{
    m_enabledCvar = ev->GetString(1);
}

void UIWidget::SetScaleCvar(Event *ev)
{
    m_scaleCvar = uii.Cvar_Find(ev->GetString(1).c_str());
}

void UIWidget::SetVirtualScale(vec2_t out)
{
    if (m_scaleCvar) {
        const float vidRatio  = (float)uid.vidWidth / (float)uid.vidHeight;
        const float minHeight = 480;
        const float minWidth  = minHeight * vidRatio;

        out[0] = uid.vidWidth / minWidth * m_scaleCvar->value;
        out[1] = uid.vidHeight / minHeight * m_scaleCvar->value;
    } else {
        out[0] = uid.vidWidth / 640.0;
        out[1] = uid.vidHeight / 480.0;
    }
}

void UIWidget::SetDontLocalize(Event *ev)
{
    SetDontLocalize();
}

void UIWidget::EventFadeSequence(Event *ev)
{
    m_fadeSequenceDelay         = ev->GetFloat(1);
    m_fadeSequenceFadeIn        = ev->GetFloat(2);
    m_fadeSequenceHold          = ev->GetFloat(3);
    m_fadeSequenceFadeOut       = ev->GetFloat(4);
    m_fadeSequenceRemainingTime = m_fadeSequenceDelay;
    m_fadeSequenceState         = fadesequence_t::SEQUENCE_STARTING;
}

void UIWidget::SetDontLocalize()
{
    m_flags |= WF_DONTLOCALIZE;
}

CLASS_DECLARATION(UIWidget, UIWidgetContainer, NULL) {
    {&EV_Layout_BGFill,     &UIWidgetContainer::SetBGFill    },
    {&EV_Layout_Fullscreen, &UIWidgetContainer::SetFullscreen},
    {&EV_Layout_VidMode,    &UIWidgetContainer::SetVidMode   },
    {NULL,                  NULL                             }
};

UIWidgetContainer::UIWidgetContainer()
{
    m_bgfill        = UColor(0, 0, 0, 0);
    m_fullscreen    = false;
    m_vidmode       = -1;
    m_currentwidnum = 0;
    m_maxordernum   = 0;
    m_layout        = NULL;
    AllowActivate(false);
}

void UIWidgetContainer::AlignPosition(void)
{
    if (m_bVirtual) {
        vec2_t vNewVirtualScale;

        SetVirtualScale(vNewVirtualScale);

        scaleFrameVirtualRes(m_frame, m_vVirtualScale, vNewVirtualScale);
    } else if (uid.bHighResScaling) {
        scaleFrameVirtualRes(m_frame, m_vVirtualScale, uid.scaleRes);
    } else {
        vec2_t vNewVirtualScale = {1.0, 1.0};
        scaleFrameVirtualRes(m_frame, m_vVirtualScale, vNewVirtualScale);
    }

    if (m_align & WA_LEFT) {
        m_frame.pos.x = 0;
    } else if (m_align & WA_RIGHT) {
        m_frame.pos.x = uid.vidWidth - m_frame.size.width;
    } else {
        // default to center
        m_frame.pos.x = (uid.vidWidth - m_frame.size.width) * 0.5;
    }

    if (m_align & WA_BOTTOM) {
        m_frame.pos.y = uid.vidHeight - m_frame.size.height;
    } else if (m_align & WA_TOP) {
        m_frame.pos.y = 0;
    } else {
        // default to center
        m_frame.pos.y = (uid.vidHeight - m_frame.size.height) * 0.5;
    }

    setFrame(m_frame);
    m_startingpos = m_frame.pos;
}

void UIWidgetContainer::Draw(void)
{
    uii.Rend_Set2D(0, 0, uid.vidWidth, uid.vidHeight, 0, uid.vidWidth, uid.vidHeight, 0, -1.0, 1.0);
    uii.Rend_Scissor(0, 0, uid.vidWidth, uid.vidHeight);

    if (m_bgfill.a != 0.0) {
        DrawBox(0, 0, uid.vidWidth, uid.vidHeight, m_bgfill, m_local_alpha);
    }

    set2D();
}

void UIWidgetContainer::Realign(void)
{
    AlignPosition();
}

void UIWidgetContainer::SetBGFill(Event *ev)
{
    m_bgfill.r = ev->GetFloat(1);
    m_bgfill.b = ev->GetFloat(2);
    m_bgfill.g = ev->GetFloat(3);
    m_bgfill.a = ev->GetFloat(4);
}

void UIWidgetContainer::SetFullscreen(Event *ev)
{
    m_fullscreen = ev->GetBoolean(1);
}

void UIWidgetContainer::SetVidMode(Event *ev)
{
    m_vidmode = ev->GetInteger(1);
}

int UIWidgetContainer::getVidMode(void)
{
    return m_vidmode;
}

qboolean UIWidgetContainer::isFullscreen(void)
{
    return m_fullscreen;
}

UIWidget *UIWidgetContainer::GetNextWidgetInOrder(void)
{
    int i;

    if (!m_maxordernum) {
        for (i = 1; i <= m_children.NumObjects(); i++) {
            if (m_children.ObjectAt(i)->getOrderNum()) {
                m_maxordernum++;
            }
        }
    }

    m_currentwidnum++;

    if (m_currentwidnum > m_maxordernum) {
        m_currentwidnum = m_maxordernum;
    }

    for (i = 1; i <= m_children.NumObjects(); i++) {
        UIWidget *wid = m_children.ObjectAt(i);

        if (wid->getOrderNum() == m_currentwidnum) {
            return wid;
        }
    }

    return NULL;
}

UIWidget *UIWidgetContainer::GetPrevWidgetInOrder(void)
{
    int i;

    if (!m_maxordernum) {
        for (i = 1; i <= m_children.NumObjects(); i++) {
            if (m_children.ObjectAt(i)->getOrderNum()) {
                m_maxordernum++;
            }
        }
    }

    m_currentwidnum--;

    if (m_currentwidnum <= 0) {
        m_currentwidnum = m_maxordernum;
    }

    for (i = 1; i <= m_children.NumObjects(); i++) {
        UIWidget *wid = m_children.ObjectAt(i);

        if (wid->getOrderNum() == m_currentwidnum) {
            return wid;
        }
    }

    return NULL;
}

void UIWidgetContainer::SetActiveWidgetOrderNum(UIWidget *wid)
{
    int i;

    for (i = 1; i <= m_children.NumObjects(); i++) {
        if (wid == m_children.ObjectAt(i)) {
            m_currentwidnum = wid->getOrderNum();
        }
    }
}

void UIWidgetContainer::SetActiveWidgetOrderNum(int num)
{
    UIWidget *wid;
    int       i;

    for (i = 1; i <= m_children.NumObjects(); i++) {
        wid = m_children.ObjectAt(i);

        if (wid->getOrderNum() == num) {
            uWinMan.ActivateControl(wid);
        } else {
            wid->SetHovermaterialActive(false);
            wid->SetPressedmaterialActive(false);
        }
    }
}

void UIWidgetContainer::SetLastActiveWidgetOrderNum(void)
{
    if (!m_currentwidnum) {
        m_currentwidnum = 1;
    }

    SetActiveWidgetOrderNum(m_currentwidnum);
}
