/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011  Cyan Worlds, Inc.

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
(at your option) 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/>.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
/*****************************************************************************
*
*   $/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/TicTacToe/pfGmTicTacToe.cpp
*   
***/

#define USES_GAME_TICTACTOE
#include "../Pch.h"
#pragma hdrstop


/*****************************************************************************
*
*   Local types
*
***/

struct ITicTacToe {
    pfGmTicTacToe * gameCli;
    char            board[3][3];
    char            myself;
    char            other;
    
    ITicTacToe (pfGmTicTacToe * gameCli);

    // pfGameCli event notification handlers
    void Recv           (GameMsgHeader * msg, void * param);
    void OnPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg);
    void OnPlayerLeft   (const Srv2Cli_Game_PlayerLeft & msg);
    void OnInviteFailed (const Srv2Cli_Game_InviteFailed & msg);
    void OnOwnerChange  (const Srv2Cli_Game_OwnerChange & msg);

    // TicTacToe network message handlers   
    void RecvGameStarted    (const Srv2Cli_TTT_GameStarted & msg, void * param);
    void RecvGameOver       (const Srv2Cli_TTT_GameOver & msg, void * param);
    void RecvMoveMade       (const Srv2Cli_TTT_MoveMade & msg, void * param);
};


/*****************************************************************************
*
*   Factory functions
*
***/

//============================================================================
static pfGameCli * TicTacToeFactory (
    unsigned    gameId,
    plKey       receiver
) {
    return NEWZERO(pfGmTicTacToe)(gameId, receiver);
}

//============================================================================
AUTO_INIT_FUNC(RegisterTicTacToeFactory) {

    static GameTypeReg reg = {
        TicTacToeFactory,
        kGameTypeId_TicTacToe,
        L"Tic-Tac-Toe"
    };

    GameMgrRegisterGameType(reg);
}


/*****************************************************************************
*
*   ITicTacToe
*
***/

//============================================================================
ITicTacToe::ITicTacToe (pfGmTicTacToe * gameCli)
:   gameCli(gameCli)
{
    // Fill the board with space chars
    MemSet(board, ' ', sizeof(board));
}

//============================================================================
void ITicTacToe::OnPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg) {

    pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg);
    gameCliMsg->Set(gameCli, msg);
    gameCliMsg->Send(gameCli->GetReceiver());
}

//============================================================================
void ITicTacToe::OnPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg) {

    pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg);
    gameCliMsg->Set(gameCli, msg);
    gameCliMsg->Send(gameCli->GetReceiver());
}

//============================================================================
void ITicTacToe::OnInviteFailed (const Srv2Cli_Game_InviteFailed & msg) {
    
    pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg);
    gameCliMsg->Set(gameCli, msg);
    gameCliMsg->Send(gameCli->GetReceiver());
}

//============================================================================
void ITicTacToe::OnOwnerChange (const Srv2Cli_Game_OwnerChange & msg) {

    pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg);
    gameCliMsg->Set(gameCli, msg);
    gameCliMsg->Send(gameCli->GetReceiver());
}

//============================================================================
void ITicTacToe::RecvGameStarted (const Srv2Cli_TTT_GameStarted & msg, void * param) {
    // player that goes first is shown as X's.
    if (msg.yourTurn) {
        myself  = 'X';
        other   = 'O';
    }
    else {
        myself  = 'O';
        other   = 'X';
    }

    pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg);
    gameCliMsg->Set(gameCli, msg);
    gameCliMsg->Send(gameCli->GetReceiver());
}

//============================================================================
void ITicTacToe::RecvGameOver (const Srv2Cli_TTT_GameOver & msg, void * param) {
    pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg);
    gameCliMsg->Set(gameCli, msg);
    gameCliMsg->Send(gameCli->GetReceiver());

    DEL(gameCli);   // we're done
}

//============================================================================
void ITicTacToe::RecvMoveMade (const Srv2Cli_TTT_MoveMade & msg, void * param) {
    // Update the board with the appropriate piece  
    if (msg.playerId == NetCommGetPlayer()->playerInt)
        board[msg.row][msg.col] = myself;
    else
        board[msg.row][msg.col] = other;

    pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg);
    gameCliMsg->Set(gameCli, msg);
    gameCliMsg->Send(gameCli->GetReceiver());
}
                                                                

/*****************************************************************************
*
*   pfGmTicTacToe
*
***/

//============================================================================
pfGmTicTacToe::pfGmTicTacToe (
    unsigned    gameId,
    plKey       receiver
)
:   pfGameCli(gameId, receiver)
{
    internal = NEWZERO(ITicTacToe)(this);
}

//============================================================================
pfGmTicTacToe::~pfGmTicTacToe () {

    DEL(internal);
}

//============================================================================
void pfGmTicTacToe::Recv (GameMsgHeader * msg, void * param) {

    #define DISPATCH(a) case kSrv2Cli_TTT_##a: {                    \
        const Srv2Cli_TTT_##a & m = *(const Srv2Cli_TTT_##a *)msg;  \
        internal->Recv##a(m, param);                                \
    }                                                               \
    break;                                                          //
    switch (msg->messageId) {
        DISPATCH(GameStarted);
        DISPATCH(GameOver);
        DISPATCH(MoveMade);
        DEFAULT_FATAL(msg->messageId);
    }
    #undef DISPATCH
}

//============================================================================
void pfGmTicTacToe::OnPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg) {

    internal->OnPlayerJoined(msg);
}

//============================================================================
void pfGmTicTacToe::OnPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg) {

    internal->OnPlayerLeft(msg);
}

//============================================================================
void pfGmTicTacToe::OnInviteFailed (const Srv2Cli_Game_InviteFailed & msg) {

    internal->OnInviteFailed(msg);
}

//============================================================================
void pfGmTicTacToe::OnOwnerChange (const Srv2Cli_Game_OwnerChange & msg) {

    internal->OnOwnerChange(msg);
}

//============================================================================
void pfGmTicTacToe::MakeMove (unsigned row, unsigned col) {

    Cli2Srv_TTT_MakeMove msg;
    msg.messageId       = kCli2Srv_TTT_MakeMove;
    msg.messageBytes    = sizeof(msg);
    msg.recvGameId      = GetGameId();  // send to GameSrv on server
    msg.transId         = 0;
    msg.row             = (byte)row;
    msg.col             = (byte)col;
    
    GameMgrSend(&msg);
}

//============================================================================
void pfGmTicTacToe::ShowBoard () {

    // Technically, we should stuff the board into a plMessage and
    // have our receiver handle how the board is shown, but heck,
    // this is just a little demo and not quite worth the effort.

#if 0 // Max doesn't have the console, and can't link with it anyway, so I'm removing this code since "this is just a little demo and not quite worth the effort"
    pfConsole::AddLine ("\n");
    pfConsole::AddLineF("\\i %c | %c | %c", internal->board[0][0], internal->board[0][1], internal->board[0][2]);
    pfConsole::AddLine ("\\i---+---+---");
    pfConsole::AddLineF("\\i %c | %c | %c", internal->board[1][0], internal->board[1][1], internal->board[1][2]);
    pfConsole::AddLine ("\\i---+---+---");
    pfConsole::AddLineF("\\i %c | %c | %c", internal->board[2][0], internal->board[2][1], internal->board[2][2]);
    pfConsole::AddLine ("\n");
#endif // 0
}