#include "qe3.h"
#include "entityw.h"
#include "deco.h"

//  Adam:  entity bitmap stuff
int deco_x;
int deco_y;
int deco_w;
int deco_h;
//1 if we should show the pics, 0 if we should not.
int show_deco_pics = 0;
//the index of the list of deco pics we are going to show.
int deco_list_drawn = 0;
//the number of pics we currently have room to draw.
int deco_visible_pics = 1;
//the position of the scroll bar in each list.
int deco_scroll_position[4] = {0, 0, 0, 0};
//the maximum position of the scroll bar.
int deco_max_pos = 0;

//the list of deco bitmaps for each episode.
extern deco_bitmap_t *deco_bitmaps[4];
extern int num_deco_pics[4];

//  Nelno:  entity bitmap stuff
int entity_bitmap_x;
int entity_bitmap_y;

static  HDC hdc_Entity;

int rgIds[EntLast] = {
    IDC_E_LIST,
    IDC_E_COMMENT,
    IDC_CHECK1,
    IDC_CHECK2,
    IDC_CHECK3,
    IDC_CHECK4,
    IDC_CHECK5,
    IDC_CHECK6,
    IDC_CHECK7,
    IDC_CHECK8,
    IDC_CHECK9,
    IDC_CHECK10,
    IDC_CHECK11,
    IDC_CHECK12,
    IDC_E_PROPS,
    IDC_E_0,
    IDC_E_45,
    IDC_E_90,
    IDC_E_135,
    IDC_E_180,
    IDC_E_225,
    IDC_E_270,
    IDC_E_315,
    IDC_E_UP,
    IDC_E_DOWN,
    IDC_E_DELPROP,

    IDC_STATIC_KEY,
    IDC_E_KEY_FIELD,
    IDC_STATIC_VALUE,
    IDC_E_VALUE_FIELD,

    IDC_E_COLOR,

    //  Nelno:  for viewing entity bitmaps
    IDC_E_SCROLLBAR
};

HWND hwndEnt[EntLast];

int     inspector_mode;     // W_TEXTURE, W_ENTITY, or W_CONSOLE

qboolean    multiple_entities;

entity_t    *edit_entity;

HWND CreateTextureWindow (void);

BOOL CALLBACK EntityWndProc(
    HWND hwndDlg,   // handle to dialog box
    UINT uMsg,      // message
    WPARAM wParam,  // first message parameter
    LPARAM lParam); // second message parameter

void SizeEntityDlg(int iWidth, int iHeight);
void AddProp(void);
void GetTexMods(void);

LRESULT (CALLBACK* OldFieldWindowProc) (HWND, UINT, WPARAM, LPARAM);
LRESULT (CALLBACK* OldEntityListWindowProc) (HWND, UINT, WPARAM, LPARAM);

/*
=========================
FieldWndProc

Just to handle tab and enter...
=========================
*/
BOOL CALLBACK FieldWndProc(
    HWND hwnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_CHAR:
        if (LOWORD(wParam) == VK_TAB)
            return FALSE;
        if (LOWORD(wParam) == VK_RETURN)
            return FALSE;
        if (LOWORD(wParam) == VK_ESCAPE)
        {
            SetFocus (g_qeglobals.d_hwndCamera);
            return FALSE;
        }
        break;

    case WM_KEYDOWN:
        if (LOWORD(wParam) == VK_TAB)
        {
            if (hwnd == hwndEnt[EntKeyField])
            {
                SendMessage (hwndEnt[EntValueField], WM_SETTEXT, 0, (long)"");
                SetFocus (hwndEnt[EntValueField]);
            }
            else
                SetFocus (hwndEnt[EntKeyField]);
        }
        if (LOWORD(wParam) == VK_RETURN)
        {
            if (hwnd == hwndEnt[EntKeyField])
            {
                SendMessage (hwndEnt[EntValueField], WM_SETTEXT, 0, (long)"");
                SetFocus (hwndEnt[EntValueField]);
            }
            else
            {
                AddProp ();
                SetFocus (g_qeglobals.d_hwndCamera);
            }
        }
        break;
    case WM_LBUTTONDOWN:
        SetFocus (hwnd);
        break;
    }
    return CallWindowProc (OldFieldWindowProc, hwnd, uMsg, wParam, lParam);
}


/*
=========================
EntityListWndProc

Just to handle enter...
=========================
*/
BOOL CALLBACK EntityListWndProc(
    HWND hwnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_KEYDOWN:
        if (LOWORD(wParam) == VK_RETURN)
        {
            SendMessage ( g_qeglobals.d_hwndEntity, 
                          WM_COMMAND, 
                          (LBN_DBLCLK<<16) + IDC_E_LIST, 
                          0 );
            return 0;
        }
        break;
    }
    return CallWindowProc (OldEntityListWindowProc, hwnd, uMsg, wParam, lParam);
}


