/*
 * Gearboy - Nintendo Game Boy Emulator
 * Copyright (C) 2012  Ignacio Sanchez

 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see http://www.gnu.org/licenses/
 *
 */

#include <QFileDialog>
#include <QDesktopWidget>
#include <QSettings>
#include <QStandardPaths>
#include "MainWindow.h"
#include "ui_MainWindow.h"
#include "GLFrame.h"
#include "Emulator.h"
#include "InputSettings.h"
#include "SoundSettings.h"
#include "VideoSettings.h"
#include "About.h"

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
    qApp->installEventFilter(this);
    m_bFullscreen = false;
    m_iScreenSize = 2;
    m_iSelectedSlot = 1;

    for (int i=0; i<4;i++)
        m_bMenuPressed[i] = false;

    m_pUI = new Ui::MainWindow();
    m_pUI->setupUi(this);

    this->addAction(m_pUI->actionFullscreen);
    this->addAction(m_pUI->actionReset);
    this->addAction(m_pUI->actionPause);
    this->addAction(m_pUI->actionSave_State);
    this->addAction(m_pUI->actionLoad_State);

    m_pExitShortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this);
    m_pExitShortcut->setContext(Qt::ApplicationShortcut);
    QObject::connect(m_pExitShortcut, SIGNAL(activated()), this, SLOT(Exit()));


    QObject::connect(m_pUI->menuGame_Boy, SIGNAL(aboutToShow()), this, SLOT(MenuGameBoyPressed()));
    QObject::connect(m_pUI->menuGame_Boy, SIGNAL(aboutToHide()), this, SLOT(MenuGameBoyReleased()));
    QObject::connect(m_pUI->menuDebug, SIGNAL(aboutToShow()), this, SLOT(MenuDebugPressed()));
    QObject::connect(m_pUI->menuDebug, SIGNAL(aboutToHide()), this, SLOT(MenuDebugReleased()));
    QObject::connect(m_pUI->menuSettings, SIGNAL(aboutToShow()), this, SLOT(MenuSettingsPressed()));
    QObject::connect(m_pUI->menuSettings, SIGNAL(aboutToHide()), this, SLOT(MenuSettingsReleased()));
    QObject::connect(m_pUI->menuHelp, SIGNAL(aboutToShow()), this, SLOT(MenuAboutPressed()));
    QObject::connect(m_pUI->menuHelp, SIGNAL(aboutToHide()), this, SLOT(MenuAboutReleased()));

    m_pUI->actionX_1->setData(1);
    m_pUI->actionX_2->setData(2);
    m_pUI->actionX_3->setData(3);
    m_pUI->actionX_4->setData(4);
    m_pUI->actionX_5->setData(5);

    m_pUI->action1->setData(1);
    m_pUI->action2->setData(2);
    m_pUI->action3->setData(3);
    m_pUI->action4->setData(4);
    m_pUI->action5->setData(5);

    m_pEmulator = new Emulator();
    m_pEmulator->Init();

    QGLFormat f;
    f.setSwapInterval(0);
    QGLFormat::setDefaultFormat(f);

    m_pGLFrame = new GLFrame();
    ResizeWindow(m_iScreenSize);
    setCentralWidget(m_pGLFrame);

    m_pInputSettings = new InputSettings(m_pGLFrame);

    m_pSoundSettings = new SoundSettings(m_pGLFrame, m_pEmulator);

    m_pVideoSettings = new VideoSettings(m_pGLFrame, m_pEmulator);

    m_pAbout = new About();

    QPalette pal = this->palette();
    pal.setColor(this->backgroundRole(), Qt::black);
    this->setPalette(pal);

    LoadSettings();

    m_pGLFrame->InitRenderThread(m_pEmulator);
}

MainWindow::~MainWindow()
{
    SaveSettings();

    SafeDelete(m_pAbout);
    SafeDelete(m_pExitShortcut);
    SafeDelete(m_pEmulator);
    SafeDelete(m_pGLFrame);
    SafeDelete(m_pInputSettings);
    SafeDelete(m_pSoundSettings);
    SafeDelete(m_pUI);
}

