#include "private.h"
#include "commdlg.h"
#include "TextInputCtrl.h"

extern HINSTANCE g_hInst;

#define TEXTINPUTCTRL_CLASSNAME TEXT("TextInputCtrlClassName")
#define TIMERID_CARET 0x100

//----------------------------------------------------------------
//
//
//
//----------------------------------------------------------------

ATOM CTextInputCtrl::RegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX); 
	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= (WNDPROC)s_WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= NULL;
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= NULL;
	wcex.lpszClassName	= TEXTINPUTCTRL_CLASSNAME;
	wcex.hIconSm		= NULL;

	return RegisterClassEx(&wcex);
}

//----------------------------------------------------------------
//
//
//
//----------------------------------------------------------------

HWND CTextInputCtrl::Create(HWND hwndParent)
{
    _hwnd = CreateWindow(TEXTINPUTCTRL_CLASSNAME, 
                        NULL, 
                        WS_CHILD,
                        0, 0, 0, 0, 
                        hwndParent, 
                        NULL, 
                        g_hInst, 
                        this);


    return _hwnd;
}

//----------------------------------------------------------------
//
//
//
//----------------------------------------------------------------

LRESULT CALLBACK CTextInputCtrl::s_WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    CTextInputCtrl *ptic;
    switch (message)
    {
        case WM_CREATE:
            SetThis(hwnd, ((CREATESTRUCT *)lParam)->lpCreateParams);
            SetTimer(hwnd, TIMERID_CARET, GetCaretBlinkTime(), NULL);
            GetThis(hwnd)->OnCreate(hwnd, wParam, lParam);
            break;

        case WM_DESTROY:
            ptic = GetThis(hwnd);
            if (ptic)
            {
                ptic->OnDestroy();
            }
            break;

        case WM_SETFOCUS:
            ptic = GetThis(hwnd);
            if (ptic)
            {
                ptic->OnSetFocus(wParam, lParam);
            }
            break;

        case WM_PAINT:
            ptic = GetThis(hwnd);
            hdc = BeginPaint(hwnd, &ps);
            if (ptic)
                ptic->OnPaint(hdc);
            EndPaint(hwnd, &ps);
            break;

        case WM_KEYDOWN:
            ptic = GetThis(hwnd);
            if (ptic)
                ptic->OnKeyDown(wParam, lParam);
            break;

        case WM_LBUTTONDOWN:
            ptic = GetThis(hwnd);
            if (ptic)
                ptic->OnLButtonDown(wParam, lParam);
            break;

        case WM_LBUTTONUP:
            ptic = GetThis(hwnd);
            if (ptic)
                ptic->OnLButtonUp(wParam, lParam);
            break;

        case WM_MOUSEMOVE:
            ptic = GetThis(hwnd);
            if (ptic)
                ptic->OnMouseMove(wParam, lParam);
            break;


        case WM_IME_COMPOSITION:
            if (lParam & GCS_RESULTSTR)
            {
                HIMC himc = ImmGetContext(hwnd);

                if (himc)
                {
                    LONG nSize = ImmGetCompositionString(himc, GCS_RESULTSTR, NULL, 0);
                    if (nSize)
                    {
                        LPWSTR psz = (LPWSTR)LocalAlloc(LPTR, nSize + sizeof(WCHAR));
                        if (psz)
                        {
                            ImmGetCompositionString(himc, GCS_RESULTSTR, psz, nSize);
                            LocalFree(psz);
                        }
                    }
                }
                ImmReleaseContext(hwnd, himc);

                //
                // If we don't want to receive WM_IME_CHAR or WM_CHAR with
                // this result string, we should not call DefWindowProc()
                // with GCS_RESULTSTR, GCS_RESULTREADSTR, GCS_RESULTCLAUSE and
                // GCS_RESULTREADCLAUSE flags. 
                //
                // lParam &= ~(GCS_RESULTCLAUSE |
                //             GCS_RESULTREADCLAUSE |
                //             GCS_RESULTREADSTR |
                //             GCS_RESULTSTR);
                // if (!lParam)
                //     break;
                //
            }
            return DefWindowProc(hwnd, message, wParam, lParam);

        case WM_IME_CHAR:
            //
            // wParam is a character of the result string. 
            // if we don't want to receive WM_CHAR message for this character,
            // we should not call DefWindowProc().
            //
            return DefWindowProc(hwnd, message, wParam, lParam);

        case WM_CHAR:
            //
            // wParam is a character of the result string. 
            //
            
            switch ((WCHAR)wParam)
            {
                case 0x08:
                case 0x0a:
                    return 0;
                default:
                    break;
            }

            ptic = GetThis(hwnd);
            if (ptic)
            {
                WCHAR wc[2];
                wc[0] = (WCHAR)wParam;
                wc[1] = L'\0';
                ptic->_editor.InsertAtSelection(wc);
                InvalidateRect(hwnd, NULL, TRUE);
            }
            break;

        case WM_TIMER:
            if (wParam == TIMERID_CARET)
            {
                ptic = GetThis(hwnd);
                if (ptic)
                {
                    HDC hdc = GetDC(hwnd);
                    ptic->_editor.BlinkCaret(hdc);
                    ReleaseDC(hwnd, hdc);
                }
            }

            break;
    
        default:
            return DefWindowProc(hwnd, message, wParam, lParam);
    }
    return 0;
}