/*
================
GetEntityControls

Finds the controls from the dialog and
moves them to the window
================
*/
void GetEntityControls(HWND ghwndEntity)
{
    int i;

    for (i = 0; i < EntLast; i++)
    {
        if (i == EntList || i == EntProps || i == EntComment)
            continue;
        if (i == EntKeyField || i == EntValueField)
            continue;
        hwndEnt[i] = GetDlgItem(ghwndEntity, rgIds[i]); 
        if (hwndEnt[i])
            SetParent (hwndEnt[i], g_qeglobals.d_hwndEntity );
    }


    // SetParent apears to not modify some internal state
    // on listboxes, so create it from scratch...

    g_qeglobals.d_hwndEdit = CreateWindow ("edit", NULL, 
        ES_MULTILINE | ES_READONLY | WS_VSCROLL | WS_CHILD | WS_VISIBLE | WS_BORDER,
        995, 100, 180, 99,
        g_qeglobals.d_hwndEntity,
        (void *)IDC_E_STATUS,
        g_qeglobals.d_hInstance,
        NULL);
    if (!g_qeglobals.d_hwndEdit)
        Error ("CreateWindow failed");

    hwndEnt[EntList] = CreateWindow ("listbox", NULL, 
        LBS_STANDARD | LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT 
        | WS_VSCROLL | WS_CHILD | WS_VISIBLE,
        995, 5, 180, 99,
        g_qeglobals.d_hwndEntity,
        (void *)IDC_E_LIST,
        g_qeglobals.d_hInstance,
        NULL);
    if (!hwndEnt[EntList])
        Error ("CreateWindow failed");

    hwndEnt[EntProps] = CreateWindow ("listbox", NULL, 
        LBS_STANDARD | LBS_NOINTEGRALHEIGHT | LBS_USETABSTOPS
        | WS_VSCROLL | WS_CHILD | WS_VISIBLE,
        995, 100, 180, 99,
        g_qeglobals.d_hwndEntity,
        (void *)IDC_E_PROPS,
        g_qeglobals.d_hInstance,
        NULL);
    if (!hwndEnt[EntProps])
        Error ("CreateWindow failed");

    hwndEnt[EntComment] = CreateWindow ("edit", NULL, 
        ES_MULTILINE | ES_READONLY | WS_VSCROLL | WS_CHILD | WS_VISIBLE | WS_BORDER,
        995, 100, 180, 99,
        g_qeglobals.d_hwndEntity,
        (void *)IDC_E_COMMENT,
        g_qeglobals.d_hInstance,
        NULL);
    if (!hwndEnt[EntComment])
        Error ("CreateWindow failed");

    hwndEnt[EntKeyField] = CreateWindow ("edit", NULL, 
        WS_CHILD | WS_VISIBLE | WS_BORDER,
        995, 100, 180, 99,
        g_qeglobals.d_hwndEntity,
        (void *)IDC_E_KEY_FIELD,
        g_qeglobals.d_hInstance,
        NULL);
    if (!hwndEnt[EntKeyField])
        Error ("CreateWindow failed");

    hwndEnt[EntValueField] = CreateWindow ("edit", NULL, 
        WS_CHILD | WS_VISIBLE | WS_BORDER,
        995, 100, 180, 99,
        g_qeglobals.d_hwndEntity,
        (void *)IDC_E_VALUE_FIELD,
        g_qeglobals.d_hInstance,
        NULL);
    if (!hwndEnt[EntValueField])
        Error ("CreateWindow failed");

    g_qeglobals.d_hwndTexture = CreateTextureWindow ();

}



/*
===============================================================

ENTITY WINDOW

===============================================================
*/


void FillClassList (void)
{
    eclass_t    *pec;
    int         iIndex;

    SendMessage(hwndEnt[EntList], LB_RESETCONTENT, 0 , 0);

    for (pec = eclass ; pec ; pec = pec->next)
    {
        iIndex = SendMessage(hwndEnt[EntList], LB_ADDSTRING, 0 , (LPARAM)pec->name);
        SendMessage(hwndEnt[EntList], LB_SETITEMDATA, iIndex, (LPARAM)pec);
    }   

}


/*
==============
WEnt_Create
==============
*/
void WEnt_Create (HINSTANCE hInstance)
{
    WNDCLASS   wc;

    /* Register the camera class */
    memset (&wc, 0, sizeof(wc));

    wc.style         = 0;
    wc.lpfnWndProc   = (WNDPROC)EntityWndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = 0;
    wc.hCursor       = LoadCursor (NULL,IDC_ARROW);
    wc.hbrBackground = GetStockObject (LTGRAY_BRUSH);
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = ENT_WINDOW_CLASS;

    if (!RegisterClass (&wc) )
        Error ("RegisterClass: failed");

    g_qeglobals.d_hwndEntity = CreateWindow (ENT_WINDOW_CLASS ,
        "Entity",
        QE3_STYLE,
        20,
        20,
        100,
        480,    // size

        g_qeglobals.d_hwndMain, // parent
        0,      // no menu
        hInstance,
        NULL);

    if (!g_qeglobals.d_hwndEntity )
        Error ("Couldn't create Entity window");
}

