// ==============================================================
//	This file is part of Glest (www.glest.org)
//
//	Copyright (C) 2001-2008 Martiño Figueroa
//
//	You can redistribute this code and/or modify it under
//	the terms of the GNU General Public License as published
//	by the Free Software Foundation; either version 2 of the
//	License, or (at your option) any later version
// ==============================================================

#ifndef _GLEST_GAME_NETWORKINTERFACE_H_
#define _GLEST_GAME_NETWORKINTERFACE_H_

#ifdef WIN32
    #include <winsock2.h>
    #include <winsock.h>
#endif

#include <string>
#include <vector>
#include "checksum.h"
#include "network_message.h"
#include "network_types.h"
#include "game_settings.h"
#include "thread.h"
#include "data_types.h"
#include <time.h>
#include "leak_dumper.h"

using std::string;
using std::vector;
using Shared::Util::Checksum;
using namespace Shared::Util;
using namespace Shared::Platform;

namespace Glest{ namespace Game{

// =====================================================
//	class NetworkInterface
// =====================================================

//
// This interface describes the methods to notify when a client is lagging
//
class ClientLagCallbackInterface {
public:
	virtual bool clientLagHandler(int slotIndex, bool networkPauseGameForLaggedClients) = 0;

	virtual ~ClientLagCallbackInterface() {}
};


class ChatMsgInfo {

protected:

	void copyAll(const ChatMsgInfo &obj) {
		this->chatText			= obj.chatText.c_str();
		this->chatTeamIndex		= obj.chatTeamIndex;
		this->chatPlayerIndex 	= obj.chatPlayerIndex;
		this->targetLanguage	= obj.targetLanguage;
	}
public:

	ChatMsgInfo() {
		this->chatText			= "";
		this->chatTeamIndex		= -1;
		this->chatPlayerIndex	= -1;
		this->targetLanguage	= "";
	}
	ChatMsgInfo(string chatText, int chatTeamIndex, int chatPlayerIndex,
			string targetLanguage) {
		this->chatText		= chatText;
		this->chatTeamIndex	= chatTeamIndex;
		this->chatPlayerIndex = chatPlayerIndex;
		this->targetLanguage = targetLanguage;
	}
	ChatMsgInfo(const ChatMsgInfo& obj) {
		copyAll(obj);
	}
	ChatMsgInfo & operator=(const ChatMsgInfo & obj) {
		copyAll(obj);
		return *this;
	}

	string chatText;
	int chatTeamIndex;
	int chatPlayerIndex;
	string targetLanguage;

};

class MarkedCell {
protected:
	Vec2i targetPos;
	const Faction *faction;
	int factionIndex;
	int playerIndex;
	string note;
	int aliveCount;

public:
	static Vec3f static_system_marker_color;

	MarkedCell() {
		faction = NULL;
		factionIndex = -1;
		playerIndex = -1;
		note = "";
		aliveCount=200;
	}
	MarkedCell(Vec2i targetPos,const Faction *faction,string note, int playerIndex) {
		this->targetPos = targetPos;
		this->faction = faction;
		this->factionIndex = -1;
		this->playerIndex = playerIndex;
		this->note = note;
		aliveCount=200;
	}
	MarkedCell(Vec2i targetPos,int factionIndex,string note, int playerIndex) {
		this->targetPos = targetPos;
		this->faction = NULL;
		this->factionIndex = factionIndex;
		this->playerIndex = playerIndex;
		this->note = note;
		aliveCount=200;
	}

	Vec2i getTargetPos() const { return targetPos; }
	const Faction * getFaction() const { return faction; }
	void setFaction(const Faction *faction) { this->faction = faction; }
	int getFactionIndex() const { return factionIndex; }
	string getNote() const { return note; }
	void decrementAliveCount() { this->aliveCount--; }
	int getAliveCount() const { return aliveCount; }
	void setAliveCount(int value) { this->aliveCount = value; }
	int getPlayerIndex() const { return playerIndex; }

	void setNote(string value) { note = value; }
	void setPlayerIndex(int value) { playerIndex = value; }
};

class UnMarkedCell {
protected:
	Vec2i targetPos;
	const Faction *faction;
	int factionIndex;

public:
	UnMarkedCell() {
		faction = NULL;
		factionIndex = -1;
	}
	UnMarkedCell(Vec2i targetPos,const Faction *faction) {
		this->targetPos = targetPos;
		this->faction = faction;
		this->factionIndex = -1;
	}
	UnMarkedCell(Vec2i targetPos,int factionIndex) {
		this->targetPos = targetPos;
		this->faction = NULL;
		this->factionIndex = factionIndex;
	}