void MainWindow::Exit()
{
#ifdef DEBUG_GEARBOY
    m_pEmulator->MemoryDump();
#endif
    this->close();
}

void MainWindow::InitalGameBoyLoadROM(const char* szFilePath)
{
    if (IsValidPointer(szFilePath))
    {
        m_pGLFrame->PauseRenderThread();

        m_pEmulator->LoadRom(szFilePath, m_pUI->actionForce_Game_Boy_DMG->isChecked(), m_pUI->actionSave_RAM_in_ROM_folder->isChecked());
        m_pUI->actionPause->setChecked(false);

        setFocus();
        activateWindow();

        m_pGLFrame->ResumeRenderThread();
    }
}

void MainWindow::MenuGameBoyLoadROM()
{
    m_pGLFrame->PauseRenderThread();

    QString filename = QFileDialog::getOpenFileName(
            this,
            tr("Load ROM"),
            QDir::currentPath(),
            tr("Game Boy ROM files (*.gb *.dmg *.gbc *.cgb *.sgb *.zip);;All files (*.*)"));

    if (!filename.isNull())
    {
        m_pEmulator->LoadRom(filename.toUtf8().data(), m_pUI->actionForce_Game_Boy_DMG->isChecked(), m_pUI->actionSave_RAM_in_ROM_folder->isChecked());
        m_pUI->actionPause->setChecked(false);
    }

    setFocus();
    activateWindow();

    m_pGLFrame->ResumeRenderThread();
}

void MainWindow::MenuGameBoyPause()
{
    if (m_pEmulator->IsPaused())
        m_pEmulator->Resume();
    else
        m_pEmulator->Pause();
}

void MainWindow::MenuGameBoyReset()
{
    m_pUI->actionPause->setChecked(false);
    m_pEmulator->Reset(m_pUI->actionForce_Game_Boy_DMG->isChecked(), m_pUI->actionSave_RAM_in_ROM_folder->isChecked());
}

void MainWindow::MenuGameBoySelectStateSlot(QAction* action)
{
    m_pUI->action1->setChecked(false);
    m_pUI->action2->setChecked(false);
    m_pUI->action3->setChecked(false);
    m_pUI->action4->setChecked(false);
    m_pUI->action5->setChecked(false);
    action->setChecked(true);
    m_iSelectedSlot = action->data().toInt();
}

void MainWindow::MenuGameBoySaveState()
{
    m_pEmulator->SaveState(m_iSelectedSlot);
}

void MainWindow::MenuGameBoyLoadState()
{
    m_pEmulator->LoadState(m_iSelectedSlot);
}

void MainWindow::MenuSettingsInput()
{
    m_pGLFrame->PauseRenderThread();
    m_pInputSettings->show();
}

void MainWindow::MenuSettingsVideo()
{
    m_pGLFrame->PauseRenderThread();
    m_pVideoSettings->show();
}

void MainWindow::MenuSettingsSound()
{
    m_pGLFrame->PauseRenderThread();
    m_pSoundSettings->show();
}

void MainWindow::MenuSettingsWindowSize(QAction* action)
{
    m_pUI->actionX_1->setChecked(false);
    m_pUI->actionX_2->setChecked(false);
    m_pUI->actionX_3->setChecked(false);
    m_pUI->actionX_4->setChecked(false);
    m_pUI->actionX_5->setChecked(false);
    action->setChecked(true);
    m_iScreenSize = action->data().toInt();
    ResizeWindow(m_iScreenSize);
}

void MainWindow::MenuSettingsFullscreen()
{
    if (m_bFullscreen)
    {
        m_bFullscreen = false;
        this->showNormal();
        m_pUI->menubar->show();
        ResizeWindow(m_iScreenSize);
        m_pGLFrame->move(0, 0);
    }
    else
    {
        m_bFullscreen = true;

        this->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
        this->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
        this->setMinimumSize(0, 0);
        this->showFullScreen();

        m_pUI->menubar->hide();

        int w = qApp->desktop()->size().width();
        int h = qApp->desktop()->size().height();

        int factor = h / GAMEBOY_HEIGHT;

        m_pGLFrame->setMaximumSize(GAMEBOY_WIDTH * factor, GAMEBOY_HEIGHT * factor);
        m_pGLFrame->setMinimumSize(GAMEBOY_WIDTH * factor, GAMEBOY_HEIGHT * factor);

        int move_x = (w - (GAMEBOY_WIDTH * factor)) / 2;
        int move_y = (h - (GAMEBOY_HEIGHT * factor)) / 2;
        m_pGLFrame->setGeometry(move_x, move_y, GAMEBOY_WIDTH * factor, GAMEBOY_HEIGHT * factor);
    }

    setFocus();
    activateWindow();
}

