// colorsel.cpp
//
// Implementation file for D3DCOLOR editing dialog.
//
// Copyright (C) 2001, Rich Thomson.  All rights reserved.
//
// C++ Standard Library includes
#include <sstream>

// Win32 includes
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <windowsx.h>
#include <tchar.h>
#include <commdlg.h>

// ATL includes
#include <atlbase.h>

// Application
#include "colorsel.h"
#include "resource.h"

// C++ stringstreams for use with TCHAR
typedef std::basic_ostringstream<TCHAR> tostringstream;
typedef std::basic_istringstream<TCHAR> tistringstream;

// Number of items in a fixed-size array
#define NUM_OF(ary_) (sizeof(ary_)/sizeof(ary_[0]))

// Context information for the dialog.
struct s_dialog_context
{
    enum e_color_status
    {
        COLOR_OK = 0,
        COLOR_BADVAL,
        COLOR_NONEWVAL
    };

    BYTE m_alpha;

    e_color_status update_ui(HWND dialog, bool put_not_get);
};

// Synchs the dialog controls with the dialog context
s_dialog_context::e_color_status
s_dialog_context::update_ui(HWND dialog, bool put_not_get)
{
    HWND edit_alpha = ::GetDlgItem(dialog, COLOR_ALPHA);
    if (put_not_get)
    {
        tostringstream buff;
        buff << int(m_alpha) << std::ends;
        Edit_SetSel(edit_alpha, 0, -1);
        Edit_ReplaceSel(edit_alpha, buff.str().c_str());
    }
    else
    {
        TCHAR edit_text[5];
        {
            WORD text_len = (WORD) Edit_LineLength(edit_alpha, 0);
            if (text_len > 3)
            {
    	        return COLOR_BADVAL;
            }

            if (text_len == 0)
            {
	            return COLOR_NONEWVAL;
            }

            memcpy(edit_text, &text_len, sizeof(WORD));

            ::SendMessage(edit_alpha, EM_GETLINE, 0, LPARAM(edit_text));
            edit_text[text_len] = TCHAR(0);
        }

        tistringstream stream(edit_text);
        DWORD alpha = 255;
        stream >> alpha;
        if (stream.fail() || alpha > 255)
        {
            return COLOR_BADVAL;
        }
        m_alpha = alpha;
    }

    return COLOR_OK;
}

// Color chooser hook procedure
UINT APIENTRY
hook_proc(HWND dialog, UINT msg, WPARAM wp, LPARAM lp)
{
    // this is hacky and it means we can never have more than one color
    // edit dialog open at a time, but it suffices for our purposes here
    static s_dialog_context *context = 0;

    switch (msg)
    {
	case WM_INITDIALOG:
        {
            const CHOOSECOLOR *cc = reinterpret_cast<CHOOSECOLOR *>(lp);
            ATLASSERT(cc && cc->lCustData);
            context = reinterpret_cast<s_dialog_context *>(cc->lCustData);
            context->update_ui(dialog, true);
        }
	    break;

	case WM_COMMAND:
	    if (wp == IDOK)
	    {
		    /*
		     * Get the Alpha Value
		     */
            ATLASSERT(context);
            context->update_ui(dialog, false);
	    }

	    break;
    }

    return 0; /* Always give default behaviour */
}

// Modify a D3DCOLOR value interactively through the color chooser dialog
D3DCOLOR
rt_choose_color(HWND owner, D3DCOLOR last)
{
    COLORREF custom_colors[16];
    {
        for (UINT i = 0; i < NUM_OF(custom_colors); i++)
        {
            custom_colors[i] = RGB(255, 255, 255);
        };
    }

    s_dialog_context context =
    {
        D3DColor_Alpha(last)
    };
    CHOOSECOLOR cc =
    {
        sizeof(cc),
        owner,
        reinterpret_cast<HWND>(::GetWindowLong(owner, GWL_HINSTANCE)),
        RGB(D3DColor_Red(last), D3DColor_Green(last),
            D3DColor_Blue(last)),
        custom_colors,
        CC_RGBINIT | CC_FULLOPEN | CC_ENABLETEMPLATE | CC_ENABLEHOOK,
        reinterpret_cast<LPARAM>(&context),
        hook_proc,
        _T("rt_ColorChooser")
    };

    if (::ChooseColor(&cc))
    {
        last = D3DCOLOR_ARGB(context.m_alpha,
                             GetRValue(cc.rgbResult),
                             GetGValue(cc.rgbResult),
                             GetBValue(cc.rgbResult));
    }

    return last;
}
