#include "App.h"


App& App::instance()
{
    static App app;
    return app;
}

App::App()
: _appConfig(AppConfig::instance()), _deviceRegistry(DeviceRegistry::instance()), _active(true), _windowManager(0), _renderDevice(0),
_background(0), _menuBar(0), _cameraManipulator(0), _dirLight(0), _inSizeMove(false)
{
    ZeroMemory(&_dmScreenSettings, sizeof(_dmScreenSettings));
	_dmScreenSettings.dmSize = sizeof(_dmScreenSettings);
}

void App::onActivate(bool status)
{
    if ((_active == false) && (status == true)) {
        _active = true;

        if (_fullscreen) {
            ChangeDisplaySettings(&_dmScreenSettings, CDS_FULLSCREEN);
            ShowCursor(FALSE);
        }

        _renderDevice->unpause();
    }

    else if ((_active == true) && (status == false)) {
        _active = false;

        _renderDevice->pause();

        if (_fullscreen) {
            CloseWindow(_hWnd);
            ChangeDisplaySettings(0, 0);
		    ShowCursor(TRUE);
        }
    }
}

void App::onSize(int width, int height)
{
    _dmScreenSettings.dmPelsWidth = width;
    _dmScreenSettings.dmPelsHeight = height;

    _renderDevice->resizeCanvas(&_dmScreenSettings);

    _background->setScale(Vector3((float)width, (float)height, 1.0f));
    _menuBar->setSize(Size((float)width, 24.0f));
}

HRESULT App::createWindow()
{
    WNDCLASSEX wcex;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    wcex.lpfnWndProc = (WNDPROC)&App::wndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = _hInstance;
    wcex.hIcon = LoadIcon(_hInstance, (LPCTSTR)IDI_ICON1);
    wcex.hIcon = 0;
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = NULL; //(HBRUSH)GetStockObject(BLACK_BRUSH);
    wcex.lpszMenuName = NULL;
    wcex.lpszClassName = L"NequamWindowClass";
    wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_ICON1);
   
    if(!RegisterClassEx(&wcex))
        return E_FAIL;


    try {
        _dmScreenSettings.dmPelsWidth        = _appConfig.get<DWORD> (L"Display", L"Width");
        _dmScreenSettings.dmPelsHeight       = _appConfig.get<DWORD> (L"Display", L"Height");
        _dmScreenSettings.dmBitsPerPel       = _appConfig.get<DWORD> (L"Display", L"Depth");
        _dmScreenSettings.dmDisplayFrequency = _appConfig.get<DWORD> (L"Display", L"RefreshRate");
        _fullscreen                          = _appConfig.get<bool>  (L"Display", L"Fullscreen");
    }
    catch(AppConfig::InvalidEntryException&) {
        EnumDisplaySettings(0, ENUM_CURRENT_SETTINGS, &_dmScreenSettings);
        _fullscreen = true;
    }

    _dmScreenSettings.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY;

    if (_fullscreen) { 
        HRESULT hResult = ChangeDisplaySettings(&_dmScreenSettings, CDS_FULLSCREEN);
        
        if (hResult != DISP_CHANGE_SUCCESSFUL) {

            MessageBox(NULL, L"Unable to set requested fullscreen mode. Attempting (assumed) native display resolution (current desktop resolution).", L"Error", MB_OK | MB_ICONERROR);
            
            EnumDisplaySettings(0, ENUM_CURRENT_SETTINGS, &_dmScreenSettings);
            _dmScreenSettings.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY;

            HRESULT hResult = ChangeDisplaySettings(&_dmScreenSettings, CDS_FULLSCREEN);
        
            if (hResult != DISP_CHANGE_SUCCESSFUL) {
                MessageBox(NULL, L"Unable to set requested fullscreen mode. Reverting to windowed mode.", L"Error", MB_OK | MB_ICONERROR);

                _dmScreenSettings.dmPelsWidth = 800;
                _dmScreenSettings.dmPelsHeight = 600;
                _fullscreen = false;
            }
        }
    }
    
    _appConfig.set<DWORD> (L"Display", L"Width",       _dmScreenSettings.dmPelsWidth);
    _appConfig.set<DWORD> (L"Display", L"Height",      _dmScreenSettings.dmPelsHeight);
    _appConfig.set<DWORD> (L"Display", L"Depth",       _dmScreenSettings.dmBitsPerPel);
    _appConfig.set<DWORD> (L"Display", L"RefreshRate", _dmScreenSettings.dmDisplayFrequency);
    _appConfig.set<bool>  (L"Display", L"Fullscreen",  _fullscreen);


    DWORD dwExStyle = 0, dwStyle = 0;

    if (_fullscreen) {
		dwExStyle = WS_EX_APPWINDOW;							
		dwStyle = WS_POPUP;						
	}
	else {
		dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;	
		dwStyle = WS_OVERLAPPEDWINDOW;
	}

    RECT windowRect;
	windowRect.left   = (LONG) 0;
	windowRect.right  = (LONG) _dmScreenSettings.dmPelsWidth;
	windowRect.top    = (LONG) 0;
	windowRect.bottom = (LONG) _dmScreenSettings.dmPelsHeight;

    AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle);

    _hWnd = CreateWindowEx(	dwExStyle,
                            L"NequamWindowClass",
							L"Nequam",
							dwStyle | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
							0, 0,
							windowRect.right - windowRect.left,
							windowRect.bottom - windowRect.top,
							NULL,
							NULL,
							_hInstance,
							NULL );

    if(!_hWnd)
        return E_FAIL;

    ShowWindow(_hWnd, _nCmdShow);

    return S_OK;
}