void MainWindow::MenuSettingsForceDMG()
{
}

void MainWindow::MenuSettingsSaveRAMInROMFolder()
{
}

void MainWindow::MenuDebugDisassembler()
{
}

void MainWindow::MenuDebugOAM()
{
}

void MainWindow::MenuDebugMap()
{
}

void MainWindow::MenuDebugPalette()
{
}

void MainWindow::MenuAbout()
{
    m_pAbout->setModal(true);
    m_pAbout->show();
}

void MainWindow::MenuGameBoyPressed()
{
    m_bMenuPressed[0] = true;
    m_pGLFrame->PauseRenderThread();
}

void MainWindow::MenuGameBoyReleased()
{
    m_bMenuPressed[0] = false;
    MenuReleased();
}

void MainWindow::MenuSettingsPressed()
{
    m_bMenuPressed[1] = true;
    m_pGLFrame->PauseRenderThread();
}

void MainWindow::MenuSettingsReleased()
{
    m_bMenuPressed[1] = false;
    MenuReleased();
}

void MainWindow::MenuDebugPressed()
{
    m_bMenuPressed[2] = true;
    m_pGLFrame->PauseRenderThread();
}

void MainWindow::MenuDebugReleased()
{
    m_bMenuPressed[2] = false;
    MenuReleased();
}

void MainWindow::MenuAboutPressed()
{
    m_bMenuPressed[3] = true;
    m_pGLFrame->PauseRenderThread();
}

void MainWindow::MenuAboutReleased()
{
    m_bMenuPressed[3] = false;
    MenuReleased();
}

void MainWindow::MenuReleased()
{
    for (int i = 0; i < 4; i++)
    {
        if (m_bMenuPressed[i])
            return;
    }
    m_pGLFrame->ResumeRenderThread();
}

void MainWindow::closeEvent(QCloseEvent *evt)
{
    m_pGLFrame->StopRenderThread();
    QMainWindow::closeEvent(evt);
}

void MainWindow::keyPressEvent(QKeyEvent* e)
{
    switch (m_pInputSettings->GetKey(e->key()))
    {
        case 0:
            m_pEmulator->KeyPressed(Up_Key);
            break;
        case 3:
            m_pEmulator->KeyPressed(Left_Key);
            break;
        case 1:
            m_pEmulator->KeyPressed(Right_Key);
            break;
        case 2:
            m_pEmulator->KeyPressed(Down_Key);
            break;
        case 6:
            m_pEmulator->KeyPressed(Start_Key);
            break;
        case 7:
            m_pEmulator->KeyPressed(Select_Key);
            break;
        case 5:
            m_pEmulator->KeyPressed(B_Key);
            break;
        case 4:
            m_pEmulator->KeyPressed(A_Key);
            break;
        default:
            break;
    }
}

void MainWindow::keyReleaseEvent(QKeyEvent* e)
{
    switch (m_pInputSettings->GetKey(e->key()))
    {
        case 0:
            m_pEmulator->KeyReleased(Up_Key);
            break;
        case 3:
            m_pEmulator->KeyReleased(Left_Key);
            break;
        case 1:
            m_pEmulator->KeyReleased(Right_Key);
            break;
        case 2:
            m_pEmulator->KeyReleased(Down_Key);
            break;
        case 6:
            m_pEmulator->KeyReleased(Start_Key);
            break;
        case 7:
            m_pEmulator->KeyReleased(Select_Key);
            break;
        case 5:
            m_pEmulator->KeyReleased(B_Key);
            break;
        case 4:
            m_pEmulator->KeyReleased(A_Key);
            break;
        default:
            break;
    }
}

