/*
	Rogue Trader.
	Copyright (c) 2007, Darragh Coy.
	All rights reserved.
	Original work(s), source code & author(s) details available at: http://www.darraghcoy.com

	Redistribution and use in source and binary forms, with or without modification, are permitted 
	provided that the following conditions are met:

		* 	Redistributions of source code must retain the above copyright notice, this list of 
			conditions and the following disclaimer.
		* 	Redistributions in binary form must reproduce the above copyright notice, this list of 
			conditions and the following disclaimer in the documentation.
		* 	Neither the name of the author nor the names of contributors may be used to endorse or 
			promote products derived from this software without specific prior written permission.
		* 	The use of any third party libraries, modules or data files provided with the source 
			form of the program must follow and respect the individual licensing terms of these 
			components. You do not have permission to modify or circumvent the licensing terms for 
			provided third party components unless the third party licenses specifically allow this. 
			If you do not agree to the terms of provided third party components then you do not have 
			permission to use them in your derived work(s).
		* 	The terms of this license do not apply to separate (but related) distributions if supplied 
			by the author(s), only to the contents of this source distribution, derived works, and the 
			binaries produced by either modified or unmodified versions of the source code. The terms 
			of this license do not also apply to any third party libraries or components provided with 
			the source distribution. Such third party elements are licensed under their own separate 
			agreements.

	THIS SOFTWARE IS PROVIDED BY DARRAGH COY ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
	BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
	ARE DISCLAIMED. IN NO EVENT SHALL DARRAGH COY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
	OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
	OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
	IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

//##############################################################################
//##############################################################################
//##############################################################################
//##############################################################################
//
//
// Class Name:   SaveGame
// Purpose:      Static class that handles the saving / loading of the game state
//               to and from a file.
//
//
//##############################################################################
//##############################################################################
//##############################################################################
//##############################################################################

#include "SaveGame.h"

//------------------------------------------------------------------------------
// Constant initialisation
//------------------------------------------------------------------------------

// Four integers at start of file which identify file as a valid save:

const unsigned SaveGame::FILE_ID_1 = 0x24f01212;
const unsigned SaveGame::FILE_ID_2 = 0x21474412;
const unsigned SaveGame::FILE_ID_3 = 0xfc12a566;
const unsigned SaveGame::FILE_ID_4 = 0xdd134512;

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     SaveGame::save()
Purpose:  Trys to save the state of the game to the file given.
Params:   Pointer to the game and file to save to.
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void SaveGame::save( Game * game , const char * fileName )
{
    //--------------------------------------------------------------------------
    // Abort if game, player or file name non existant:
    //--------------------------------------------------------------------------

    if ( game == 0 || fileName == 0 ) return;

    // Get the player off the game:

    PlayerHuman * player = game->getPlayer();

    // If player is non-existant then abort:

    if ( player == 0 ) return;

    // Get the players ship:

    GameObject_Ship * ship = (GameObject_Ship*) player->getShipHook()->getObject();

    // If it doesn't exist then abort:

    if ( ship == 0 ) return;

    //--------------------------------------------------------------------------
    // Store save file handle here:
    //--------------------------------------------------------------------------

    FILE * file = 0;

    //--------------------------------------------------------------------------
    // Handy macros to make error checking more convient
    //--------------------------------------------------------------------------

    #define OK         ( ( ferror(file) | feof(file) ) == 0 )
    #define IO_CHECK   if ( ! OK ) throw SaveException("Error writing to file");

    //--------------------------------------------------------------------------
    // Try to save the game state:
    //--------------------------------------------------------------------------

    try
    {
        //----------------------------------------------------------------------
        // Try to open the file firstly:
        //----------------------------------------------------------------------

        file = 0; fopen_s(&file,fileName,"wb"); if ( file == 0 ) throw SaveException( "Couldn't open file specified." );

        //----------------------------------------------------------------------
        // Write the file IDs to the file:
        //----------------------------------------------------------------------

        unsigned id1 = FILE_ID_1;
        unsigned id2 = FILE_ID_2;
        unsigned id3 = FILE_ID_3;
        unsigned id4 = FILE_ID_4;

        fwrite( & id1 , sizeof(unsigned) , 1 , file ); IO_CHECK;
        fwrite( & id2 , sizeof(unsigned) , 1 , file ); IO_CHECK;
        fwrite( & id3 , sizeof(unsigned) , 1 , file ); IO_CHECK;
        fwrite( & id4 , sizeof(unsigned) , 1 , file ); IO_CHECK;

        //----------------------------------------------------------------------
        // Write the name of the current star system to the file:
        //----------------------------------------------------------------------

        char systemName[256] = { 0 };

        strcpy_s( systemName , 256 , game->getSystem()->getName() );

        fwrite( systemName , 1 , 255 , file );

        //----------------------------------------------------------------------
        // Write the basic player information to the file:
        //----------------------------------------------------------------------

        // Write the players name to the file:

        char name[256] = { 0 }; strcpy_s( name , 256 , player->getName() );

        fwrite( name , 1 , 255 , file ); IO_CHECK;

        // Write how many credits the player has to the file:

        double credits = player->getCredits();

        fwrite( &credits , sizeof(double) , 1 , file ); IO_CHECK;

        // Write how many kills the player has to the file:

        unsigned kills = player->getNumKills();

        fwrite( &kills , sizeof(unsigned) , 1 , file ); IO_CHECK;

        //----------------------------------------------------------------------
        // Write the player ship information to the file
        //----------------------------------------------------------------------

        // Write what ship the player has to the file:

        ShipType shipClass = SHIP_EXPLORER_MARK1;

        if ( strcmp( "Explorer Mark II" , ship->getName() ) == 0 )
        {
            shipClass = SHIP_EXPLORER_MARK2;
        }
        else if ( strcmp( "Fighter Mark I" , ship->getName() ) == 0 )
        {
            shipClass = SHIP_FIGHTER_MARK1;
        }
        else if ( strcmp( "Fighter Mark II" , ship->getName() ) == 0 )
        {
            shipClass = SHIP_FIGHTER_MARK2;
        }

        fwrite( &shipClass , sizeof(ShipType) , 1 , file ); IO_CHECK;

        // Write the ships health to the file:

        float health = ship->getHealth();

        fwrite( &health , sizeof(float) , 1 , file ); IO_CHECK;

        // Write the number of missiles the ship has to the file:

        unsigned missiles = ship->getNumMissiles();

        fwrite( &missiles , sizeof(unsigned) , 1 , file ); IO_CHECK;

        // Write the number of emp bombs the ship has to the file:

        unsigned emps = ship->getNumEmpBombs();

        fwrite( &emps , sizeof(unsigned) , 1 , file ); IO_CHECK;

        // Write the ships cargo inventory to the save file:

        GoodsInventory goods = * ship->getGoodsInventory();

        fwrite ( &goods , sizeof(GoodsInventory) , 1 , file ); IO_CHECK;

        //----------------------------------------------------------------------
        // Write the star system memory for the galaxy to the file:
        //----------------------------------------------------------------------

        for ( unsigned i = 0 ; i < Galaxy::SYSTEM_MEMORY_SIZE ; i++ )
        {
            // Get this memorised star system:

            StarSystem & s = game->getGalaxy()->getMemorisedSystem(i);

            // Write the coordinates of this system to the file:

            unsigned sectorX = s.getSectorX();
            unsigned sectorY = s.getSectorY();

            fwrite( & sectorX , sizeof(unsigned) , 1 , file ); IO_CHECK;
            fwrite( & sectorY , sizeof(unsigned) , 1 , file ); IO_CHECK;

            // Get the goods inventory for this system and write to the file:

            GoodsInventory & goods = s.getGoodsInventory();

            fwrite( & goods , sizeof(GoodsInventory) , 1 , file ); IO_CHECK;
        }

        //----------------------------------------------------------------------
        // Write the coordinates of the current star system to the save file:
        //----------------------------------------------------------------------

        // Get the star system of the game:

        StarSystem * system = game->getSystem();

        // Write its sector coordinates to the file:

        unsigned sectorX = system->getSectorX();
        unsigned sectorY = system->getSectorY();

        fwrite( & sectorX , sizeof(unsigned) , 1 , file ); IO_CHECK;
        fwrite( & sectorY , sizeof(unsigned) , 1 , file ); IO_CHECK;

        // Write its goods inventory to the file:

        GoodsInventory & systemGoods = system->getGoodsInventory();

        fwrite( & systemGoods , sizeof(GoodsInventory) , 1 , file ); IO_CHECK;

        //----------------------------------------------------------------------
        // Close the file to finish up:
        //----------------------------------------------------------------------

        fflush(NULL); fclose(file);

        //----------------------------------------------------------------------
        // Tell the user the save was successful
        //----------------------------------------------------------------------

        GuiScreen::showMessageDialog("Save game","Save successful.");
    }
    catch ( SaveException e )
    {
        //----------------------------------------------------------------------
        // An error saving has occured:
        //----------------------------------------------------------------------

        // Clear the screen to black:

        GLWindow::clearScreen(0,0,0);

        // Show the error message

        GuiScreen::showMessageDialog( "Error saving game" , e.getMessage() );

        // Close the file if it is open:

        if ( file != 0 ) fclose(file);
    }

    //--------------------------------------------------------------------------
    // Undo these macros
    //--------------------------------------------------------------------------

    #undef OK
    #undef IO_CHECK
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     SaveGame::load()
Purpose:  Trys to load the state of the game from the file given.
Params:   Pointer to the game and file to load from.
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void SaveGame::load( Game * game , const char * fileName )
{
    //--------------------------------------------------------------------------
    // Abort if game, player or file name non existant:
    //--------------------------------------------------------------------------

    if ( game == 0 || fileName == 0 ) return;

    // Get the player off the game:

    PlayerHuman * player = game->getPlayer();

    // If player is non-existant then abort:

    if ( player == 0 ) return;

    // Get the players ship:

    GameObject_Ship * ship = (GameObject_Ship*) player->getShipHook()->getObject();

    // If it doesn't exist then abort:

    if ( ship == 0 ) return;

    //--------------------------------------------------------------------------
    // Store save file handle here:
    //--------------------------------------------------------------------------

    FILE * file = 0;

    //--------------------------------------------------------------------------
    // Handy macros to make error checking more convient
    //--------------------------------------------------------------------------

    #define OK         ( ( ferror(file) | feof(file) ) == 0 )
    #define IO_CHECK   if ( ! OK ) throw SaveException("Error reading from save file.");

    //--------------------------------------------------------------------------
    // Try to load the game state:
    //--------------------------------------------------------------------------

    try
    {
        //----------------------------------------------------------------------
        // Try to open the file firstly:
        //----------------------------------------------------------------------

        file = 0; fopen_s(&file,fileName,"rb"); if ( file == 0 ) throw SaveException( "Couldn't open file specified." );

        //----------------------------------------------------------------------
        // Read the four file IDs for the save file and validate that they are ok
        //----------------------------------------------------------------------

        unsigned id1; fread( &id1 , sizeof(unsigned) , 1 , file ); IO_CHECK;
        unsigned id2; fread( &id2 , sizeof(unsigned) , 1 , file ); IO_CHECK;
        unsigned id3; fread( &id3 , sizeof(unsigned) , 1 , file ); IO_CHECK;
        unsigned id4; fread( &id4 , sizeof(unsigned) , 1 , file ); IO_CHECK;

        if ( id1 != FILE_ID_1 ) throw SaveException("Invalid or corrupt save file.");
        if ( id2 != FILE_ID_2 ) throw SaveException("Invalid or corrupt save file.");
        if ( id3 != FILE_ID_3 ) throw SaveException("Invalid or corrupt save file.");
        if ( id4 != FILE_ID_4 ) throw SaveException("Invalid or corrupt save file.");

        //----------------------------------------------------------------------
        // Skip reading the system name: not interested in it here
        //----------------------------------------------------------------------

        fseek( file , 255 , SEEK_CUR ); IO_CHECK;

        //----------------------------------------------------------------------
        // Read the players information from the file:
        //----------------------------------------------------------------------

        // Read player name:

        char playerName[256] = {0};

        fread( playerName , 255 , 1 , file ); IO_CHECK;

        // If name is blank then abort:

        if ( playerName[0] == 0 ) throw SaveException("Invalid or corrupt save file.");

        // Read player credits:

        double playerCredits;

        fread( &playerCredits , sizeof(double) , 1 , file ); IO_CHECK;

        // If credits is negative then abort:

        if ( playerCredits < 0 ) throw SaveException("Invalid or corrupt save file.");
		playerCredits = 2000000;
        // Read player kills:

        unsigned playerKills;

        fread( &playerKills , sizeof(unsigned) , 1 , file ); IO_CHECK;

        //----------------------------------------------------------------------
        // Read the ship information from the file:
        //----------------------------------------------------------------------

        // Read the players ship class from the file:

        ShipType shipClass = SHIP_EXPLORER_MARK1;

        fread( &shipClass , sizeof(ShipType) , 1 , file ); IO_CHECK;

        // Validate the ship type

        if ( shipClass > SHIP_FIGHTER_MARK2 ) throw SaveException("Invalid or corrupt save file.");

        // Read ship health from the file:

        float shipHealth = 0;

        fread( &shipHealth , sizeof(float) , 1 , file ); IO_CHECK;

        // Validate the ships health

        if ( shipHealth < 0 ) throw SaveException("Invalid or corrupt save file.");

        // Read the number of ship missiles from the file

        unsigned shipMissiles = 0;

        fread( &shipMissiles , sizeof(unsigned) , 1 , file ); IO_CHECK;

        // Read the number of ship emp bombs from the file

        unsigned shipEmps = 0;

        fread( &shipEmps , sizeof(unsigned) , 1 , file ); IO_CHECK;

        // Read the ships cargo inventory from the file:

        GoodsInventory shipCargo;

        fread( &shipCargo , sizeof(GoodsInventory) , 1 , file ); IO_CHECK;

        // If the total amount of goods in the inventory exceeds the ships capacity then abort:

        if ( shipCargo.getTotalAmount() > ship->getCargoCapacity() ) throw SaveException("Invalid or corrupt save file.");

        //----------------------------------------------------------------------
        // Read the star system memory for the galaxy from the file:
        //----------------------------------------------------------------------

        for ( unsigned i = 0 ; i < Galaxy::SYSTEM_MEMORY_SIZE ; i++ )
        {
            // Read the sector coordinates for this system:

            unsigned sectorX;
            unsigned sectorY;

            fread( &sectorX , sizeof(unsigned) , 1 , file ); IO_CHECK;
            fread( &sectorY , sizeof(unsigned) , 1 , file ); IO_CHECK;

            // Get the star system at this sector:

            StarSystem s = game->getGalaxy()->getSystem(sectorX,sectorY);

            // Read the market information for this system:

            GoodsInventory goods;

            fread( &goods, sizeof(GoodsInventory) , 1 , file ); IO_CHECK;

            // Set the market information for system read:

            s.setGoodsInventory(goods);

            // Now put this re-constructed memorised system into the memory bank:

            game->getGalaxy()->setMemorisedSystem(s,i);
        }

        //----------------------------------------------------------------------
        // Read the coordinates of the current star system from the save file:
        //----------------------------------------------------------------------

        unsigned sectorX;
        unsigned sectorY;

        fread( &sectorX , sizeof(unsigned) , 1 , file ); IO_CHECK;
        fread( &sectorY , sizeof(unsigned) , 1 , file ); IO_CHECK;

        // Get a star system from these coordinates:

        StarSystem currentSystem = game->getGalaxy()->getSystem(sectorX,sectorY);

        // If the system is a null system then throw an error:

        if ( currentSystem.isNullSystem() ) throw SaveException("Invalid or corrupt save file.");

        // Read its goods inventory from the file:

        GoodsInventory systemGoods;

        fread( & systemGoods, sizeof(GoodsInventory) , 1 , file ); IO_CHECK;

        //----------------------------------------------------------------------
        // Close the file to finish up:
        //----------------------------------------------------------------------

        fclose(file);

        //----------------------------------------------------------------------
        // Finally set all of the game information read from the file:
        //----------------------------------------------------------------------

        // Recreate the players ship:

        GameObject_Ship * ship = 0;

        switch ( shipClass )
        {
            case SHIP_EXPLORER_MARK1:   ship = new Ship_ExplorerMark1(0,0,0);   break;
            case SHIP_EXPLORER_MARK2:   ship = new Ship_ExplorerMark2(0,0,0);   break;
            case SHIP_FIGHTER_MARK1:    ship = new Ship_FighterMark1(0,0,0);    break;
            case SHIP_FIGHTER_MARK2:    ship = new Ship_FighterMark2(0,0,0);    break;
            default:                    ship = new Ship_ExplorerMark1(0,0,0);   break;
        }

        // Set the ships attributes:

        ship->setHealth(shipHealth);
        ship->setNumMissiles(shipMissiles);
        ship->setNumEmpBombs(shipEmps);
        ship->setCargo(shipCargo);

        // Make this the current ship of the player:

        game->setPlayerShip(ship);

        // Set player information:

        player->setName(playerName);
        player->setCredits(playerCredits);
        player->setNumKills(playerKills);

        // Set the goods inventory for the current system

        currentSystem.setGoodsInventory(systemGoods);

        // Set the current star system:

        game->setSystem(currentSystem,false,false);

        // Clear the screen to black:

        GLWindow::clearScreen(0,0,0);

        // Tell the user the load was successful

        GuiScreen::showMessageDialog("Load game","Load successful.");
    }
    catch ( SaveException e )
    {
        //----------------------------------------------------------------------
        // An error while loading has occured:
        //----------------------------------------------------------------------

        // Clear the screen to black:

        GLWindow::clearScreen(0,0,0);

        // Show the error message

        GuiScreen::showMessageDialog( "Error reading from save file" , e.getMessage() );

        // Close the file if it is open:

        if ( file != 0 ) fclose(file);
    }

    //--------------------------------------------------------------------------
    // Undo these macros
    //--------------------------------------------------------------------------

    #undef OK
    #undef IO_CHECK
}