void App::destroyWindow()
{
    DestroyWindow(_hWnd);
	UnregisterClass(L"NequamWindowClass", _hInstance);

    if (_fullscreen) {
		ChangeDisplaySettings(0, 0);
		ShowCursor(TRUE);
	}
}


LRESULT CALLBACK App::wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    static App& app = App::instance();

    Event event;
    ZeroMemory(&event, sizeof(event));

    RECT windowRect;
    ZeroMemory(&windowRect, sizeof(windowRect));



    switch(msg) {
        case WM_DESTROY:
            PostQuitMessage(0);
            break;

        case WM_KEYDOWN:
            event.type = Event::KEY_DOWN;
            event.keyCode = (int)wParam;
            app._windowManager->dispatchEvent(event);
            if (app._cameraManipulator != 0) app._cameraManipulator->handleEvent(event);
            break;

        case WM_KEYUP:
            event.type = Event::KEY_UP;
            event.keyCode = (int)wParam;
            app._windowManager->dispatchEvent(event);
            if (app._cameraManipulator != 0) app._cameraManipulator->handleEvent(event);
            break;

        case WM_CHAR:
            event.type = Event::CHAR;
            event.keyCode = (int)wParam;
            app._windowManager->dispatchEvent(event);
            if (app._cameraManipulator != 0) app._cameraManipulator->handleEvent(event);
            break;

        case WM_LBUTTONDOWN:
            event.type = Event::MOUSE_LEFT_DOWN;
            event.mouseX = (float)LOWORD(lParam); 
            event.mouseY = (float)HIWORD(lParam);
            app._windowManager->dispatchEvent(event);
            if (app._cameraManipulator != 0) app._cameraManipulator->handleEvent(event);
            break;

        case WM_LBUTTONUP:
            event.type = Event::MOUSE_LEFT_UP;
            event.mouseX = (float)LOWORD(lParam); 
            event.mouseY = (float)HIWORD(lParam);
            app._windowManager->dispatchEvent(event);
            if (app._cameraManipulator != 0) app._cameraManipulator->handleEvent(event);
            break;

        case WM_RBUTTONDOWN:
            event.type = Event::MOUSE_RIGHT_DOWN;
            event.mouseX = (float)LOWORD(lParam); 
            event.mouseY = (float)HIWORD(lParam);
            app._windowManager->dispatchEvent(event);
            if (app._cameraManipulator != 0) app._cameraManipulator->handleEvent(event);
            break;

        case WM_RBUTTONUP:
            event.type = Event::MOUSE_RIGHT_UP;
            event.mouseX = (float)LOWORD(lParam); 
            event.mouseY = (float)HIWORD(lParam);
            app._windowManager->dispatchEvent(event);
            if (app._cameraManipulator != 0) app._cameraManipulator->handleEvent(event);
            break;

        case WM_MBUTTONDOWN:
            event.type = Event::MOUSE_MIDDLE_DOWN;
            event.mouseX = (float)LOWORD(lParam); 
            event.mouseY = (float)HIWORD(lParam);
            app._windowManager->dispatchEvent(event);
            if (app._cameraManipulator != 0) app._cameraManipulator->handleEvent(event);
            break;

        case WM_MBUTTONUP:
            event.type = Event::MOUSE_MIDDLE_UP;
            event.mouseX = (float)LOWORD(lParam); 
            event.mouseY = (float)HIWORD(lParam);
            app._windowManager->dispatchEvent(event);
            if (app._cameraManipulator != 0) app._cameraManipulator->handleEvent(event);
            break;

        case WM_MOUSEWHEEL:
            event.type = Event::MOUSE_WHEEL;
            event.mouseX = (float)LOWORD(lParam); 
            event.mouseY = (float)HIWORD(lParam);
            event.scroll = ((float)(HIWORD(wParam))) * ((float)WHEEL_DELTA);
            app._windowManager->dispatchEvent(event);
            if (app._cameraManipulator != 0) app._cameraManipulator->handleEvent(event);
            break;

        case WM_MOUSEMOVE:
            event.type = Event::MOUSE_MOVE;
            event.mouseX = (float)LOWORD(lParam); 
            event.mouseY = (float)HIWORD(lParam);
            app._windowManager->dispatchEvent(event);
            if (app._cameraManipulator != 0) app._cameraManipulator->handleEvent(event);
            break;

        case WM_ACTIVATEAPP:
            if (wParam == TRUE)
                app.onActivate(true);
            else
                app.onActivate(false);
            break;

            
        case WM_SIZE:
            GetClientRect(hWnd, &windowRect);
          
            if (!app._inSizeMove) {
                app.onSize(windowRect.right - windowRect.left, windowRect.bottom - windowRect.top);
            }

            break;
            

        case WM_ENTERSIZEMOVE:
            app._inSizeMove = true;
            break;

        case WM_EXITSIZEMOVE:
            GetClientRect(hWnd, &windowRect);
            app.onSize(windowRect.right - windowRect.left, windowRect.bottom - windowRect.top);
            app._inSizeMove = false;
            break;




        default:
            return DefWindowProc(hWnd, msg, wParam, lParam);
    }

    return 0;
}