/*
==============
CreateEntityWindow
==============
*/
BOOL CreateEntityWindow(HINSTANCE hInstance)
{
    HWND hwndEntityPalette;

    inspector_mode = W_ENTITY;

    WEnt_Create (hInstance);

    hwndEntityPalette = CreateDialog(hInstance, (char *)IDD_ENTITY, g_qeglobals.d_hwndMain, (DLGPROC)NULL);
    if (!hwndEntityPalette)
        Error ("CreateDialog failed");

    GetEntityControls (hwndEntityPalette);
    DestroyWindow (hwndEntityPalette);

    OldFieldWindowProc = (void *)GetWindowLong (hwndEnt[EntKeyField], GWL_WNDPROC);
    SetWindowLong (hwndEnt[EntKeyField], GWL_WNDPROC, (long)FieldWndProc);
    SetWindowLong (hwndEnt[EntValueField], GWL_WNDPROC, (long)FieldWndProc);

    OldEntityListWindowProc = (void *)GetWindowLong (hwndEnt[EntList], GWL_WNDPROC);
    SetWindowLong (hwndEnt[EntList], GWL_WNDPROC, (long)EntityListWndProc);

    FillClassList ();

    LoadWindowState(g_qeglobals.d_hwndEntity, "EntityWindow");

    ShowWindow (g_qeglobals.d_hwndEntity, SW_SHOW);
    SetInspectorMode (W_CONSOLE);

    return TRUE;
}

/*
==============
SetInspectorMode
==============
*/
void SetInspectorMode(int iType)
{
    RECT rc;
    HMENU hMenu = GetMenu( g_qeglobals.d_hwndMain );

    // Is the caller asking us to cycle to the next window?

    if (iType == -1)
    {
        if (inspector_mode == W_ENTITY)
            iType = W_TEXTURE;
        else if (inspector_mode == W_TEXTURE)
            iType = W_CONSOLE;
        else
            iType = W_ENTITY;
    }       

    inspector_mode = iType;
    switch(iType)
    {
        
    case W_ENTITY:
        SetWindowText(g_qeglobals.d_hwndEntity, "Entity");
        EnableMenuItem( hMenu, ID_MISC_SELECTENTITYCOLOR, MF_ENABLED | MF_BYCOMMAND );
        break;

    case W_TEXTURE:
// title is set by textures.c       SetWindowText(g_qeglobals.d_hwndEntity, "Textures");
        EnableMenuItem( hMenu, ID_MISC_SELECTENTITYCOLOR, MF_GRAYED | MF_DISABLED | MF_BYCOMMAND );
        break;

    case W_CONSOLE:
        SetWindowText(g_qeglobals.d_hwndEntity, "Console");     
        EnableMenuItem( hMenu, ID_MISC_SELECTENTITYCOLOR, MF_GRAYED | MF_DISABLED | MF_BYCOMMAND );
        break;

    default:
        break;
    }

    GetWindowRect (g_qeglobals.d_hwndEntity, &rc);
    SizeEntityDlg( rc.right - rc.left - 8, rc.bottom - rc.top - 32);

    RedrawWindow (g_qeglobals.d_hwndEntity, NULL, NULL, RDW_ERASE | RDW_INVALIDATE
        | RDW_ERASENOW | RDW_UPDATENOW | RDW_ALLCHILDREN);

    SetWindowPos( g_qeglobals.d_hwndEntity, 
                  HWND_TOP, 
                  rc.left, rc.top, 
                  rc.right - rc.left, rc.bottom - rc.top, 
                  SWP_NOSIZE | SWP_NOMOVE );
}





// SetKeyValuePairs
//
// Reset the key/value (aka property) listbox and fill it with the 
// k/v pairs from the entity being edited.
//

void SetKeyValuePairs (void)
{
    epair_t *pep;
    RECT    rc;
    char    sz[4096];

    // set key/value pair list

    GetWindowRect(hwndEnt[EntProps], &rc);
    SendMessage(hwndEnt[EntProps], LB_SETCOLUMNWIDTH, (rc.right - rc.left)/2, 0);
    SendMessage(hwndEnt[EntProps], LB_RESETCONTENT, 0, 0);

    if (edit_entity == NULL)
        return;

    // Walk through list and add pairs

    for (pep = edit_entity->epairs ; pep ; pep = pep->next)
    {
        // if the key is less than 8 chars, add a tab for alignment
        if (strlen(pep->key) > 8)
            sprintf (sz, "%s\t%s", pep->key, pep->value);
        else
            sprintf (sz, "%s\t\t%s", pep->key, pep->value);
        SendMessage(hwndEnt[EntProps], LB_ADDSTRING, 0, (LPARAM)sz);
    }

}

// SetSpawnFlags
// 
// Update the checkboxes to reflect the flag state of the entity
//
void SetSpawnFlags(void)
{
    int     f;
    int     i;
    int     v;

    f = atoi(ValueForKey (edit_entity, "spawnflags"));
    for (i=0 ; i<12 ; i++)
    {
        v = !!(f&(1<<i));
        SendMessage(hwndEnt[EntCheck1+i], BM_SETCHECK, v, 0);
    }
}


