/*
 * Copyright (C) TES Electronic Solutions GmbH,
 * All Rights Reserved.
 * Contact: info@guiliani.de
 *
 * This file is part of the Guiliani HMI framework
 * for the development of graphical user interfaces on embedded systems.
 */

#include "MyGUI_SR.h"

#include "GUI.h"
#include "GUITimer.h"
#include "eC_TList_Iterators.h"
#include "GUIButton.h"

#include "GUICommandHandler.h"
#include "GUICallAPICmd.h"
#include "GUITransitionCmd.h"
#include "GUILoadDialogCmd.h"

#if defined(PLATFORM_RENESAS_RZA2M_M13)
#include "CHardwareRZA2M.h"
#elif defined(PLATFORM_RENESAS_RA6M3)
#include "CHardwareRA6M3.h"
#else
#include "CHardwareMock.h"
#endif

#include "CLevel.h"
#include "CGame.h"

// always include this last
#include "GUIMemLeakWatcher.h"

CMyGUI::CMyGUI(eC_Value x, eC_Value y, eC_Value width, eC_Value height, ObjectHandle_t eID) :
    CStreamRuntimeGUI(x, y, width, height, eID),
    m_pkCurrentLevel(NULL),
    m_pkOverlayDialog(NULL),
    m_bOverlayRequested(false)
{
    // Add application specific initialisation here if necessary

    m_pkCurrentLevel = new CLevel();

#if defined(PLATFORM_RENESAS_RZA2M_M13)
    m_pkHardware = new CHardwareRZA2M();
#elif defined(PLATFORM_RENESAS_RA6M3)
    m_pkHardware = new CHardwareRA6M3();
#else
    m_pkHardware = new CHardwareMock();
#endif
    if (NULL != m_pkHardware)
    {
        m_pkHardware->SetObserver(this);
        m_pkGame = new CGame(m_pkHardware);
        if (NULL != m_pkGame)
        {
            m_pkGame->SetField(x, y, x + width, y + height);
            m_pkGame->SetObserver(this);
        }
    }
}

CMyGUI::~CMyGUI()
{
    // Add application specific de-initialisation here if necessary
    if (NULL != m_pkGame)
    {
        delete m_pkGame;
        m_pkGame = NULL;
    }

    if (NULL != m_pkCurrentLevel)
    {
        delete m_pkCurrentLevel;
        m_pkCurrentLevel = NULL;
    }
}

#if defined(eC_TARGET_CPU_X86) || defined(eC_TARGET_CPU_X64)
// this is needed when the hardware mockup is used
eC_Bool CMyGUI::DoKeyDown(const GUIKeyIdentifier_t &eKeyIdentifier, const eC_UInt &uiModifiers)
{
    if (NULL != m_pkHardware)
    {
        switch (eKeyIdentifier)
        {
            case GK_UP:
                m_pkHardware->SetY(8000);
                return true;
                break;

            case GK_DOWN:
                m_pkHardware->SetY(-8000);
                return true;
                break;

            case GK_LEFT:
                m_pkHardware->SetX(-8000);
                return true;
                break;

            case GK_RIGHT:
                m_pkHardware->SetX(8000);
                return true;
                break;

            case GK_A:
                m_pkHardware->SetSw1State(true);
                return true;
                break;

            case GK_Z:
                m_pkHardware->SetSw2State(true);
                return true;
                break;

            default:
                break;
        }
    }
    return false;
}

eC_Bool CMyGUI::DoKeyUp(const GUIKeyIdentifier_t &eKeyIdentifier, const eC_UInt &uiModifiers)
{
    if (NULL != m_pkHardware)
    {
        switch (eKeyIdentifier)
        {
            case GK_UP:
                m_pkHardware->SetY(0);
                return true;
                break;

            case GK_DOWN:
                m_pkHardware->SetY(0);
                return true;
                break;

            case GK_LEFT:
                m_pkHardware->SetX(0);
                return true;
                break;

            case GK_RIGHT:
                m_pkHardware->SetX(0);
                return true;
                break;

            case GK_A:
                m_pkHardware->SetSw1State(false);
                return true;
                break;

            case GK_Z:
                m_pkHardware->SetSw2State(false);
                return true;
                break;

            default:
                break;
        }
    }
    return false;
}
#endif

void CMyGUI::OnHardwareChange(eC_Int iButton)
{
    switch (iButton)
    {
        case 1:
        {
            // Restart the level
            m_pkGame->Reset();
            break;
        }

        case 2:
        {
            m_pkGame->Stop();
            SwitchDialog("Menu");
            break;
        }
    }
}