//----------------------------------------------------------------
//
//
//
//----------------------------------------------------------------

void CTextInputCtrl::OnPaint(HDC hdc)
{
    _editor.Render(hdc, &_lfCurrentFont);
}

//----------------------------------------------------------------
//
//
//
//----------------------------------------------------------------

void CTextInputCtrl::OnKeyDown(WPARAM wParam, LPARAM lParam)
{
    UINT nSelStart;
    UINT nSelEnd;
    switch (0xff & wParam)
    {
        case VK_LEFT:
             if (GetKeyState(VK_SHIFT) & 0x80)
             {
                 nSelStart = _editor.GetSelectionStart();
                 nSelEnd = _editor.GetSelectionEnd();
                 if (nSelStart > 0)
                 {
                     _editor.MoveSelection(nSelStart - 1, nSelEnd);
                 }
             }
             else
             {
                 _editor.MoveSelectionPrev();
             }
             InvalidateRect(_hwnd, NULL, TRUE);
             break;

        case VK_RIGHT:
             if (GetKeyState(VK_SHIFT) & 0x80)
             {
                 nSelStart = _editor.GetSelectionStart();
                 nSelEnd = _editor.GetSelectionEnd();
                 _editor.MoveSelection(nSelStart, nSelEnd + 1);
             }
             else
             {
                 _editor.MoveSelectionNext();
             }
             InvalidateRect(_hwnd, NULL, TRUE);
             break;

        case VK_UP:
             _editor.MoveSelectionUpDown(TRUE);
             InvalidateRect(_hwnd, NULL, TRUE);
             break;

        case VK_DOWN:
             _editor.MoveSelectionUpDown(FALSE);
             InvalidateRect(_hwnd, NULL, TRUE);
             break;

        case VK_HOME:
             _editor.MoveSelectionToLineFirstEnd(TRUE);
             InvalidateRect(_hwnd, NULL, TRUE);
             break;

        case VK_END:
             _editor.MoveSelectionToLineFirstEnd(FALSE);
             InvalidateRect(_hwnd, NULL, TRUE);
             break;

        case VK_DELETE:
             nSelStart = _editor.GetSelectionStart();
             nSelEnd = _editor.GetSelectionEnd();
             if (nSelStart == nSelEnd)
             {
                 _editor.DeleteAtSelection(FALSE);
             }
             else
             {
                 _editor.DeleteSelection();
             }
             InvalidateRect(_hwnd, NULL, TRUE);
             break;

        case VK_BACK:
             nSelStart = _editor.GetSelectionStart();
             nSelEnd = _editor.GetSelectionEnd();
             if (nSelStart == nSelEnd)
             {
                 _editor.DeleteAtSelection(TRUE);
             }
             else
             {
                 _editor.DeleteSelection();
             }
             InvalidateRect(_hwnd, NULL, TRUE);
             break;
    }
}

