/****************************************************************************
*                                                                           *
* Project64 - A Nintendo 64 emulator.                                      *
* http://www.pj64-emu.com/                                                  *
* Copyright (C) 2012 Project64. All rights reserved.                        *
*                                                                           *
* License:                                                                  *
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html                        *
*                                                                           *
****************************************************************************/
#include "stdafx.h"
#include "EditNumber32.h"

CEditNumber32::CEditNumber32(void) :
    m_DisplayType(DisplayDec)
{
}

CEditNumber32::~CEditNumber32(void)
{
}

bool CEditNumber32::IsHexConvertableText(LPTSTR _text)
{
    int start, end;
    GetSel(start, end);

    wchar_t WindowText[200];
    GetWindowText(WindowText, sizeof(WindowText) / sizeof(WindowText[0]));

    bool bPaste = true;
    size_t Len = wcslen(WindowText);
    wchar_t head = Len > 0 ? WindowText[0] : 0;
    wchar_t second = Len > 1 ? WindowText[1] : 0;

    if (second == 'X' || second == 'x')
    {
        if (end <= 1)
        {
            bPaste = false;
        }
    }
    if (!bPaste) { return bPaste; }
    //Check
    unsigned int i = 0;
    if (wcslen(_text) >= 2)
    {
        if (_text[0] == L'0' && (_text[1] == L'x' || _text[1] == L'X'))
        {
            if ((second == L'x' || second == L'X') && (!(start == 0 && end >= 2)))
            {
                bPaste = false;
            }
            else if (start > 0)
            {
                bPaste = false;
            }
            else
            {
                i += 2;
            }
        }
    }
    if (!bPaste) return bPaste;
    if (wcslen(_text) >= 1)
    {
        if (head == L'0' && (_text[0] == L'x' || _text[0] == L'X'))
        {
            i++;
        }
        if ((_text[0] == L'x' || _text[0] == L'X'))
        {
            if (head != L'0' && start == 0)
            {
                bPaste = false;
            }
            else if (!(start == 1 && end >= 1 && head == L'0'))
            {
                bPaste = false;
            }
        }
    }
    if (!bPaste) return bPaste;
    for (; i < wcslen(_text); i++)
    {
        wchar_t c = _text[i];
        if (!(c >= 48 && c <= 57 || c >= L'A'&&c <= L'F' || c >= L'a'&&c <= L'f' || c == L' '))
        {
            bPaste = false;
            break;
        }
    }
    return bPaste;
}

void CEditNumber32::FormatClipboard()
{
    LPTSTR  lptstr, lptstrCopy;
    HGLOBAL hglb;
    if (!this->OpenClipboard() || !IsClipboardFormatAvailable(CF_UNICODETEXT))
    {
        return;
    }
    hglb = GetClipboardData(CF_UNICODETEXT);
    if (hglb != NULL)
    {
        lptstr = (LPTSTR)GlobalLock(hglb);
        for (unsigned int i = 0; i < wcslen(lptstr); i++)
        {
            if (lptstr[i] != L'X'&&lptstr[i] != L'x')
            {
                lptstr[i] = (char)toupper(lptstr[i]);
            }
            if (lptstr[i] == L'X')
            {
                lptstr[i] = L'x';
            }
            if (lptstr[i] == ' ' && (i < wcslen(lptstr)))
            {
                wcscpy(&lptstr[i], &lptstr[i + 1]);
            }
        }
        hglb = GlobalAlloc(GMEM_MOVEABLE, (wcslen(lptstr) + 1) * sizeof(TCHAR));
        if (hglb == NULL)
        {
            CloseClipboard();
            return;
        }
        lptstrCopy = (LPTSTR)GlobalLock(hglb);
        memcpy(lptstrCopy, lptstr, (wcslen(lptstr) + 1) * sizeof(TCHAR));
        GlobalUnlock(lptstr);
        GlobalUnlock(hglb);
        SetClipboardData(CF_UNICODETEXT, hglb);
        CloseClipboard();
    }
}