void CMyGUI::OnGameStateChange(GameState_t eGameState)
{
    switch (eGameState)
    {
        case GAME_WON:
        {
            if (NULL != m_pkGame)
            {
                m_pkGame->Stop();
            }
            GETGUI.SetClickThrough(false, false);
            DoCallAPI("showOverlay", "info");
            DoCallAPI("showOverlay", "won");
            break;
        }

        case GAME_LOST:
        {
            if (NULL != m_pkGame)
            {
                m_pkGame->Stop();
            }
            GETGUI.SetClickThrough(false, false);
            DoCallAPI("showOverlay", "info");
            DoCallAPI("showOverlay", "lost");
            break;
        }

        case GAME_PLAYING:
        {
            GETGUI.SetClickThrough(true, true);
            DoCallAPI("hideOverlay", "");
            break;
        }
    }
}

void CMyGUI::OnNotification(const CGUIValue& kObservedValue, const CGUIObject* const pkUpdatedObject, const eC_UInt uiX, const eC_UInt uiY)
{
    if (pkUpdatedObject->GetID() == CHECKBOX_HARD_MODE)
    {
        if (true == kObservedValue)
        {
            m_pkCurrentLevel->SetActiveHolesIndex(1);
        }
        else
        {
            m_pkCurrentLevel->SetActiveHolesIndex(0);
        }
    }
}

void CMyGUI::DoCallAPI(const eC_String& kAPI, const eC_String& kParam)
{
    if ("showOverlay" == kAPI)
    {
        CGUIObject* pkOverlay = (GETGUI.GetObjectByID(DLG_OVERLAY));
        if (NULL != pkOverlay)
        {
            // Then make the requested visible

            pkOverlay = (GETGUI.GetObjectByID(VISUAL_GLASSPANE));
            if (NULL != pkOverlay)
                pkOverlay->SetInvisible(false);

            if ("won" == kParam)
            {
                pkOverlay = (GETGUI.GetObjectByID(SCREEN_WIN));
                if (NULL != pkOverlay)
                    pkOverlay->SetInvisible(false);
            }
            else if ("lost" == kParam)
            {
                pkOverlay = (GETGUI.GetObjectByID(SCREEN_LOSE));
                if (NULL != pkOverlay)
                    pkOverlay->SetInvisible(false);

            }
            else if ("info" == kParam)
            {
                pkOverlay = (GETGUI.GetObjectByID(SCREEN_INFO));
                if (NULL != pkOverlay)
                    pkOverlay->SetInvisible(false);
            }
        }
        else
        {
            if (m_bOverlayRequested)
            {
                // Is the Overlay already requested, and currently loading? This can happen if two overlays are requested shortly after each other (e.g. info and won)
                // Then do the same command again and hope it has finished loading by the time it executes
                CGUICallAPICmd* pkCmd = new CGUICallAPICmd("showOverlay", kParam);
                GETCMDHDL.Execute(pkCmd);
            }
            else
            {
                // Then request it to load and execute the command afterwards
                m_bOverlayRequested = true;
                CGUILoadDialogCmd* pkCmd = new CGUILoadDialogCmd("Overlay");
                pkCmd->AddAdditionalCmd(new CGUICallAPICmd("showOverlay", kParam));
                GETCMDHDL.Execute(pkCmd);
            }
        }

    }
    else if ("hideOverlay" == kAPI)
    {
        CGUIObject* pkOverlay = (GETGUI.GetObjectByID(DLG_OVERLAY));
        if (NULL != pkOverlay)
        {
            // If so hide the requested. If not then there's nothing to do
            if ("won" == kParam)
            {
                pkOverlay = (GETGUI.GetObjectByID(SCREEN_WIN));
                if (NULL != pkOverlay)
                    pkOverlay->SetInvisible(true);
            }
            else if ("lost" == kParam)
            {
                pkOverlay = (GETGUI.GetObjectByID(SCREEN_LOSE));
                if (NULL != pkOverlay)
                    pkOverlay->SetInvisible(true);
            }
            else if ("info" == kParam)
            {
                pkOverlay = (GETGUI.GetObjectByID(SCREEN_INFO));
                if (NULL != pkOverlay)
                    pkOverlay->SetInvisible(true);
            }
            else
            {
                // No specific part of the Overlay was specified, so just unload the whole Overlay dialog
                m_bOverlayRequested = false;
                CGUILoadDialogCmd* pkCmd = new CGUILoadDialogCmd("", NO_HANDLE, DLG_OVERLAY);
                GETCMDHDL.Execute(pkCmd);
            }
        }
    }

    else if ("switchDialog" == kAPI) // This is the call that needs to be called to properly switch to a new dialog/screen
    {
        // DO NOT add a new dialog/screen here, it needs to be added in "INTERNALInitDialog"
        SwitchDialog(kParam);
    }
    else if ("InitLevel" == kAPI)
    {
        if ("Menu" == kParam)
        {
            if (NULL != m_pkCurrentLevel)
            {
                m_pkCurrentLevel->ClearLevel();
            }
        }
        if ("Level_Tutorial" == kParam)
        {
            InitLevel(DLG_LEVEL_TUTORIAL);
        }
        else if ("Level_1" == kParam)
        {
            InitLevel(DLG_LEVEL_1, false);
        }
        // ADD NEW LEVEL-DIALOGS HERE!
        // Remember to make your kParam string the same as the filename of the dialog (as seen in the GSE 'Dialogs' window.)
    }

    else if ("Game" == kAPI)
    {
        if ("start" == kParam)
        {
            if (NULL != m_pkCurrentLevel)
            {
                CGUIObject* pkDialog = GETGUI.GetObjectByID(m_pkCurrentLevel->GetDialogID());
                if (NULL != pkDialog)
                {
                    CGUIObject* pkMenu = pkDialog->GetObjectByID(MENU);
                    if (NULL != pkMenu)
                    {
                        pkMenu->SetInvisible(true);
                    }
                    if (NULL != m_pkGame)
                    {
                        m_pkGame->Start();
                    }
                }
            }
        }
    }

    else // If the kAPI call is not known to the MyGUI class then relay the call to the currently active screen
    {
        if (NULL != m_pkCurrentLevel)
        {
            m_pkCurrentLevel->HandleCallAPI(kAPI, kParam);
        }
    }
}

