#include "EditBox.h"
#include <sstream>
#include <windows.h>

class EditBox::Caret : public Sprite {
public:
    Caret(Window* parent, Text* text);
    virtual ~Caret();

    uint32 getTextPosition();
    void setTextPosition(uint32 textPosition);

protected:
    Text*   _text;
    uint32  _textPosition;
    Window* _parent;
};

EditBox::Caret::Caret(Window* parent, Text* text)
: Sprite(), _parent(parent), _text(text), _textPosition(0)
{
    _texture = ResourceManager::instance().loadTexture(L"textures/gui/caret.dds");
}

EditBox::Caret::~Caret()
{
}

uint32 EditBox::Caret::getTextPosition()
{
    return _textPosition;
}

void EditBox::Caret::setTextPosition(uint32 textPosition)
{
    _textPosition = textPosition;

    DWRITE_HIT_TEST_METRICS hitTestMetrics;
    ZeroMemory(&hitTestMetrics, sizeof(DWRITE_HIT_TEST_METRICS));

    float caretX = 0.0f;
    float caretY = 0.0f;

    _text->getTextLayout()->HitTestTextPosition(
        _textPosition,
        false,
        &caretX,
        &caretY,
        &hitTestMetrics
        );

    Size parentSize = _parent->getSize();
    setPosition(Vector3(caretX / parentSize.w(), 3.0f / parentSize.h(), 1.0f));
}


EditBox::EditBox(Window* parent)
: Window(parent), _caret(0), _caretPosition(0), _normalTexture(0), _disabledTexture(0)
{
    setPosition(Point(0.0f, 0.0f));
    setSize(Size(128.0f, 22.0f));

    _normalTexture = ResourceManager::instance().loadTexture(L"textures/gui/listbox.dds");;
    _disabledTexture = ResourceManager::instance().loadTexture(L"textures/gui/listbox_disabled.dds");

    _texture = _normalTexture;

    _caption->setFont(L"Microsoft Sans Serif", L"000000", 13.0f, Text::FONT_STYLE_REGULAR, Text::H_ALIGN_LEFT | Text::V_ALIGN_CENTER);

    _caret = new Caret(this, _caption);
    _caret->setPosition(Vector3(0.0f, 3.0f / _size.h(), 0.0f));
    _caret->setScale(Vector3(1.0f / _size.w(), 16.0f / _size.h(), 1.0f));
    _caret->setVisible(false);
    Sprite::addChild(_caret);

    connectEvent(Event::MOUSE_ENTER,  this, (EventHandler)&EditBox::onMouseEnter);
    connectEvent(Event::MOUSE_LEAVE,  this, (EventHandler)&EditBox::onMouseLeave);
    connectEvent(Event::FOCUS_GAINED, this, (EventHandler)&EditBox::onGainFocus);
    connectEvent(Event::FOCUS_LOST,   this, (EventHandler)&EditBox::onLoseFocus);
    connectEvent(Event::KEY_DOWN,     this, (EventHandler)&EditBox::onKeyDown);
    connectEvent(Event::CHAR,         this, (EventHandler)&EditBox::onChar);
}

EditBox::~EditBox()
{
}

const wchar_t* EditBox::getValue()
{
    return _text.c_str();
}

void EditBox::setValue(const wchar_t* value)
{
    _text = value;
    _caption->setText(value);
}

Text* EditBox::getCaption()
{
    return _caption;
}

void EditBox::setCaption(const wchar_t* value)
{
    _text = value;
    _caption->setText(value);
}


void EditBox::onGainFocus(Event& event)
{
    _caret->setVisible(true);
}

void EditBox::onLoseFocus(Event& event)
{
    _caret->setVisible(false);
}

void EditBox::onMouseEnter(Event& event)
{
    //SetCursor(LoadCursor(0, IDC_IBEAM));
}

void EditBox::onMouseLeave(Event& event)
{
    //SetCursor(LoadCursor(0, IDC_ARROW));
}

void EditBox::onKeyDown(Event& event)
{
    switch (event.keyCode) {
        case VK_BACK:
            if (_caretPosition > 0) {
                _text = _text.substr(0, _caretPosition - 1) + _text.substr(_caretPosition, _text.length() - _caretPosition);
                _caretPosition--;
            }

            _caption->setText(_text.c_str());
            _caret->setTextPosition(_caretPosition);
            break;

        case VK_DELETE:
            if (_caretPosition < _text.length()) {
                _text = _text.substr(0, _caretPosition) + _text.substr(_caretPosition + 1, _text.length() - _caretPosition - 1);
            }

            _caption->setText(_text.c_str());
            _caret->setTextPosition(_caretPosition);
            break;

        case VK_LEFT:
            if (_caretPosition > 0)
                _caretPosition--;

            _caret->setTextPosition(_caretPosition);
            break;

        case VK_RIGHT:
            if (_caretPosition < (uint32)_text.length())
                _caretPosition++;

            _caret->setTextPosition(_caretPosition);
            break;

        case VK_RETURN:
            break;
    }
}

void EditBox::onChar(Event& event)
{
     // Allow normal characters
    if (event.keyCode >= 0x20)
    {
        wchar_t buffer[] = { (wchar_t)event.keyCode, 0, 0 };

        // If above the basic multi-lingual plane, split into
        // leading and trailing surrogates.
        if (event.keyCode > 0xFFFF)
        {
            // From http://unicode.org/faq/utf_bom.html#35
            buffer[0] = wchar_t(0xD800 + (event.keyCode >> 10)  - (0x10000 >> 10));
            buffer[1] = wchar_t(0xDC00 + (event.keyCode & 0x3FF));
        }

        _text.insert(_caretPosition, buffer);
        _caretPosition += (uint32)wcslen(buffer);

        _caption->setText(_text.c_str());
        _caret->setTextPosition(_caretPosition);
    }

    // Convert tabs to spaces
    else if (event.keyCode == 9) {
        _text.insert(_caretPosition, L"    ");
        _caretPosition += 4;

        _caption->setText(_text.c_str());
        _caret->setTextPosition(_caretPosition);
    }
}