////////////////////////////////////////////////////////////////////////////////
// Main.cpp
//
//  Main program logic.

#include "stdhdr.h"

#include "ddutil.h"
#include "Debug.h"
#include "Sound.h"
#include "Input.h"
#include "Draw.h"
#include "Window.h"

#include "Sprites.h"
#include "Logic.h"

////////////////////////////////////////////////////////////////////////////////
// DirectX Audio

IDirectMusicSegment8    *g_pBackgroundMusic;
bool                    g_bMusicPlaying, g_bFinishedInit;
bool                    g_bMusicEnabled;

////////////////////////////////////////////////////////////////////////////////
// DirectInput

// {5447976E-1120-4762-87E5-61ACC539746A}
GUID g_guidApp = 
{ 0x5447976e, 0x1120, 0x4762, { 0x87, 0xe5, 0x61, 0xac, 0xc5, 0x39, 0x74, 0x6a } };
DWORD   g_dwGenre = DIVIRTUAL_ARCADE_SIDE2SIDE;
LPCTSTR g_tszActionMapName = _T("Xonix");

enum Actions 
{
    ACTIONS_LEFTRIGHT,
    ACTIONS_UPDOWN,
    ACTIONS_LEFT,
    ACTIONS_RIGHT,
    ACTIONS_UP,
    ACTIONS_DOWN,
    ACTIONS_QUIT,
    ACTIONS_HELP,
    ACTIONS_CONFIGURE,
    ACTIONS_MUSIC,
    ACTIONS_PAUSE,
    ACTIONS_NONE
};

bool    g_bQuit, g_bShowHelp, g_bPaused, g_bDoConfig;
bool    g_bLeft, g_bRight, g_bUp, g_bDown;

DIACTION g_rgActions[] =
{
    // Genre-defined virtual axes
    { ACTIONS_LEFTRIGHT,    DIAXIS_ARCADES_LATERAL,             0,  "Left/Right" },
    { ACTIONS_UPDOWN,       DIAXIS_ARCADES_MOVE,                0,  "Up/Down" },

    // Genre-defined virtual buttons
    { ACTIONS_LEFT,         DIBUTTON_ARCADES_LEFT_LINK,         0,  "Left" },
    { ACTIONS_RIGHT,        DIBUTTON_ARCADES_RIGHT_LINK,        0,  "Right" },
    { ACTIONS_UP,           DIBUTTON_ARCADES_FORWARD_LINK,      0,  "Up" },
    { ACTIONS_DOWN,         DIBUTTON_ARCADES_BACK_LINK,         0,  "Down" },
    { ACTIONS_PAUSE,        DIBUTTON_ARCADES_PAUSE,             0,  "Pause" },

    // Keys
    { ACTIONS_LEFT,         DIKEYBOARD_LEFT,                    0,  "Left" },
    { ACTIONS_LEFT,         DIKEYBOARD_NUMPAD4,                 0,  "Left" },
    { ACTIONS_RIGHT,        DIKEYBOARD_RIGHT,                   0,  "Right" },
    { ACTIONS_RIGHT,        DIKEYBOARD_NUMPAD6,                 0,  "Right" },
    { ACTIONS_UP,           DIKEYBOARD_UP,                      0,  "Up" },
    { ACTIONS_UP,           DIKEYBOARD_NUMPAD8,                 0,  "Up" },
    { ACTIONS_DOWN,         DIKEYBOARD_DOWN,                    0,  "Down" },
    { ACTIONS_DOWN,         DIKEYBOARD_NUMPAD2,                 0,  "Down" },
    { ACTIONS_PAUSE,        DIKEYBOARD_P,           DIA_APPFIXED,   "Pause" },
    { ACTIONS_MUSIC,        DIKEYBOARD_M,           DIA_APPFIXED,   "Music on/off" },
    { ACTIONS_HELP,         DIKEYBOARD_F1,          DIA_APPFIXED,   "Help" },
    { ACTIONS_CONFIGURE,    DIKEYBOARD_F2,          DIA_APPFIXED,   "Configure" },
    { ACTIONS_QUIT,         DIKEYBOARD_ESCAPE,      DIA_APPFIXED,   "Quit" },
};

DWORD g_nActions = sizeof(g_rgActions) / sizeof(DIACTION);

////////////////////////////////////////////////////////////////////////////////
// DirectDraw

// Surface containing images
IDirectDrawSurface7 *lpImages;