// GetSpawnFlags
// 
// Update the entity flags to reflect the state of the checkboxes
//
void GetSpawnFlags(void)
{
    int     f;
    int     i, v;
    char    sz[32];

    f = 0;
    for (i=0 ; i<12 ; i++)
    {
        v = SendMessage(hwndEnt[EntCheck1+i], BM_GETCHECK, 0, 0);
        f |= v<<i;
    }

    sprintf (sz, "%i", f);

    if (multiple_entities)
    {
        brush_t *b;

        for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
            SetKeyValue(b->owner, "spawnflags", sz);
    }
    else
        SetKeyValue (edit_entity, "spawnflags", sz);
    SetKeyValuePairs ();
}

// UpdateSel
//
// Update the listbox, checkboxes and k/v pairs to reflect the new selection
//

BOOL UpdateSel(int iIndex, eclass_t *pec)
{
    int     i;
    brush_t *b;

    if (selected_brushes.next == &selected_brushes)
    {
        edit_entity = world_entity;
        multiple_entities = false;
    }
    else
    {
        edit_entity = selected_brushes.next->owner;
        for (b=selected_brushes.next->next ; b != &selected_brushes ; b=b->next)
        {
            if (b->owner != edit_entity)
            {
                multiple_entities = true;
                break;
            }
        }
    }

    if (iIndex != LB_ERR)
        SendMessage(hwndEnt[EntList], LB_SETCURSEL, iIndex, 0);

    if (pec == NULL)
        return TRUE;

    // Set up the description

    SendMessage(hwndEnt[EntComment], WM_SETTEXT, 0, 
            (LPARAM)TranslateString(pec->comments));

    for (i=0 ; i<8 ; i++)
    {
        HWND hwnd = hwndEnt[EntCheck1+i];
        if (pec->flagnames[i] && pec->flagnames[i][0] != 0)
        {
            EnableWindow(hwnd, TRUE);
            SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)pec->flagnames[i]);
        } else {

            // disable check box
            SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)" ");
            EnableWindow(hwnd, FALSE);
        }
    }

    SetSpawnFlags();
    SetKeyValuePairs();
    return TRUE;
}

BOOL UpdateEntitySel(eclass_t *pec)
{
    int iIndex;
    
    iIndex = (int)SendMessage(hwndEnt[EntList], LB_FINDSTRINGEXACT, 
            (WPARAM)-1, (LPARAM)pec->name);

    return UpdateSel(iIndex, pec);
}

// CreateEntity
//
// Creates a new entity based on the currently selected brush and entity type.
//

void CreateEntity(void)
{
    eclass_t *pecNew;
    entity_t *petNew;
    int i;
    HWND hwnd;
    char sz[1024];

    // check to make sure we have a brush

    if (selected_brushes.next == &selected_brushes)
    {
        MessageBox(g_qeglobals.d_hwndMain, "You must have a selected brush to create an entity"
            , "info", 0);
        return;
    }


    // find out what type of entity we are trying to create

    hwnd = hwndEnt[EntList];

    i = SendMessage(hwndEnt[EntList], LB_GETCURSEL, 0, 0);

    if (i < 0)
    {
        MessageBox(g_qeglobals.d_hwndMain, "You must have a selected class to create an entity"
            , "info", 0);
        return;
    }

    SendMessage(hwnd, LB_GETTEXT, i, (LPARAM)sz);

    if (!stricmp(sz, "worldspawn"))
    {
        MessageBox(g_qeglobals.d_hwndMain, "Can't create an entity with worldspawn.", "info", 0);
        return;
    }

    pecNew = Eclass_ForName(sz, false);

    // create it

    petNew = Entity_Create(pecNew);

    if (petNew == NULL)
    {
        MessageBox(g_qeglobals.d_hwndMain, "Failed to create entity.", "info", 0);
        return;
    }

    if (selected_brushes.next == &selected_brushes)
        edit_entity = world_entity;
    else
        edit_entity = selected_brushes.next->owner;

    SetKeyValuePairs();
    Select_Deselect ();
    Select_Brush (edit_entity->brushes.onext);

	//	Nelno
	if (!stricmp (sz, "node_node"))
		node_number_node_entity (edit_entity);

    InvalidateRect(g_qeglobals.d_hwndEntity, NULL, TRUE);
    RedrawWindow (g_qeglobals.d_hwndEntity, NULL, NULL, RDW_ERASE | RDW_INVALIDATE
        | RDW_ERASENOW | RDW_UPDATENOW | RDW_ALLCHILDREN);

    SetInspectorMode(W_ENTITY);
}



/*
===============
AddProp

===============
*/
void AddProp(void)
{
    char    key[4096];
    char    value[4096];

    if (edit_entity == NULL)
        return;

    // Get current selection text

    SendMessage(hwndEnt[EntKeyField], WM_GETTEXT, sizeof(key)-1, (LPARAM)key);  
    SendMessage(hwndEnt[EntValueField], WM_GETTEXT, sizeof(value)-1, (LPARAM)value);    

    if (multiple_entities)
    {
        brush_t *b;

        for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
            SetKeyValue(b->owner, key, value);
    }
    else
        SetKeyValue(edit_entity, key, value);

    // refresh the prop listbox

    SetKeyValuePairs(); 
}