void MainWindow::ResizeWindow(int factor)
{
    m_iScreenSize = factor;
    m_pGLFrame->setMaximumSize(GAMEBOY_WIDTH * factor, GAMEBOY_HEIGHT * factor);
    m_pGLFrame->setMinimumSize(GAMEBOY_WIDTH * factor, GAMEBOY_HEIGHT * factor);
}

bool MainWindow::eventFilter(QObject * watched, QEvent * event)
{
    if (event->type() == QEvent::ApplicationActivate)
    {
        m_pGLFrame->ResumeRenderThread();
    }
    else if (event->type() == QEvent::ApplicationDeactivate)
    {
        m_pGLFrame->PauseRenderThread();
    }

    return QMainWindow::eventFilter(watched, event);
}

bool MainWindow::event(QEvent *ev)
{
    if (ev->type() == QEvent::LayoutRequest)
    {
        if (!m_bFullscreen)
        {
            this->setMaximumSize(sizeHint());
            this->setMinimumSize(sizeHint());
            this->resize(sizeHint());
        }
    }

    return QMainWindow::event(ev);
}

void MainWindow::LoadSettings()
{
    QSettings settings(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/gearboy.ini", QSettings::IniFormat);

    settings.beginGroup("Gearboy");
    m_iSelectedSlot = settings.value("SavestateSlot", 1).toInt();

    switch (m_iSelectedSlot)
    {
        case 1:
            MenuGameBoySelectStateSlot(m_pUI->action1);
            break;
        case 2:
            MenuGameBoySelectStateSlot(m_pUI->action2);
            break;
        case 3:
            MenuGameBoySelectStateSlot(m_pUI->action3);
            break;
        case 4:
            MenuGameBoySelectStateSlot(m_pUI->action4);
            break;
        case 5:
            MenuGameBoySelectStateSlot(m_pUI->action5);
            break;
    }

    m_iScreenSize = settings.value("ScreenSize", 2).toInt();

    switch (m_iScreenSize)
    {
        case 1:
            MenuSettingsWindowSize(m_pUI->actionX_1);
            break;
        case 2:
            MenuSettingsWindowSize(m_pUI->actionX_2);
            break;
        case 3:
            MenuSettingsWindowSize(m_pUI->actionX_3);
            break;
        case 4:
            MenuSettingsWindowSize(m_pUI->actionX_4);
            break;
        case 5:
            MenuSettingsWindowSize(m_pUI->actionX_5);
            break;
    }

    m_bFullscreen = !settings.value("FullScreen", false).toBool();

    MenuSettingsFullscreen();

    m_pUI->actionForce_Game_Boy_DMG->setChecked(settings.value("ForceDMG", false).toBool());
    m_pUI->actionSave_RAM_in_ROM_folder->setChecked(settings.value("SaveInROMFolder", false).toBool());
    settings.endGroup();

    settings.beginGroup("Input");
    m_pInputSettings->LoadSettings(settings);
    settings.endGroup();
    settings.beginGroup("Video");
    m_pVideoSettings->LoadSettings(settings);
    settings.endGroup();
    settings.beginGroup("Sound");
    m_pSoundSettings->LoadSettings(settings);
    settings.endGroup();
}

void MainWindow::SaveSettings()
{
    QSettings settings(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/gearboy.ini", QSettings::IniFormat);

    settings.beginGroup("Gearboy");
    settings.setValue("SavestateSlot", m_iSelectedSlot);
    settings.setValue("ScreenSize", m_iScreenSize);
    settings.setValue("FullScreen", m_bFullscreen);
    settings.setValue("ForceDMG", m_pUI->actionForce_Game_Boy_DMG->isChecked());
    settings.setValue("SaveInROMFolder", m_pUI->actionSave_RAM_in_ROM_folder->isChecked());
    settings.endGroup();

    settings.beginGroup("Input");
    m_pInputSettings->SaveSettings(settings);
    settings.endGroup();
    settings.beginGroup("Video");
    m_pVideoSettings->SaveSettings(settings);
    settings.endGroup();
    settings.beginGroup("Sound");
    m_pSoundSettings->SaveSettings(settings);
    settings.endGroup();
}