void ReloadAllSurfaces()
{
    DDReLoadBitmap(lpImages, "Main.bmp");
}

////////////////////////////////////////////////////////////////////////////////
// Music handling

void PlayBackgroundMusic()
{
    if ((g_pBackgroundMusic != NULL) && !g_bMusicPlaying && g_bFinishedInit && g_bMusicEnabled)
    {
        // Play the background music
        TRACE0("Playing background music...\n");
        g_bMusicPlaying = true;
        PlaySound(g_pBackgroundMusic);
    }
}

void StopBackgroundMusic()
{
    StopAllSound();
    g_bMusicPlaying = false;
}

////////////////////////////////////////////////////////////////////////////////
// Input handling

#define AXIS_THRESHOLD  20

void HandleAction(UINT nAction, DWORD dwData)
{
    int nAxisPos = (int)dwData;

    switch (nAction)
    {
    case ACTIONS_LEFTRIGHT:
        if (nAxisPos < -AXIS_THRESHOLD)
        {
            g_bLeft = true;
            g_bRight = false;
        }
        else if (nAxisPos > AXIS_THRESHOLD)
        {
            g_bRight = true;
            g_bLeft = false;
        }
        else
        {
            g_bLeft = g_bRight = false;
        }
        break;
    
    case ACTIONS_UPDOWN:
        if (nAxisPos < -AXIS_THRESHOLD)
        {
            g_bUp = true;
            g_bDown = false;
        }
        else if (nAxisPos > AXIS_THRESHOLD)
        {
            g_bDown = true;
            g_bUp = false;
        }
        else
        {
            g_bUp = g_bDown = false;
        }
        break;
    
    case ACTIONS_LEFT:
        g_bLeft = (dwData != 0);
        break;

    case ACTIONS_RIGHT:
        g_bRight = (dwData != 0);
        break;

    case ACTIONS_UP:
        g_bUp = (dwData != 0);
        break;

    case ACTIONS_DOWN:
        g_bDown = (dwData != 0);
        break;

    case ACTIONS_QUIT:
        g_bQuit = true;
        break;

    case ACTIONS_MUSIC:
        if (dwData != 0)
        {
            g_bMusicEnabled = !g_bMusicEnabled;
            if (g_bMusicEnabled)
                PlayBackgroundMusic();
            else
                StopBackgroundMusic();
        }
        break;

    case ACTIONS_HELP:
        if (dwData != 0)
            g_bShowHelp = true;
        break;

    case ACTIONS_CONFIGURE:
        if (dwData != 0)
            g_bDoConfig = true;
        break;

    case ACTIONS_PAUSE:
        if (dwData != 0)
            g_bPaused = !g_bPaused;
        break;
    
    default:
        break;
    }
}

////////////////////////////////////////////////////////////////////////////////
// Idle-time processing function.

void DrawFrame()
{
    DEBUG_TIMING_START();

    RECT    r;
    int     x, y, i;

    // Temporary copy of the current grid.
    static BYTE DrawGrid[YBLOCKS][XBLOCKS];

    // Clear screen to black
    ClearScreen();

    memcpy(DrawGrid, Grid, sizeof(Grid));

    for (y = 0; y < YBLOCKS; y++)
    {
        for (x = 0; x < XBLOCKS; x++)
        {
            if (DrawGrid[y][x] == GRID_EMPTY)
                continue;

            // Extend the drawing rectangle as far as possible in the horizontal direction.
            for (i = 1; ((x + i) < XBLOCKS) && (DrawGrid[y][x + i] == DrawGrid[y][x]); i++)
                DrawGrid[y][x + i] = GRID_EMPTY;
            if (i > 1)
            {
                SetRect(&r, x * BLOCKSIZE, y * BLOCKSIZE, (x + i) * BLOCKSIZE, (y + 1) * BLOCKSIZE);
            }
            else
            {
                // Rectangle was only 1 block wide, so extend the rectangle 
                //  as far as possible in the vertical direction.
                for (i = 1; ((y + i) < YBLOCKS) && (DrawGrid[y + i][x] == DrawGrid[y][x]); i++)
                    DrawGrid[y + i][x] = GRID_EMPTY;
                SetRect(&r, x * BLOCKSIZE, y * BLOCKSIZE, (x + 1) * BLOCKSIZE, (y + i) * BLOCKSIZE);
            }

            switch (DrawGrid[y][x])
            {
            case GRID_EMPTY:
                break;

            case GRID_FULL:
                BackBlt(x * BLOCKSIZE, y * BLOCKSIZE, lpImages, &r, DDBLTFAST_NOCOLORKEY);
                break;

            case GRID_MID:
                r.top += MIDTOP;
                r.bottom += MIDTOP;
                BackBlt(x * BLOCKSIZE, y * BLOCKSIZE, lpImages, &r, DDBLTFAST_NOCOLORKEY);
                break;
            }

            // Note that we don't have to reset the current position, since 
            //  it will not again be considered.
        }
    }
    g_spritePlayer->Draw();

    for (i = 0; i < g_nBalls; i++)
    {
        g_spriteBalls[i]->NextFrame();
        g_spriteBalls[i]->Draw();
    }

    DEBUG_TIMING_END("Scene draw");
    g_lpPrimary->Flip(NULL, DDFLIP_WAIT);
}