//----------------------------------------------------------------
//
//
//
//----------------------------------------------------------------

void CTextInputCtrl::OnCreate(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    _editor.SetWnd(hwnd);
    _editor.InitTSF();
}

//----------------------------------------------------------------
//
//
//
//----------------------------------------------------------------

void CTextInputCtrl::OnDestroy()
{
    _editor.UninitTSF();
}

//----------------------------------------------------------------
//
//
//
//----------------------------------------------------------------

void CTextInputCtrl::OnSetFocus(WPARAM wParam, LPARAM lParam)
{
    _editor.SetFocusDocumentMgr();
}

//----------------------------------------------------------------
//
//
//
//----------------------------------------------------------------

void CTextInputCtrl::OnLButtonDown(WPARAM wParam, LPARAM lParam)
{

    _editor.TerminateCompositionString();

    POINT pt;
    _uSelDragStart = (UINT)-1;
    pt.x = GET_X_LPARAM(lParam);
    pt.y = GET_Y_LPARAM(lParam);
    if (_editor.MoveSelectionAtPoint(pt))
    {
        InvalidateRect(_hwnd, NULL, TRUE);
        _uSelDragStart = _editor.GetSelectionStart();
    }
}

//----------------------------------------------------------------
//
//
//
//----------------------------------------------------------------

void CTextInputCtrl::OnLButtonUp(WPARAM wParam, LPARAM lParam)
{
    UINT nSelStart = _editor.GetSelectionStart();
    UINT nSelEnd = _editor.GetSelectionEnd();
    POINT pt;
    pt.x = GET_X_LPARAM(lParam);
    pt.y = GET_Y_LPARAM(lParam);
    if (_editor.MoveSelectionAtPoint(pt))
    {
        UINT nNewSelStart = _editor.GetSelectionStart();
        UINT nNewSelEnd = _editor.GetSelectionEnd();
        _editor.MoveSelection(min(nSelStart, nNewSelStart), max(nSelEnd, nNewSelEnd));
        InvalidateRect(_hwnd, NULL, TRUE);
    }
}

//----------------------------------------------------------------
//
//
//
//----------------------------------------------------------------

void CTextInputCtrl::OnMouseMove(WPARAM wParam, LPARAM lParam)
{
    if (wParam & MK_LBUTTON)
    {
        POINT pt;
        pt.x = GET_X_LPARAM(lParam);
        pt.y = GET_Y_LPARAM(lParam);
        if (_editor.MoveSelectionAtPoint(pt))
        {
            UINT nNewSelStart = _editor.GetSelectionStart();
            UINT nNewSelEnd = _editor.GetSelectionEnd();
            _editor.MoveSelection(min(_uSelDragStart, nNewSelStart), max(_uSelDragStart, nNewSelEnd));
            InvalidateRect(_hwnd, NULL, TRUE);
        }
    }
}
 
//----------------------------------------------------------------
//
//
//
//----------------------------------------------------------------

void CTextInputCtrl::SetFont(HWND hwndParent)
{
    CHOOSEFONT  cf;
    LOGFONT     lf = _lfCurrentFont;

    cf.lStructSize    = sizeof(CHOOSEFONT);
    cf.hwndOwner      = hwndParent;
    cf.lpLogFont      = &lf;
    cf.Flags          = CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS;
    cf.rgbColors      = RGB(0, 0, 0);
    cf.lCustData      = 0;
    cf.lpfnHook       = NULL;
    cf.lpTemplateName = NULL;
    cf.hInstance      = NULL;
    cf.lpszStyle      = NULL;
    cf.nFontType      = SCREEN_FONTTYPE;
    cf.nSizeMin       = 0;
    cf.nSizeMax       = 0;

    if (ChooseFont(&cf))
    {
        _lfCurrentFont = lf;
        InvalidateRect(_hwnd, NULL, TRUE);
    }

}
