#include "WindowManager.h"
#include <windows.h>


WindowManager::WindowManager()
: _windowGraph(0), _cursor(0), _focus(0), _topLevelFocus(0), _rollover(0), _dragging(0), _pressed(0)
{
    POINT cursorPos;
    GetCursorPos(&cursorPos);

    ScreenToClient(GetActiveWindow(), &cursorPos);

    _cursor.x() = (float)cursorPos.x;
    _cursor.y() = (float)cursorPos.y;


    _windowGraph = new Group();
}

WindowManager::~WindowManager()
{
    delete _windowGraph;
}


Node* WindowManager::getWindowGraph()
{
    return _windowGraph;
}

void WindowManager::addWindow(Window* window)
{
    _windowGraph->addChild(window);
}

void WindowManager::removeWindow(Window* window)
{
    _windowGraph->removeChild(window);
}

void WindowManager::dispatchEvent(Event& event)
{
    if ((event.type == Event::MOUSE_LEFT_UP) && (_dragging != 0)) {
        Event evtDragEnd = event;
        evtDragEnd.type = Event::DRAG_END;
        _dragging->handleEvent(evtDragEnd);

        _dragging = 0;
    }

    if ((event.type == Event::KEY_DOWN) || (event.type == Event::KEY_UP) || (event.type == Event::CHAR)) {
        if (_focus != 0)
            _focus->handleEvent(event);
    }

    else {
        Window *topLevelWindow = 0, *hitWindow = 0;

        for (std::list<Node*>::reverse_iterator iWindow = _windowGraph->getChildren().rbegin(); iWindow != _windowGraph->getChildren().rend(); ++iWindow) {
            Window* window = (Window*)(*iWindow);

            hitWindow = window->getHitWindow(event.mouseX, event.mouseY);

            if (hitWindow != 0) {
                topLevelWindow = window;
                break;
            }
        }

        if (event.type == Event::MOUSE_MOVE) {
            _cursor.x() = event.mouseX;
            _cursor.y() = event.mouseY;

            if (_dragging != 0) {
                Point pos = _dragPivotWindow + (_cursor - _dragPivotCursor);

                if ((_dragging->isDragBound()) && (_dragging->getParent() != 0)) {
                    Size size = _dragging->getSize();
                    Size parentSize = _dragging->getParent()->getSize();

                    if (pos.x() < 0) { pos.x() = 0; }
                    if (pos.y() < 0) { pos.y() = 0; }
                    if (pos.x() + size.w() > parentSize.w()) { pos.x() = parentSize.w() - size.w(); }
                    if (pos.y() + size.h() > parentSize.h()) { pos.y() = parentSize.h() - size.h(); }
                }
                
                _windowGraph->lock();

                _dragging->setPosition(pos);
                
                Event evtDrag = event;
                evtDrag.type = Event::DRAG;
                _dragging->handleEvent(evtDrag);

                _windowGraph->unlock();
            }

            else {
                if (hitWindow != _rollover) {
                    if (_rollover != 0) {
                        Event newEvent = event;
                        newEvent.type = Event::MOUSE_LEAVE;
                        _rollover->handleEvent(newEvent);

                        if (_pressed == _rollover) {
                            _pressed = 0;
                        }
                    }

                    _rollover = hitWindow;

                    if (_rollover != 0) {
                        Event newEvent = event;
                        newEvent.type = Event::MOUSE_ENTER;
                        _rollover->handleEvent(newEvent);
                    }
                }
            }
        }

        else if ((event.type == Event::MOUSE_LEFT_DOWN) || (event.type == Event::MOUSE_MIDDLE_DOWN) || (event.type == Event::MOUSE_RIGHT_DOWN)) {
            Event evtFocusLost = event;
            evtFocusLost.type = Event::FOCUS_LOST;

            Event evtFocusGained = event;
            evtFocusGained.type = Event::FOCUS_GAINED;
            
            if ((_topLevelFocus != 0) && (_topLevelFocus != topLevelWindow))
                _topLevelFocus->handleEvent(evtFocusLost);

            if (_topLevelFocus != _focus) {
                if ((_focus != 0) && (_focus != hitWindow)) {
                    _focus->handleEvent(evtFocusLost);
                }
            }
            
            if ((topLevelWindow != 0) && (topLevelWindow != _topLevelFocus)) {
                _windowGraph->lock();
                _windowGraph->getChildren().remove(topLevelWindow);
                _windowGraph->getChildren().push_back(topLevelWindow);
                _windowGraph->unlock();              
                topLevelWindow->handleEvent(evtFocusGained);
            }

            if (topLevelWindow != hitWindow) {
                if ((hitWindow != 0) && (hitWindow != _focus)) {
                    hitWindow->handleEvent(evtFocusGained);
                }
            }
            
            _focus = hitWindow;
            _topLevelFocus = topLevelWindow;


            if ((event.type == Event::MOUSE_LEFT_DOWN) && (hitWindow != 0)) {
                if (hitWindow->isDraggable() && hitWindow->isEnabled()) {
                    _dragging = hitWindow;

                    _dragPivotWindow = _dragging->getPosition();

                    _dragPivotCursor.x() = event.mouseX;
                    _dragPivotCursor.y() = event.mouseY;

                    Event evtDragBegin = event;
                    evtDragBegin.type = Event::DRAG_BEGIN;
                    _dragging->handleEvent(evtDragBegin);
                }
            }
        }

        if (hitWindow != 0) {
            if (event.type == Event::MOUSE_LEFT_DOWN)
                _pressed = hitWindow;

            if ((event.type == Event::MOUSE_LEFT_UP) && (_pressed == hitWindow)) {
                Event leftClickEvent = event;
                leftClickEvent.type = Event::MOUSE_LEFT_CLICK;
                if (_pressed->isEnabled())
                    _pressed->handleEvent(leftClickEvent);
            }
        }

        if (hitWindow != 0)
            hitWindow->handleEvent(event);
    }
}