	Vec2i getTargetPos() const { return targetPos; }
	const Faction * getFaction() const { return faction; }
	void setFaction(const Faction *faction) { this->faction = faction; }
	int getFactionIndex() const { return factionIndex; }
};

typedef int (*DisplayMessageFunction)(const char *msg, bool exit);

class NetworkInterface {

protected:
    static bool allowGameDataSynchCheck;
    static bool allowDownloadDataSynch;
	bool networkGameDataSynchCheckOkMap;
	bool networkGameDataSynchCheckOkTile;
	bool networkGameDataSynchCheckOkTech;
	string networkGameDataSynchCheckTechMismatchReport;
	bool receivedDataSynchCheck;

	NetworkMessagePing lastPingInfo;

	std::vector<ChatMsgInfo> chatTextList;
	std::vector<MarkedCell> markedCellList;
	std::vector<UnMarkedCell> unmarkedCellList;

	static DisplayMessageFunction pCB_DisplayMessage;
	void DisplayErrorMessage(string sErr, bool closeSocket=true);

	virtual Mutex * getServerSynchAccessor() = 0;

	std::vector<MarkedCell> highlightedCellList;

	Mutex *networkAccessMutex;

	void init();

	Mutex *networkPlayerFactionCRCMutex;
	uint32 networkPlayerFactionCRC[GameConstants::maxPlayers];

public:
	static const int readyWaitTimeout;
	GameSettings gameSettings;

public:
	NetworkInterface();
	virtual ~NetworkInterface();

	NetworkInterface(const NetworkInterface& obj) {
		init();
		throw megaglest_runtime_error("class NetworkInterface is NOT safe to copy!");
	}
	NetworkInterface & operator=(const NetworkInterface& obj) {
		init();
		throw megaglest_runtime_error("class NetworkInterface is NOT safe to assign!");
	}

	uint32 getNetworkPlayerFactionCRC(int index);
	void setNetworkPlayerFactionCRC(int index, uint32 crc);

	virtual Socket* getSocket(bool mutexLock=true)= 0;

	virtual void close()= 0;
	virtual string getHumanPlayerName(int index=-1) = 0;
	virtual int getHumanPlayerIndex() const = 0;

    static void setDisplayMessageFunction(DisplayMessageFunction pDisplayMessage) { pCB_DisplayMessage = pDisplayMessage; }
    static DisplayMessageFunction getDisplayMessageFunction() { return pCB_DisplayMessage; }

    virtual std::string getIpAddress(bool mutexLock=true) = 0;
	string getIp() const		{return Socket::getIp();}
	string getHostName() const	{return Socket::getHostName();}

	virtual void sendMessage(NetworkMessage* networkMessage);
	NetworkMessageType getNextMessageType(int waitMilliseconds=0);
	bool receiveMessage(NetworkMessage* networkMessage);
	bool receiveMessage(NetworkMessage* networkMessage, NetworkMessageType type);

	virtual bool isConnected();

	const virtual GameSettings * getGameSettings() { return &gameSettings; }
	GameSettings * getGameSettingsPtr() { return &gameSettings; }

	static void setAllowDownloadDataSynch(bool value)          { allowDownloadDataSynch = value; }
	static bool getAllowDownloadDataSynch()                    { return allowDownloadDataSynch; }

	static void setAllowGameDataSynchCheck(bool value)          { allowGameDataSynchCheck = value; }
	static bool getAllowGameDataSynchCheck()                    { return allowGameDataSynchCheck; }

    virtual bool getNetworkGameDataSynchCheckOk()               { return (networkGameDataSynchCheckOkMap && networkGameDataSynchCheckOkTile && networkGameDataSynchCheckOkTech); }
    virtual void setNetworkGameDataSynchCheckOkMap(bool value)  { networkGameDataSynchCheckOkMap = value; }
    virtual void setNetworkGameDataSynchCheckOkTile(bool value) { networkGameDataSynchCheckOkTile = value; }
    virtual void setNetworkGameDataSynchCheckOkTech(bool value) { networkGameDataSynchCheckOkTech = value; }
    virtual bool getNetworkGameDataSynchCheckOkMap()            { return networkGameDataSynchCheckOkMap; }
    virtual bool getNetworkGameDataSynchCheckOkTile()           { return networkGameDataSynchCheckOkTile; }
    virtual bool getNetworkGameDataSynchCheckOkTech()           { return networkGameDataSynchCheckOkTech; }