void CMyGUI::InitLevel(ObjectHandle_t eDialogID, eC_Bool bInstantStart)
{
    if (NULL != m_pkCurrentLevel)
    {
        m_pkCurrentLevel->GenerateLevel(eDialogID);
        if (NULL != m_pkGame)
        {
            m_pkGame->SetLevel(m_pkCurrentLevel);
            if (bInstantStart)
            {
                m_pkGame->Start();
            }

            if (DLG_LEVEL_1 == eDialogID)
            {
                CGUIObject* pkGUIDialog = GETGUI.GetObjectByID(DLG_LEVEL_1);
                CGUIObject* pkHoles = pkGUIDialog->GetObjectByID(HOLES_HARD);

                if (NULL != pkHoles)
                {
                    m_pkCurrentLevel->AddHoles(
                        auto_cast<CGUICompositeObject*>(pkHoles));
                }
                pkGUIDialog->GetObjectByID(CHECKBOX_HARD_MODE)->AddValueObserver(this);
            }
        }
    }
}

void CMyGUI::SwitchDialog(eC_String kDialogName)
{
    if (NULL != m_pkGame)
    {
        m_pkGame->Stop();
    }

    // make sure the overlay is hidden
    DoCallAPI("hideOverlay", "");

    ObjectHandle_t kCurrentDialogID = NO_HANDLE;
    if (NULL != m_pkCurrentLevel)
    {
        kCurrentDialogID = m_pkCurrentLevel->GetDialogID();

        if (DLG_LEVEL_1 == kCurrentDialogID)
        {
            GETGUI.GetObjectByID(CHECKBOX_HARD_MODE)->RemoveValueObserver(this);
        }
        // Add Level/Dialog specific deInit procedures here
    }

    // If there's no current level, then it is the menu
    if (NO_HANDLE == kCurrentDialogID && GETGUI.GetObjectByID(DLG_MENU))
    {
        kCurrentDialogID = DLG_MENU;
    }
    else
    {
        GUILOGMESSAGE("Something went wrong when switching dialogs");
        kCurrentDialogID = GETGUI.GetChild(0)->GetID();
    }

    CGUITransitionCmd* pkCmd = new CGUITransitionCmd(
        CGUITransitionCmd::BLEND_ONLY_SIMPLE, kCurrentDialogID, kDialogName,
        10);
    pkCmd->AddAdditionalCmd(new CGUICallAPICmd("InitLevel", kDialogName));
    GETCMDHDL.Execute(pkCmd);
}