LRESULT CEditNumber32::OnValidateValue(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
{
    bHandled = true;
    return true;
}

LRESULT CEditNumber32::OnPaste(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
{
    //Paste
    bHandled = false;

    if (!IsClipboardFormatAvailable(CF_UNICODETEXT))
    {
        bHandled = true;
        return true;
    }
    if (!OpenClipboard())
    {
        bHandled = true;
        return true;
    }

    HGLOBAL hglb = GetClipboardData(CF_UNICODETEXT);
    if (hglb != NULL)
    {
        LPTSTR  lptstr = (LPTSTR)GlobalLock(hglb);
        //Check invalid hex string
        if (!IsHexConvertableText(lptstr))
        {
            bHandled = true;
        }
        GlobalUnlock(lptstr);
    }
    CloseClipboard();
    if (!bHandled)
    {
        FormatClipboard();
    }
    return true;
}

LRESULT CEditNumber32::OnKeyDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    int start, end;
    GetSel(start, end);
    wchar_t WindowText[200];
    GetWindowText(WindowText, sizeof(WindowText) / sizeof(WindowText[0]));
    size_t Len = wcslen(WindowText);

    wchar_t head = Len > 0 ? WindowText[0] : 0;
    wchar_t second = Len > 1 ? WindowText[1] : 0;

    if (uMsg == WM_CHAR)
    {
        size_t MaxLen = 30;

        if (m_DisplayType == DisplayHex)
        {
            MaxLen = 8;
            if (second == L'x' || second == L'X')
            {
                MaxLen += 2;
            }
        }
        wchar_t c = (wchar_t)wParam;
        if (wParam < 32)
        {
            if (wParam == 8 && (second == L'x' || second == L'X') && head == L'0' && end == 1)
            {
                //does not allow to delete '0' before x
                bHandled = true;
            }
            else {
                bHandled = false;
            }
            return TRUE;
        }

        if (second == L'x' || second == L'X')
        {
            //does not allow to change head except select includes first and second
            if (start <= 1 && end <= 1)
            {
                bHandled = true;
                return TRUE;
            }
        }
        if (start == 1 && (c == L'X' || c == L'x') && head == L'0')
        {
            if (c == L'X')
            {
                SendMessage(uMsg, L'x', lParam);
                bHandled = true;
            }
            else {
                bHandled = false;
            }
            return true;
        }
        else if (c >= L'0' && c <= L'9' || c >= L'A' && c <= L'F')
        {
            if (Len >= MaxLen && start == end)
            {
                bHandled = true;
                return true;
            }
            bHandled = false;
            return true;
        }
        else if (c >= L'a' && c <= L'f')
        {
            if (Len >= MaxLen && start == end)
            {
                bHandled = true;
                return true;
            }
            SendMessage(uMsg, wParam - 32, lParam);
            bHandled = true;
            return true;
        }
        bHandled = true;
        return true;
    }

    bHandled = false;
    return false;
}

BOOL CEditNumber32::Attach(HWND hWndNew)
{
    return SubclassWindow(hWndNew);
}

BOOL CEditNumber32::AttachToDlgItem(HWND parent, UINT dlgID)
{
    return SubclassWindow(::GetDlgItem(parent, dlgID));
}

void CEditNumber32::SetDisplayType(DisplayType Type)
{
    DWORD lCurrentValue = GetValue();
    m_DisplayType = Type;
    SetValue(lCurrentValue);
}

uint32_t CEditNumber32::GetValue(void)
{
    wchar_t text[200];
    GetWindowText(text, sizeof(text) / sizeof(text[0]));
    if (m_DisplayType == DisplayDec)
    {
        return _wtoi(text);
    }

    size_t Finish = wcslen(text);
    wchar_t second = Finish > 1 ? text[1] : 0;
    size_t Start = (second == L'x' || second == L'X') ? 2 : 0;

    if (Finish > 8 + Start) { Finish = 8 + Start; }

    DWORD Value = 0;
    for (size_t i = Start; i < Finish; i++)
    {
        Value = (Value << 4);
        switch (text[i])
        {
        case L'0': break;
        case L'1': Value += 1; break;
        case L'2': Value += 2; break;
        case L'3': Value += 3; break;
        case L'4': Value += 4; break;
        case L'5': Value += 5; break;
        case L'6': Value += 6; break;
        case L'7': Value += 7; break;
        case L'8': Value += 8; break;
        case L'9': Value += 9; break;
        case L'A': Value += 10; break;
        case L'a': Value += 10; break;
        case L'B': Value += 11; break;
        case L'b': Value += 11; break;
        case L'C': Value += 12; break;
        case L'c': Value += 12; break;
        case L'D': Value += 13; break;
        case L'd': Value += 13; break;
        case L'E': Value += 14; break;
        case L'e': Value += 14; break;
        case L'F': Value += 15; break;
        case L'f': Value += 15; break;
        default:
            Value = (Value >> 4);
            i = Finish;
        }
    }
    return Value;
}

stdstr CEditNumber32::GetValueText(void)
{
    return ::GetCWindowText(*this);
}

void CEditNumber32::SetValue(uint32_t Value, DisplayMode Display)
{
    stdstr text;
    if (m_DisplayType == DisplayDec)
    {
        text.Format("%d", Value);
    }
    else
    {
        text.Format("%s%0*X",(Display & DisplayMode::ShowHexIdent) == DisplayMode::ShowHexIdent ? "0x" : "", (Display & DisplayMode::ZeroExtend) == DisplayMode::ZeroExtend ? 8 : 0, Value);
    }
    SetWindowText(text.ToUTF16().c_str());
}