void OnIdle(void)
{
    if (g_lpPrimary == NULL)
        return;

    if (!g_bActive)
        return;

    CheckInput();

    if (g_bQuit)
    {
        PostMessage(hWndMain, WM_CLOSE, 0, 0);
        return;
    }

    if (g_bShowHelp)
    {
        StopAllSound();
        g_bMusicPlaying = false;
        g_bShowHelp = false;
        DisplayInput();
        // Music came back on when we were reactivated...
    }

    if (g_bDoConfig)
    {
        StopAllSound();
        g_bMusicPlaying = false;
        g_bDoConfig = false;
        ConfigureInput();
        // Music came back on when we were reactivated...
    }
    
    if (!g_bPaused)
    {
        // Handle movement
        if (g_bUp)
            g_spritePlayer->m_nNextDirection = CPlayerSprite::Direction::UP;
        if (g_bDown)
            g_spritePlayer->m_nNextDirection = CPlayerSprite::Direction::DOWN;
        if (g_bLeft)
            g_spritePlayer->m_nNextDirection = CPlayerSprite::Direction::LEFT;
        if (g_bRight)
            g_spritePlayer->m_nNextDirection = CPlayerSprite::Direction::RIGHT;

        UpdateGameState();
    }
    
    DrawFrame();
}

////////////////////////////////////////////////////////////////////////////////
// Initialisation and shutdown

BOOL InitApplication(HINSTANCE hInstance)
{
    if (!InitWindow("DirectX Sample Application with configuration"))
        return FALSE;

    if (!InitDirectMusic())
        return FALSE;

    // Load the background music
    g_pBackgroundMusic = LoadSound(L"passport.mid");
    if (g_pBackgroundMusic == NULL)
    {
        ExitDirectMusic();
        DISPLAYERROR("Could not load background music.");
        return FALSE;
    }
    // Standard MIDI file
    g_pBackgroundMusic->SetParam(GUID_StandardMIDIFile, 
        0xFFFFFFFF, DMUS_SEG_ALLTRACKS, 0, NULL);
    // Repeat forever
    g_pBackgroundMusic->SetRepeats(DMUS_SEG_REPEAT_INFINITE);
    // Download it, ready for playback
    DownloadSound(g_pBackgroundMusic);
    g_bMusicEnabled = true;

    if (!InitDirectInput())
    {
        DISPLAYERROR("Failed to initialise DirectInput.");
        ExitDirectMusic();
        return FALSE;
    }
    if (!InitDirectDraw())
    {
        ExitDirectInput();
        ExitDirectMusic();
        return FALSE;
    }

    // Load the sprites.
    lpImages = LoadSurface(_T("Main.bmp"));
    if (lpImages == NULL)
    {
        ExitDirectDraw();
        
        DISPLAYERROR("Could not load sprites.");
        return FALSE;
    }
    DDSetColorKey(lpImages, RGB(0, 0, 0));

    ResetGame();

    AcquireDevices();

    g_bFinishedInit = true;
    return TRUE;
}

void OnClose()
{
    StopBackgroundMusic();

    DeletePlayer();
    DeleteLevel();

    // Release our images surface.
    lpImages->Release();
    lpImages = NULL;

    ExitDirectDraw();

    UnloadSound(g_pBackgroundMusic);
    g_pBackgroundMusic->Release();
    g_pBackgroundMusic = NULL;
    ExitDirectMusic();

    UnacquireDevices();
    ExitDirectInput();
}

// Program entry point.
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int) 
{
    if (!InitApplication(hInstance))
        return 1;

    // Play the background music
    PlayBackgroundMusic();

    Run();

    return 0;
}