/*
===============
DelProp

===============
*/
void DelProp(void)
{
    char    sz[4096];

    if (edit_entity == NULL)
        return;

    // Get current selection text

    SendMessage(hwndEnt[EntKeyField], WM_GETTEXT, sizeof(sz)-1, (LPARAM)sz);    

    if (multiple_entities)
    {
        brush_t *b;

        for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
            DeleteKey(b->owner, sz);
    }
    else
        DeleteKey(edit_entity, sz);

    // refresh the prop listbox

    SetKeyValuePairs(); 
}

/*
===============
EditProp

===============
*/
void EditProp(void)
{
    int i;
    HWND hwnd;
    char    sz[4096];
    char    *val;

    if (edit_entity == NULL)
        return;

    hwnd = hwndEnt[EntProps];

    // Get current selection text

    i = SendMessage(hwnd, LB_GETCURSEL, 0, 0);  

    if (i < 0)
        return;

    SendMessage(hwnd, LB_GETTEXT, i, (LPARAM)sz);   

    // strip it down to the key name

    for(i=0;sz[i] != '\t';i++)
    ;

    sz[i] = '\0';

    val = sz + i + 1;
    if (*val == '\t')
        val++;

    SendMessage(hwndEnt[EntKeyField], WM_SETTEXT, 0, (LPARAM)sz);   
    SendMessage(hwndEnt[EntValueField], WM_SETTEXT, 0, (LPARAM)val);    
}


HDWP    defer;
int     col;
void MOVE(HWND hwnd, int x, int y, int w, int h)
{
    MoveWindow(hwnd, x + col, y, w, h, TRUE);
    InvalidateRect(hwnd, NULL, TRUE);
    UpdateWindow(hwnd);
}

///////////////////////////////////////////////////////////////////////////////
//  Entity_DrawBitmap
///////////////////////////////////////////////////////////////////////////////

#define FONT_HEIGHT 10


void Entity_DrawBitmap (HDC hdc)
{
    deco_bitmap_t *list;
    int i;
    
    list = deco_bitmaps[deco_list_drawn];

    //walk down the list of bitmaps and get to the first one that is visible.
    for (i = 0; i < deco_scroll_position[deco_list_drawn]; i++) {
        if (list->next == NULL) return;

        list = list->next;
    }

    //draw the bitmaps.
    for (i = 0; i < deco_visible_pics; i++) {
        //the location we will draw to.
        int x, y;
        int w, h;

        if (list == NULL) break;

        //get the position to draw.
        x = deco_x + i * 64;
        y = deco_y;

        //get the width we will draw.
        w = min(list->info.biWidth, 64);
        h = min(list->info.biHeight, 64);

        SetDIBitsToDevice(hdc, x, y, w, h, 0, 0, 0, h, list->bitmap.data,
            (CONST BITMAPINFO *)&list->info,
            DIB_RGB_COLORS);

        //go to the next list element.
        list = list->next;

    }
}

void    Entity_SetBitmapXY (int x, int y)
{
    entity_bitmap_x = x;
    entity_bitmap_y = y;

    entity_bitmap_x = 0;
    entity_bitmap_y = 0;
}

void Entity_SetBitmapPos(int x, int y, int w, int h) {
    deco_x = x;
    deco_y = y;
    deco_w = w;
    deco_h = h;

    Entity_SetBitmapXY(x, y);
}


void ShowEntityBitmaps(int iWidth, int iHeight, int x, int y, int w, int h) {
    //determine if we are going to draw the scroll bar visible, or off the side of the box.
    //check if we are in entity mode.
    if (inspector_mode != W_ENTITY) {
        show_deco_pics = 0;
    }
    //check if we have an entity selected.
    else if (edit_entity == NULL) {
        show_deco_pics = 0;
    }
    //check what type of entity we have selected.
    else {
        //we have an entity, check what type it is.
        //get the value of the classname pair.
        char *classname = ValueForKey(edit_entity, "classname");

        //check if we got the value.
        if (classname == NULL) {
            show_deco_pics = 0;
        }
        //check if the value is one of the deco classes.
        else if (stricmp(classname, "deco_e1") == 0) {
            show_deco_pics = 1;
            deco_list_drawn = 0;
        }
        else if (stricmp(classname, "deco_e2") == 0) {
            show_deco_pics = 1;
            deco_list_drawn = 1;
        }
        else if (stricmp(classname, "deco_e3") == 0) {
            show_deco_pics = 1;
            deco_list_drawn = 2;
        }
        else if (stricmp(classname, "deco_e4") == 0) {
            show_deco_pics = 1;
            deco_list_drawn = 3;
        }
    }

    //move the scroll bar to the correct position.
    MOVE(hwndEnt[EntBitmapScroll], x, y, w, h);

    h = y - iHeight;
    y = iHeight;
    Entity_SetBitmapPos(x, y, w, h);

    //check if we supposed to draw the pics.
    if (show_deco_pics == 1) {
        //compute the number of bitmaps that we can display.
        deco_visible_pics = w / 64;

        //get the scroll range.
        if (deco_visible_pics >= num_deco_pics[deco_list_drawn]) {
            //we dont scroll at all.
            deco_max_pos = 0;
        }
        else {
            deco_max_pos = num_deco_pics[deco_list_drawn] - deco_visible_pics;
        }

        //set the scroll range.
        SetScrollRange(hwndEnt[EntBitmapScroll], SB_CTL, 0, deco_max_pos, FALSE);
        //set the scroll position.
        SetScrollPos(hwndEnt[EntBitmapScroll], SB_CTL, deco_scroll_position[deco_list_drawn], TRUE);

    }
    else {
        //set the scroll range.
        SetScrollRange(hwndEnt[EntBitmapScroll], SB_CTL, 0, 0, FALSE);
        //set the scroll position.
        SetScrollPos(hwndEnt[EntBitmapScroll], SB_CTL, 0, TRUE);
    }
}