    std::vector<ChatMsgInfo> getChatTextList(bool clearList);
	void clearChatInfo();
	void addChatInfo(const ChatMsgInfo &msg);

    std::vector<MarkedCell> getMarkedCellList(bool clearList);
	void clearMarkedCellList();
	void addMarkedCell(const MarkedCell &msg);

    std::vector<UnMarkedCell> getUnMarkedCellList(bool clearList);
	void clearUnMarkedCellList();
	void addUnMarkedCell(const UnMarkedCell &msg);

    std::vector<MarkedCell> getHighlightedCellList(bool clearList);
	void clearHighlightedCellList();
	void setHighlightedCell(const MarkedCell &msg);

	virtual bool getConnectHasHandshaked() const= 0;

	void setLastPingInfo(const NetworkMessagePing &ping);
	void setLastPingInfoToNow();
	NetworkMessagePing getLastPingInfo();
	double getLastPingLag();

	float getThreadedPingMS(std::string host);

	string getNetworkGameDataSynchCheckTechMismatchReport() const {return networkGameDataSynchCheckTechMismatchReport;}
	void setNetworkGameDataSynchCheckTechMismatchReport(string value) {networkGameDataSynchCheckTechMismatchReport = value;}

	bool getReceivedDataSynchCheck() const {return receivedDataSynchCheck;}
	void setReceivedDataSynchCheck(bool value) { receivedDataSynchCheck = value; }

	virtual void saveGame(XmlNode *rootNode) = 0;
	//static void loadGame(string name);

};

// =====================================================
//	class GameNetworkInterface
//
// Adds functions common to servers and clients
// but not connection slots
// =====================================================

class GameNetworkInterface: public NetworkInterface {

protected:
	typedef vector<NetworkCommand> Commands;

	Commands requestedCommands;	//commands requested by the user
	Commands pendingCommands;	//commands ready to be given
	bool quit;

public:
	GameNetworkInterface();
	virtual ~GameNetworkInterface(){}

	//message processimg
	virtual void update()= 0;
	virtual void updateLobby()= 0;
	virtual void updateKeyframe(int frameCount)= 0;
	virtual void setKeyframe(int frameCount)= 0;
	virtual void waitUntilReady(Checksum* checksum)= 0;

	//message sending
	virtual void sendTextMessage(const string &text, int teamIndex,bool echoLocal,
			string targetLanguage)= 0;
	virtual void quitGame(bool userManuallyQuit)=0;

	virtual void sendMarkCellMessage(Vec2i targetPos, int factionIndex, string note,int playerIndex) = 0;
	virtual void sendUnMarkCellMessage(Vec2i targetPos, int factionIndex) = 0;
	virtual void sendHighlightCellMessage(Vec2i targetPos, int factionIndex) = 0;


	//misc
	virtual string getNetworkStatus() = 0;

	//access functions
	void requestCommand(const NetworkCommand *networkCommand, bool insertAtStart=false);
	int getPendingCommandCount() const							{return (int)pendingCommands.size();}
	NetworkCommand* getPendingCommand(int i) 		            {return &pendingCommands[i];}
	void clearPendingCommands()									{pendingCommands.clear();}
	bool getQuit() const										{return quit;}
};

// =====================================================
//	class FileTransferSocketThread
// =====================================================

enum FileTransferHostType
{
    eClient,
    eServer
};

enum FileTransferOperationType
{
    eSend,
    eReceive
};

class FileTransferInfo
{
private:

    void CopyAll(const FileTransferInfo &obj)
    {
        hostType    = obj.hostType;
        serverIP    = obj.serverIP;
        serverPort  = obj.serverPort;
        opType      = obj.opType;
        fileName    = obj.fileName;
    }

public:
    FileTransferInfo()
    {
        hostType    = eClient;
        serverIP    = "";
        serverPort  = 0;
        opType      = eSend;
        fileName    = "";
    }
    FileTransferInfo(const FileTransferInfo &obj)
    {
        CopyAll(obj);
    }
    FileTransferInfo &operator=(const FileTransferInfo &obj)
    {
        CopyAll(obj);
        return *this;
    }

    FileTransferHostType hostType;
    string serverIP;
    int32  serverPort;
    FileTransferOperationType opType;
    string fileName;
};

class FileInfo
{
public:
    string fileName;
    int64 filesize;
    uint32 filecrc;
};

class FileTransferSocketThread : public Thread
{
private:
    FileTransferInfo info;

public:
    explicit FileTransferSocketThread(FileTransferInfo fileInfo);
    virtual void execute();
};


}}//end namespace

#endif