int App::run(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
    _hInstance = hInstance;
    _hPrevInstance = hPrevInstance;
    _lpCmdLine = lpCmdLine;
    _nCmdShow = nCmdShow;

    _windowManager = new WindowManager();

    std::wstring renderDeviceName;

    try {
        renderDeviceName = _appConfig.get<std::wstring>(L"Display", L"RenderDevice");
    }
    catch (AppConfig::InvalidEntryException&) {
        renderDeviceName = L"D3DDevice";
        _appConfig.set<std::wstring>(L"Display", L"RenderDevice", renderDeviceName);
    }

    _renderDevice = dynamic_cast<RenderDevice*>(_deviceRegistry.acquireDevice(renderDeviceName.c_str()));

    if (_renderDevice == NULL) {
        if (renderDeviceName != L"D3DDevice") {
            MessageBox(NULL, L"Unable to acquire requested render device. Switching to default.", L"Error", MB_OK | MB_ICONERROR);

            renderDeviceName = L"D3DDevice";
            _appConfig.set<std::wstring>(L"Display", L"RenderDevice", renderDeviceName);

            _renderDevice = dynamic_cast<RenderDevice*>(_deviceRegistry.acquireDevice(renderDeviceName.c_str()));
        }

        if (_renderDevice == NULL) {
            MessageBox(NULL, L"Unable to acquire requested render device.", L"Error", MB_OK | MB_ICONERROR);
            return 0;
        }
    }

    if (FAILED(createWindow())) {
        MessageBox(NULL, L"Unable to create application window.", L"Error", MB_OK | MB_ICONERROR);
        return 0;
    }

    _renderDevice->init(_hWnd, &_dmScreenSettings);



    _background = new Sprite();
    _background->setVisible(false);
    _background->setPosition(Vector3(0.0f, 0.0f, 0.0f));
    _background->setScale(Vector3((float)_dmScreenSettings.dmPelsWidth, (float)_dmScreenSettings.dmPelsHeight, 1.0f));
    _background->setTexture(ResourceManager::instance().loadTexture(L"textures/background.png"));

    
    class BackgroundButton : public Button {
    public:
        BackgroundButton(Sprite* background) : Button(), _background(background) {
            connectEvent(Event::MOUSE_LEFT_CLICK, this, (EventHandler)&BackgroundButton::onLeftClick);
        }

        void onLeftClick(Event& event) { 
            _background->setVisible(!_background->isVisible());
        }

    protected:
        Sprite* _background;
    };
   

    class MyDialog : public Dialog {
    public:
        MyDialog() : Dialog(), 
        _btnOK(0), _btnExit(0), _btnDisabler(0), _chkboxAO(0), 
        _sliderDisplay(0), _slider(0), _stepSliderDisplay(0), _stepSlider(0), 
        _radioGroup1(0), _radioButton1A(0), _radioButton1B(0), _listBox(0),
        _menuBar(0), _editBoxLabel(0), _editBox(0) {
            setPosition(Point(500.0f, 200.0f));
            setSize(Size(400.0f, 600.0f));

            setCaption(L"MyDialog");

            _btnOK = new Button(this);
            _btnExit = new Button(this);
            _btnDisabler = new Button(this);
            _chkboxAO = new CheckBox(this);
            _sliderDisplay = new Label(this);
            _slider = new Slider(this);
            _stepSliderDisplay = new Label(this);
            _stepSlider = new StepSlider(this);
            _radioGroup1 = new RadioGroup(this);
            _radioButton1A = new RadioButton(_radioGroup1, this);
            _radioButton1B = new RadioButton(_radioGroup1, this);
            _listBox = new ListBox(this);
            _editBoxLabel = new Label(this);
            _editBox = new EditBox(this);

            class MyMenuBar : public MenuBar {
            public:
                MyMenuBar(Window* parent) : MenuBar(parent) {}

                void onExit(Event& event) {
                    PostQuitMessage(0);
                }
            };
            _menuBar = new MenuBar(this);

            _btnOK->setPosition(Point(10.0f, 36.0f));
            _btnOK->setCaption(L"DlgOK");

            _btnExit->setPosition(Point(10.0f, 71.0f));
            _btnExit->setCaption(L"DlgExit");

            _btnDisabler->setPosition(Point(10.0f, 100.0f));
            _btnDisabler->setCaption(L"Disable");

            _chkboxAO->setPosition(Point(25.0f, 142.0f));
            _chkboxAO->setSize(Size(128.0f, 24.0f));
            _chkboxAO->setCaption(L"Ambient occlusion");

            _sliderDisplay->setPosition(Point(25.0f, 184.0f));
            _sliderDisplay->setCaption(L"0%");
            _slider->setPosition(Point(25.0f, 202.0f));

            _stepSliderDisplay->setPosition(Point(25.0f, 245.0f));
            _stepSliderDisplay->setSize(Size(128.0f, 16.0f));
            _stepSliderDisplay->setCaption(L"3 : [{3, 13}, step = 2]");
            _stepSlider->setPosition(Point(25.0f, 263.0f));
            _stepSlider->setMinValue(3);
            _stepSlider->setMaxValue(13);
            _stepSlider->setStep(2);

            _radioGroup1->setPosition(Point(10.0f, 321.0f));
            _radioGroup1->setSize(Size(100.0f, 16.0f));
            _radioGroup1->setCaption(L"Select an option:");
            _radioButton1A->setPosition(Point(25.0f, 342.0f));
            _radioButton1A->setSize(Size(80.0f, 24.0f));
            _radioButton1A->setCaption(L"Choice A");
            _radioButton1B->setPosition(Point(25.0f, 366.0f));
            _radioButton1B->setSize(Size(80.0f, 24.0f));
            _radioButton1B->setCaption(L"Choice B");

            _listBox->setPosition(Point(25.0f, 410.0f));
            _listBox->addItem(L"one");
            _listBox->addItem(L"two");
            _listBox->addItem(L"three");
            _listBox->addItem(L"four");

            Menu* fileMenu = new Menu(L"File");
            MenuItem* menuItemNew = new MenuItem(L"New");
            fileMenu->addItem(menuItemNew);
            MenuItem* menuItemOpen = new MenuItem(L"Open");
            fileMenu->addItem(menuItemOpen);
            MenuItem* menuItemClose = new MenuItem(L"Close");
            fileMenu->addItem(menuItemClose);
            MenuItem* menuItemExit = new MenuItem(L"Exit");
            fileMenu->addItem(menuItemExit);
            _menuBar->addMenu(fileMenu);
            _menuBar->connectEvent(Event::MOUSE_LEFT_CLICK, menuItemExit, (EventHandler)&MyMenuBar::onExit);

            Menu* editMenu = new Menu(L"Edit");
            MenuItem* menuItemUndo = new MenuItem(L"Undo");
            editMenu->addItem(menuItemUndo);
            MenuItem* menuItemRedo = new MenuItem(L"Redo");
            editMenu->addItem(menuItemRedo);
            MenuItem* menuItemCut = new MenuItem(L"Cut");
            editMenu->addItem(menuItemCut);
            MenuItem* menuItemCopy = new MenuItem(L"Copy");
            editMenu->addItem(menuItemCopy);
            MenuItem* menuItemPaste = new MenuItem(L"Paste");
            editMenu->addItem(menuItemPaste);
            _menuBar->addMenu(editMenu);

            Menu* viewMenu = new Menu(L"View");
            MenuItem* menuItemStatus = new MenuItem(L"Status");
            viewMenu->addItem(menuItemStatus);
            MenuItem* menuItemRenderer = new MenuItem(L"Renderer");
            PopupMenu* subMenu = new PopupMenu();
            subMenu->addItem(new MenuItem(L"Info"));
            subMenu->addItem(new MenuItem(L"Options"));
            subMenu->addItem(new MenuItem(L"Debug mode"));
            menuItemRenderer->setSubMenu(subMenu);
            viewMenu->addItem(menuItemRenderer);
            _menuBar->addMenu(viewMenu);

            Menu* helpMenu = new Menu(L"Help");
            _menuBar->addMenu(helpMenu);

            _editBoxLabel->setPosition(Point(200.0f, 71.0f));
            _editBoxLabel->setSize(Size(128.0f, 16.0f));
            _editBoxLabel->setCaption(L"Type some text here:");

            _editBox->setPosition(Point(200.0f, 100.0f));


            connectEvent(Event::MOUSE_LEFT_CLICK, _btnOK,       (EventHandler)&MyDialog::onClickBtnOK);
            connectEvent(Event::MOUSE_LEFT_CLICK, _btnExit,     (EventHandler)&MyDialog::onClickBtnExit);
            connectEvent(Event::MOUSE_LEFT_CLICK, _btnDisabler, (EventHandler)&MyDialog::onClickBtnDisabler);
            connectEvent(Event::VALUE_CHANGED,    _slider,      (EventHandler)&MyDialog::onSliderValueChanged);
            connectEvent(Event::VALUE_CHANGED,    _stepSlider,  (EventHandler)&MyDialog::onStepSliderValueChanged);
            connectEvent(Event::VALUE_CHANGED,    _listBox,     (EventHandler)&MyDialog::onListBoxValueChanged);
        }

        virtual ~MyDialog() {
            delete _btnOK;
            delete _btnExit;
            delete _btnDisabler;
            delete _chkboxAO;
            delete _sliderDisplay;
            delete _slider;
            delete _stepSliderDisplay;
            delete _stepSlider;
            delete _radioGroup1;
            delete _radioButton1A;
            delete _radioButton1B;
            delete _listBox;
            delete _menuBar;
            delete _editBoxLabel;
            delete _editBox;
        }

        void onClickBtnOK(Event& event) { 
            MessageBox(0, L"You've clicked on dialog button OK.", L"Message", MB_OK | MB_ICONINFORMATION);
        }

        void onClickBtnExit(Event& event) { 
            PostQuitMessage(0);
        }

        void onClickBtnDisabler(Event& event) {
            if (_btnOK->isEnabled()) {
                _btnOK->enable(false);
                _btnExit->enable(false);
                _chkboxAO->enable(false);
                _slider->enable(false);
                _stepSlider->enable(false);
                _radioButton1A->enable(false);
                _radioButton1B->enable(false);
                _listBox->enable(false);
                _btnDisabler->setCaption(L"Enable");
            }
            else {
                _btnOK->enable(true);
                _btnExit->enable(true);
                _chkboxAO->enable(true);
                _slider->enable(true);
                _stepSlider->enable(true);
                _radioButton1A->enable(true);
                _radioButton1B->enable(true);
                _listBox->enable(true);
                _btnDisabler->setCaption(L"Disable");
            }
        }

        void onSliderValueChanged(Event& event) {
            int value = (int)(_slider->getValue() * 100.0f);
            std::wstringstream stream;
            stream << value << L"%";
            _sliderDisplay->setCaption(stream.str().c_str());
        }

        void onStepSliderValueChanged(Event& event) {
            int value = _stepSlider->getValue();
            std::wstringstream stream;
            stream << value << L" : [{" << _stepSlider->getMinValue() << L", " << _stepSlider->getMaxValue() << L"}, step = " << _stepSlider->getStep() << L"]";
            _stepSliderDisplay->setCaption(stream.str().c_str());
        }

        void onListBoxValueChanged(Event& event) {
            int selection = _listBox->getSelection();
            MessageBox(0, _listBox->getItem(selection), L"ListBox Selection", MB_OK | MB_ICONINFORMATION);
        }

    protected:
        Button* _btnOK;
        Button* _btnExit;
        Button* _btnDisabler;

        CheckBox* _chkboxAO;

        Label*  _sliderDisplay;
        Slider* _slider;

        Label*      _stepSliderDisplay;
        StepSlider* _stepSlider;

        RadioGroup*  _radioGroup1;
        RadioButton* _radioButton1A;
        RadioButton* _radioButton1B;

        ListBox* _listBox;

        MenuBar* _menuBar;

        Label*   _editBoxLabel;
        EditBox* _editBox;
    };


    class MyMenuBar : public MenuBar {
    public:
        MyMenuBar() : MenuBar() {}

        void onExit(Event& event) {
            PostQuitMessage(0);
        }
    };

    _menuBar = new MyMenuBar();
    _menuBar->setSize(Size((float)_dmScreenSettings.dmPelsWidth, 24.0f));

    Menu* fileMenu = new Menu(L"File");
    MenuItem* menuItemNew = new MenuItem(L"New");
    fileMenu->addItem(menuItemNew);
    MenuItem* menuItemOpen = new MenuItem(L"Open");
    fileMenu->addItem(menuItemOpen);
    MenuItem* menuItemClose = new MenuItem(L"Close");
    fileMenu->addItem(menuItemClose);
    MenuItem* menuItemExit = new MenuItem(L"Exit");
    fileMenu->addItem(menuItemExit);
    _menuBar->addMenu(fileMenu);
    _menuBar->connectEvent(Event::MOUSE_LEFT_CLICK, menuItemExit, (EventHandler)&MyMenuBar::onExit);

    Menu* editMenu = new Menu(L"Edit");
    MenuItem* menuItemUndo = new MenuItem(L"Undo");
    editMenu->addItem(menuItemUndo);
    MenuItem* menuItemRedo = new MenuItem(L"Redo");
    editMenu->addItem(menuItemRedo);
    MenuItem* menuItemCut = new MenuItem(L"Cut");
    editMenu->addItem(menuItemCut);
    MenuItem* menuItemCopy = new MenuItem(L"Copy");
    editMenu->addItem(menuItemCopy);
    MenuItem* menuItemPaste = new MenuItem(L"Paste");
    editMenu->addItem(menuItemPaste);
    _menuBar->addMenu(editMenu);

    Menu* viewMenu = new Menu(L"View");
    MenuItem* menuItemStatus = new MenuItem(L"Status");
    viewMenu->addItem(menuItemStatus);
    MenuItem* menuItemRenderer = new MenuItem(L"Renderer");
    PopupMenu* subMenu = new PopupMenu();
    subMenu->addItem(new MenuItem(L"Info"));
    subMenu->addItem(new MenuItem(L"Options"));
    subMenu->addItem(new MenuItem(L"Debug mode"));
    menuItemRenderer->setSubMenu(subMenu);
    viewMenu->addItem(menuItemRenderer);
    _menuBar->addMenu(viewMenu);

    Menu* helpMenu = new Menu(L"Help");
    _menuBar->addMenu(helpMenu);

    
    Button* testBtn1 = new BackgroundButton(_background);
    testBtn1->setPosition(Point(10.0f, 200.0f));
    testBtn1->setSize(Size(160.0f, 48.0f));
    testBtn1->setCaption(L"Show/hide background");
    //_windowManager->addWindow(testBtn1);
  

    MyDialog* myDialog1 = new MyDialog();
    myDialog1->setPosition(Point(100.0f, 100.0f));
    //_windowManager->addWindow(myDialog1);

    MyDialog* myDialog2 = new MyDialog();
    myDialog2->setPosition(Point(600.0f, 300.0f));
    //_windowManager->addWindow(myDialog2);

    _windowManager->addWindow(_menuBar);


    Transform* r2d2Transform = new Transform();
    r2d2Transform->setPosition(Vector3(0.0f, 0.0f, 0.0f));
    r2d2Transform->setScale(Vector3(0.1f, 0.1f, 0.1f));

    //Transform* sphereTransform = new Transform();
    //sphereTransform->setPosition(Vector3(0.5f, 0.5f, 0.0f));
    //Node* sphere = ResourceManager::instance().loadNode(L"models/sphere.obj");
    //sphereTransform->addChild(sphere);

    Node* r2d2 = ResourceManager::instance().loadNode(L"models/r2d2/r2d2_ng.3ds");
    //Node* r2d2 = ResourceManager::instance().loadNode(L"models/living room.3ds");
    //Node* sponza = ResourceManager::instance().loadNode(L"models/sponza/sponza.3ds");
    //Node* r2d2 = ResourceManager::instance().loadNode(L"models/sibenik/sibenik.3ds");
    //Node* sponza = ResourceManager::instance().loadNode(L"models/sponza2/sponza.obj");
    Node* benchmark = ResourceManager::instance().loadNode(L"models/benchmark.obj");
    Node* hebe = ResourceManager::instance().loadNode(L"models/Hebe.3ds");
    Node* goofyKnight = ResourceManager::instance().loadNode(L"models/goof_knight.x");

    //Node* car = ResourceManager::instance().loadNode(L"models/Beast/Beast.3ds");

    //Node* bigship1 = ResourceManager::instance().loadNode(L"models/bigship1.x");
    //Node* plane = ResourceManager::instance().loadNode(L"models/GroundPlane/GroundPlane.3ds");

    
    
    Transform* goofyKnightTransform = new Transform();
    goofyKnightTransform->setPosition(Vector3(5.0f, 1.0f, -2.0f));
    goofyKnightTransform->setScale(Vector3(0.2f, 0.2f, 0.2f));
    goofyKnightTransform->setOrientation(Quaternion(90.0f, 0.0f, 0.0f));
    goofyKnightTransform->addChild(goofyKnight);
    
    
    r2d2Transform->addChild(benchmark);
    r2d2Transform->addChild(goofyKnightTransform);
    r2d2Transform->addChild(r2d2);
    //r2d2Transform->addChild(plane);
    //r2d2Transform->addChild(car);
    //r2d2Transform->addChild(sponza);
    
    _dirLight = new DirectionalLight();
    _dirLight->setSceneCamera(_renderDevice->getCamera());
    _dirLight->setDirection(Vector3(1.0f, 1.0f, 1.0f));
    _dirLight->setDiffuse(2.68f, 2.68f, 2.68f, 0.0f);
    _dirLight->setSpecular(2.56f, 2.56f, 2.56f, 0.0f);

    Transform* dirLightTransform = new Transform();
    dirLightTransform->addChild(_dirLight);

    PointLight* pointLight = new PointLight();
    pointLight->setShadowed(false);
    pointLight->setSceneCamera(_renderDevice->getCamera());
    pointLight->setPosition(Vector3(0.0f, 0.0f, 0.0f));
    pointLight->setDiffuse(1.0f, 0.2f, 0.2f, 0.0f);
    pointLight->setSpecular(0.8f, 0.1f, 0.1f, 0.0f);
    pointLight->setConstAtten(0.3f);
    pointLight->setLinearAtten(0.2f);
    pointLight->setQuadAtten(0.05f);

    Transform* pointLightTransform = new Transform();
    pointLightTransform->addChild(pointLight);

    SpotLight* spotLight = new SpotLight();
    spotLight->setShadowed(false);
    spotLight->setSceneCamera(_renderDevice->getCamera());
    spotLight->setPosition(Vector3(0.0f, 0.0f, 0.0f));
    spotLight->setDiffuse(0.2f, 0.2f, 1.0f, 0.0f);
    spotLight->setSpecular(0.1f, 0.1f, 0.8f, 0.0f);
    spotLight->setConstAtten(0.2f);
    spotLight->setLinearAtten(0.1f);
    spotLight->setQuadAtten(0.01f);
    spotLight->setDirection(Vector3(1.0f, 0.0f, 0.0f));
    spotLight->setCutoff(0.5f);
    spotLight->setExponent(1.5f);
    
    Transform* spotLightTransform = new Transform();
    spotLightTransform->setPosition(Vector3(10.0f, 10.0f, 0.0f));
    spotLightTransform->addChild(spotLight);
    
    Group* scene = new Group();
    scene->addChild(dirLightTransform);
    scene->addChild(pointLightTransform);
    scene->addChild(spotLightTransform);
    scene->addChild(r2d2Transform);
    //scene->addChild(sphereTransform);
 
    class MyPanel : public Dialog {
    public:
        MyPanel(Transform* dirLightTransform, Transform* pointLightTransform, Transform* spotLightTransform, RenderDevice* renderDevice) 
        : Dialog(0), _dirLightTransform(dirLightTransform), _pointLightTransform(pointLightTransform), _spotLightTransform(spotLightTransform), _renderDevice(renderDevice), 
        _lightThetaSlider(0), _lightPhiSlider(0), _lightDistanceSlider(0), _cbEnableLights(0), _cbEnableShadows(0), _cbEnableTextures(0), _cbEnableAo(0), _cbEnableGi(0) {
            setCaption(L"Options");
            setPosition(Point(0.0f, 0.0f));
            setSize(Size(256.0f, 256.0f));

            _lightThetaSlider = new Slider(this);
            _lightPhiSlider = new Slider(this);
            _lightDistanceSlider = new Slider(this);
            _cbEnableLights = new CheckBox(this);
            _cbEnableShadows = new CheckBox(this);
            _cbEnableTextures = new CheckBox(this);
            _cbEnableAo = new CheckBox(this);
            _cbEnableGi = new CheckBox(this);
            _lightList = new ListBox(this);

            _cbEnableLights->check(true);
            _cbEnableShadows->check(true);
            _cbEnableTextures->check(true);
            _cbEnableAo->check(true);
            _cbEnableGi->check(true);

            _lightThetaSlider->setPosition(Point(10.0f, 10.0f));
            _lightPhiSlider->setPosition(Point(10.0f, 34.0f));
            _lightDistanceSlider->setPosition(Point(10.0f, 59.0f));

            _cbEnableLights->setCaption(L"Enable lights");
            _cbEnableLights->setPosition(Point(10.0f, 83.0f));

            _cbEnableShadows->setCaption(L"Enable shadows");
            _cbEnableShadows->setPosition(Point(114.0f, 83.0f));

            _cbEnableTextures->setCaption(L"Enable textures");
            _cbEnableTextures->setPosition(Point(10.0f, 107.0f));
            
            _cbEnableAo->setCaption(L"Enable SSAO");
            _cbEnableAo->setPosition(Point(10.0f, 131.0f));

            _cbEnableGi->setCaption(L"Enable SSGI");
            _cbEnableGi->setPosition(Point(114.0f, 131.0f));
            
            _lightList->setPosition(Point(10.0f, 160.0f));
            _lightList->addItem(L"Directional light");
            _lightList->addItem(L"Point light");
            _lightList->addItem(L"Spot light");

            _settings[0].a = 0.4f;
            _settings[0].b = 0.7f;
            _settings[0].c = 0.0f;

            _settings[1].a = 0.5f;
            _settings[1].b = 0.6f;
            _settings[1].c = 0.05f;
            
            _settings[2].a = 0.29f;
            _settings[2].b = 0.76f;
            _settings[2].c = 0.25f;
         
            connectEvent(Event::VALUE_CHANGED, _lightThetaSlider, (EventHandler)&MyPanel::onLightSliderValueChanged);
            connectEvent(Event::VALUE_CHANGED, _lightPhiSlider, (EventHandler)&MyPanel::onLightSliderValueChanged);
            connectEvent(Event::VALUE_CHANGED, _lightDistanceSlider, (EventHandler)&MyPanel::onLightSliderValueChanged);

            connectEvent(Event::MOUSE_LEFT_CLICK, _cbEnableLights, (EventHandler)&MyPanel::onEnableLights);
            connectEvent(Event::MOUSE_LEFT_CLICK, _cbEnableShadows, (EventHandler)&MyPanel::onEnableShadows);
            connectEvent(Event::MOUSE_LEFT_CLICK, _cbEnableTextures, (EventHandler)&MyPanel::onEnableTextures);
            connectEvent(Event::MOUSE_LEFT_CLICK, _cbEnableAo, (EventHandler)&MyPanel::onEnableAo);
            connectEvent(Event::MOUSE_LEFT_CLICK, _cbEnableGi, (EventHandler)&MyPanel::onEnableGi);
            
            connectEvent(Event::VALUE_CHANGED, _lightList, (EventHandler)&MyPanel::onListValueChanged);
  
            _lightList->setSelection(0);
            _lightList->setSelection(1);
            _lightList->setSelection(2);
            _lightList->setSelection(0);
        }

        void onListValueChanged(Event& event) {
            int lightID = _lightList->getSelection();
            _lightThetaSlider->setValue(_settings[lightID].a);
            _lightPhiSlider->setValue(_settings[lightID].b);
            _lightDistanceSlider->setValue(_settings[lightID].c);
        }

        void onLightSliderValueChanged(Event& event) {
            Transform* lightTransform = 0;
            int lightID = _lightList->getSelection();

            switch(lightID) {
                case 0:
                    lightTransform = _dirLightTransform;
                    break;

                case 1:
                    lightTransform = _pointLightTransform;
                    break;

                case 2:
                    lightTransform = _spotLightTransform;
                    break;
            }

            if (lightTransform == 0)
                return;

            Vector3 forward(0.0f, 0.0f, 1.0f);
            Vector3 right(1.0f, 0.0f, 0.0f);
            Vector3 up(0.0f, 1.0f, 0.0f);


            float theta = _lightThetaSlider->getValue() * 6.283185307179586476925286766559f;
            float phi = _lightPhiSlider->getValue() * 6.283185307179586476925286766559f;
            float r = (_lightDistanceSlider->getValue() + 0.1f) * 20.0f;

            
            Vector3 direction = forward;
            
            direction = Vector3::rotateAroundAxis(direction, up, theta);
            right = Vector3::rotateAroundAxis(right, up, theta);

            direction = Vector3::rotateAroundAxis(direction, right, phi);

            Quaternion quat;
            quat.fromHeadPitchRoll(theta*57.295779513082320876798154814105f, phi*57.295779513082320876798154814105f, 0.0f);

            lightTransform->setOrientation(quat);
            lightTransform->setPosition(direction * r);
        }

        void onEnableLights(Event& event) {
            _renderDevice->enableLights(_cbEnableLights->isChecked());
        }

        void onEnableShadows(Event& event) {
            _renderDevice->enableShadows(_cbEnableShadows->isChecked());
        }

        void onEnableTextures(Event& event) {
            _renderDevice->enableTextures(_cbEnableTextures->isChecked());
        }

        void onEnableAo(Event& event) {
            _renderDevice->enableAo(_cbEnableAo->isChecked());
        }

        void onEnableGi(Event& event) {
            _renderDevice->enableGi(_cbEnableGi->isChecked());
        }

    protected:
        Transform*  _dirLightTransform;
        Transform*  _pointLightTransform;
        Transform*  _spotLightTransform;
        RenderDevice* _renderDevice;
        Slider* _lightThetaSlider;
        Slider* _lightPhiSlider;
        Slider* _lightDistanceSlider;
        CheckBox* _cbEnableLights;
        CheckBox* _cbEnableShadows;
        CheckBox* _cbEnableTextures;
        CheckBox* _cbEnableAo;
        CheckBox* _cbEnableGi;
        ListBox* _lightList;

        struct Settings {
          float a, b, c;  
        };

        Settings _settings[3];
    };



    MyPanel* myPanel = new MyPanel(dirLightTransform, pointLightTransform, spotLightTransform, _renderDevice);
    myPanel->setPosition(Point(10.0f, 58.0f));
    _windowManager->addWindow(myPanel);

    _renderDevice->setSceneData(scene);
    _renderDevice->setOverlayData(_windowManager->getWindowGraph());

    _cameraManipulator = new SpectatorCameraManipulator();
    _renderDevice->setCameraManipulator(_cameraManipulator);
    
    _renderDevice->start();


    MSG msg;

    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }


    _renderDevice->stop();
    _renderDevice->join();

    _deviceRegistry.releaseDevice((Device**)&_renderDevice);

    destroyWindow();

    delete _windowManager;

    return (int)msg.wParam;
}