/*
===============
SizeEnitityDlg

Positions all controls so that the active inspector
is displayed correctly and the inactive ones are
off the side
===============
*/
#define GAP_SIZE 5
#define FIXED_LINE_SIZE 20
#define TOTAL_FIXED_SIZE (GAP_SIZE * 9 + FIXED_LINE_SIZE * 9)
#define SCROLL_WIDTH 18

void SizeEntityDlg(int iWidth, int total_height)
{
    int iHeight;
    int y, x, xCheck, yCheck;
    int w, h;
    int large_control_height;
    int small_control_height;
    int window_width;
    int angle_button_width;
    int column, row;

    //iHeight is the portion of the window that stretches.
    if (inspector_mode == W_ENTITY) {
        iHeight = total_height - 82 - GAP_SIZE;
    }
    else {
        iHeight = total_height;
    }

    if (iWidth < 32 || iHeight < 32) {
        return;
    }

    //==========================================
    //
    // console
    //
    col = (inspector_mode == W_CONSOLE) ? 0 : iWidth << 2;

    MOVE(g_qeglobals.d_hwndEdit,    DlgXBorder, DlgYBorder, iWidth - (2 * DlgXBorder), iHeight - (2 * DlgYBorder) );

    //==========================================
    //
    // texture controls
    //
    col = (inspector_mode == W_TEXTURE) ? 0 : iWidth << 2;

    MOVE(g_qeglobals.d_hwndTexture, DlgXBorder, DlgYBorder, iWidth - (2 * DlgXBorder), iHeight - (2 * DlgYBorder) );

    //==========================================
    //
    // entity controls
    //
    col = (inspector_mode == W_ENTITY) ? 0 : iWidth << 2;

    // top half includes the entity list (2/3) and the 
    // comments (1/3) - 2 gaps, above and below.

    //compute the size of the large and small variable height controls.
    //there are 2 large controls, and 1 small control.
    large_control_height = (iHeight - TOTAL_FIXED_SIZE) * 2 / 5;
    small_control_height = large_control_height / 2;
    window_width = iWidth - 2 * GAP_SIZE;
    angle_button_width = window_width / 7;

    x = GAP_SIZE;
    y = GAP_SIZE;
    h = large_control_height;
    w = window_width;
    MOVE(hwndEnt[EntList], x, y, w, h);

    y += h + GAP_SIZE;
    h = small_control_height;
    MOVE(hwndEnt[EntComment], x, y, w, h);

    // bottom half includes flags (fixed), k/v pairs,
    // and buttons (fixed).

    // xCheck = width of a single check box
    // yCheck = distance from top of one check to the next
    xCheck = window_width / 3;
    yCheck = FIXED_LINE_SIZE;

    y += h + GAP_SIZE;
    for (column = 0; column < 3; column++) {
        for (row = 0; row < 4; row++) {
            MOVE(hwndEnt[EntCheck1 + row + column * 4], 
                 x + column * xCheck, y + yCheck * row, xCheck, yCheck);
        }
    }


    //
    // properties scroll box
    //

    x = GAP_SIZE;
    y += 4 * FIXED_LINE_SIZE + GAP_SIZE;
    w = window_width;
    h = large_control_height;

    MOVE(hwndEnt[EntProps], x, y, w, h);

    //
    // key / value fields
    //
    x = GAP_SIZE;
    y += large_control_height + GAP_SIZE;
    w = 40;
    h = yCheck;
    MOVE(hwndEnt[EntKeyLabel], x, y, w, h);
    x += 40;
    w = window_width - 40;
    h = yCheck;
    MOVE(hwndEnt[EntKeyField], x, y, w, h);

    x = GAP_SIZE;
    y += yCheck + GAP_SIZE;
    w = 40;
    h = yCheck;
    MOVE(hwndEnt[EntValueLabel], x, y, w, h);
    x += 40;
    w = window_width - 40;
    h = yCheck;
    MOVE(hwndEnt[EntValueField], x, y, w, h);

    //
    // angle check boxes
    //
    x = GAP_SIZE;
    y += yCheck + GAP_SIZE;
    w = angle_button_width;
    h = yCheck;
    MOVE(hwndEnt[EntDir135], x, y, w, h);
    x += w;
    MOVE(hwndEnt[EntDir90], x, y, w, h);
    x += w;
    MOVE(hwndEnt[EntDir45], x, y, w, h);

    x = GAP_SIZE;
    w = angle_button_width;
    h = yCheck;
    y += yCheck;
    MOVE(hwndEnt[EntDir180], x, y, w, h);
    x += 2 * w;
    MOVE(hwndEnt[EntDir0], x, y, w, h);
    
    x = GAP_SIZE;
    y += yCheck;
    MOVE(hwndEnt[EntDir225], x, y, w, h);
    x += w;
    MOVE(hwndEnt[EntDir270], x, y, w, h);
    x += w;
    MOVE(hwndEnt[EntDir315], x, y, w, h);
    
    //the delete button.
    y -= yCheck;
    y -= yCheck;

    x = GAP_SIZE + angle_button_width * 3 + GAP_SIZE + angle_button_width + GAP_SIZE;
    w = angle_button_width * 2;
    h = yCheck;
    MOVE(hwndEnt[EntDelProp], x, y, w, h);

    // the up/down buttons.
    x = GAP_SIZE + angle_button_width * 3 + GAP_SIZE;
    y += yCheck / 2;
    w = angle_button_width;
    h = yCheck;
    MOVE(hwndEnt[EntDirUp], x, y, w, h);
    y += yCheck;
    MOVE(hwndEnt[EntDirDown], x, y, w, h);

    //compute the size and position of the scroll bar for the deco bitmaps.
    //make the width the greatest multiple of 64 that will fit within window_width.
    w = (window_width / 64) * 64;
    x = (iWidth - w) / 2;
    h = SCROLL_WIDTH;
    y = total_height - h - GAP_SIZE;

    ShowEntityBitmaps(iWidth, iHeight, x, y, w, h);
}

void DoubleClickDecoBitmaps(int x, int y) {
    int bmp_offset;
    int bmp_index;
    deco_bitmap_t *bmp;

    if (show_deco_pics == 0) return;
    if (edit_entity == NULL) return;

    //check if the coordinates are within our deco bitmaps.
    if (x < deco_x) return;
    if (y < deco_y) return;
    if (x >= deco_x + deco_w) return;
    if (y >= deco_y + deco_h) return;

    //the coordinates are in one of our bitmaps, get the index of the bitmap.
    bmp_offset = (x - deco_x) / 64;

    //get the index of the bitmap that we clicked
    bmp_index = deco_scroll_position[deco_list_drawn] + bmp_offset;

    //check if this is a valid index.
    if (bmp_index < 0) return;
    if (bmp_index > num_deco_pics[deco_list_drawn]) return;

    //get the deco info pointer.
    bmp = deco_bitmaps[deco_list_drawn];

    while (bmp_index > 0) {
        bmp_index--;

        //make sure there is a bmp after this one.
        if (bmp->next == NULL) return;

        //get the next bitmap.
        bmp = bmp->next;
    }

    //now we have the bitmap, add the entity pair.
    SetKeyValue(edit_entity, "model", &bmp->name[0]);

    SetKeyValuePairs();
}

#define cur_pos deco_scroll_position[deco_list_drawn]

void ScrollDecoBitmaps(int code, short int pos, HWND hwnd) {
    if (hwnd != hwndEnt[EntBitmapScroll]) return;
    if (show_deco_pics == 0) return;

    //check the code.
    if (code == SB_THUMBTRACK) {
        //save the position we are now at.
        cur_pos = pos;
    }
    else if (code == SB_LINELEFT) {
        cur_pos--;
        if (cur_pos < 0) {
            cur_pos = 0;
        }
    }
    else if (code == SB_LINERIGHT) {
        cur_pos++;
        if (cur_pos > deco_max_pos) {
            cur_pos = deco_max_pos;
        }
    }
    else if (code == SB_PAGELEFT) {
        cur_pos -= deco_visible_pics;
        if (cur_pos < 0) {
            cur_pos = 0;
        }
    }
    else if (code == SB_PAGERIGHT) {
        cur_pos += deco_visible_pics;
        if (cur_pos > deco_max_pos) {
            cur_pos = deco_max_pos;
        }
    }

    pos = cur_pos;
    //set the position to the pos
    SetScrollPos(hwnd, SB_CTL, pos, TRUE);

    InvalidateRect(g_qeglobals.d_hwndEntity, NULL, TRUE);
    UpdateWindow(hwnd);
}

/*
=========================
EntityWndProc
=========================
*/

extern int  currentinspector;
extern int inspectorviews[]; 

BOOL CALLBACK EntityWndProc(
    HWND hwndDlg,   // handle to dialog box
    UINT uMsg,      // message
    WPARAM wParam,  // first message parameter
    LPARAM lParam)  // second message parameter
{
    RECT    rc;
    HDC     hdc;

    GetClientRect(hwndDlg, &rc);

    switch (uMsg)
    {
    case WM_CREATE:
        hdc_Entity = GetDC (hwndDlg);

        return 0;

    case WM_HSCROLL :
        {
            ScrollDecoBitmaps((int)LOWORD(wParam), (short int)HIWORD(wParam), (HWND)lParam);
            return 0;
        }
    case WM_LBUTTONDOWN:
        {
            DoubleClickDecoBitmaps(LOWORD(lParam), HIWORD(lParam));
            return 0;
        }
    case WM_KEYDOWN:
        if ( QE_KeyDown (wParam) )
            return 0;
        else 
            return DefWindowProc( hwndDlg, uMsg, wParam, lParam );

    case WM_GETMINMAXINFO:
        {
            LPMINMAXINFO    lpmmi;

            lpmmi = (LPMINMAXINFO) lParam;
            lpmmi->ptMinTrackSize.x = 320;
            lpmmi->ptMinTrackSize.y = 500;
        }
        return 0;

    case WM_SIZE: 
        {
            int w, h;
            w = LOWORD(lParam);
            h = HIWORD(lParam);

            SizeEntityDlg(w, h);

            InvalidateRect(hwndDlg, NULL, TRUE);
            UpdateWindow(hwndDlg);

            return 0;
        }
    case WM_NCLBUTTONDBLCLK:
        BringWindowToTop(hwndDlg);
        return 0;
    case WM_NCLBUTTONUP:
    case WM_NCLBUTTONDOWN:
        {
            SetWindowPos(hwndDlg, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
            SetInspectorMode(inspector_mode);
            BringWindowToTop(hwndDlg);
            SetFocus(hwndDlg);
            break;
        }
    case WM_PAINT:
        {
            PAINTSTRUCT ps;

            hdc = BeginPaint(hwndDlg, &ps);

            Entity_DrawBitmap(hdc);

            ValidateRect(hwndDlg, NULL);

            EndPaint(hwndDlg, &ps);
        }
        return 0;

    case WM_COMMAND: 
        switch (LOWORD(wParam)) { 
        case IDC_E_DELPROP:
            DelProp();
            SetFocus (g_qeglobals.d_hwndCamera);
            break;

        case IDC_E_0:
            SetKeyValue (edit_entity, "angle", "0");
            SetFocus (g_qeglobals.d_hwndCamera);
            SetKeyValuePairs ();
            break;
        case IDC_E_45:
            SetKeyValue (edit_entity, "angle", "45");
            SetFocus (g_qeglobals.d_hwndCamera);
            SetKeyValuePairs ();
            break;
        case IDC_E_90:
            SetKeyValue (edit_entity, "angle", "90");
            SetFocus (g_qeglobals.d_hwndCamera);
            SetKeyValuePairs ();
            break;
        case IDC_E_135:
            SetKeyValue (edit_entity, "angle", "135");
            SetFocus (g_qeglobals.d_hwndCamera);
            SetKeyValuePairs ();
            break;
        case IDC_E_180:
            SetKeyValue (edit_entity, "angle", "180");
            SetFocus (g_qeglobals.d_hwndCamera);
            SetKeyValuePairs ();
            break;
        case IDC_E_225:
            SetKeyValue (edit_entity, "angle", "225");
            SetFocus (g_qeglobals.d_hwndCamera);
            SetKeyValuePairs ();
            break;
        case IDC_E_270:
            SetKeyValue (edit_entity, "angle", "270");
            SetFocus (g_qeglobals.d_hwndCamera);
            SetKeyValuePairs ();
            break;
        case IDC_E_315:
            SetKeyValue (edit_entity, "angle", "315");
            SetFocus (g_qeglobals.d_hwndCamera);
            SetKeyValuePairs ();
            break;
        case IDC_E_UP:
            SetKeyValue (edit_entity, "angle", "-1");
            SetFocus (g_qeglobals.d_hwndCamera);
            SetKeyValuePairs ();
            break;
        case IDC_E_DOWN:
            SetKeyValue (edit_entity, "angle", "-2");
            SetFocus (g_qeglobals.d_hwndCamera);
            SetKeyValuePairs ();
            break;

        case IDC_CHECK1:
        case IDC_CHECK2:
        case IDC_CHECK3:
        case IDC_CHECK4:
        case IDC_CHECK5:
        case IDC_CHECK6:
        case IDC_CHECK7:
        case IDC_CHECK8:
        case IDC_CHECK9:
        case IDC_CHECK10:
        case IDC_CHECK11:
        case IDC_CHECK12:
            GetSpawnFlags();
            SetFocus (g_qeglobals.d_hwndCamera);
            break;


        case IDC_E_PROPS: 
            switch (HIWORD(wParam))
            { 
            case LBN_SELCHANGE:

                EditProp();
                return TRUE; 
            }
            break;

        case IDC_E_LIST: 
       
            switch (HIWORD(wParam)) { 
            
            case LBN_SELCHANGE: 
            {
                int iIndex;
                eclass_t *pec;
                
                iIndex = SendMessage(hwndEnt[EntList], LB_GETCURSEL, 0, 0); 
                pec = (eclass_t *)SendMessage(hwndEnt[EntList], LB_GETITEMDATA, 
                        iIndex, 0); 
            
                UpdateSel(iIndex, pec);

                return TRUE; 
                break; 
            }

            case LBN_DBLCLK: 
                CreateEntity ();
                SetFocus (g_qeglobals.d_hwndCamera);
                break; 
            } 
            break; 


            default: 
              return DefWindowProc( hwndDlg, uMsg, wParam, lParam ); 
        } 

        return 0;
    }

    return DefWindowProc (hwndDlg, uMsg, wParam, lParam);
}
