/*****************************************************************************
 *                                                                           *
 *  Copyright (c) 1993, 1994, 1995, Elan Feingold (feingold@zko.dec.com)     *
 *                                                                           *
 *     PERMISSION TO USE, COPY, MODIFY, AND TO DISTRIBUTE THIS SOFTWARE      *
 *     AND ITS DOCUMENTATION FOR ANY PURPOSE IS HEREBY GRANTED WITHOUT       *
 *     FEE, PROVIDED THAT THE ABOVE COPYRIGHT NOTICE APPEAR IN ALL           *
 *     COPIES AND MODIFIED COPIES AND THAT BOTH THAT COPYRIGHT NOTICE AND    *
 *     THIS PERMISSION NOTICE APPEAR IN SUPPORTING DOCUMENTATION.  THERE     *
 *     IS NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR      *
 *     ANY PURPOSE.  THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS       *
 *     OR IMPLIED WARRANTY.                                                  *
 *                                                                           *
 *****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include "callbacks.h"
#include "network.h"
#include "utils.h"
#include "riskgame.h"
#include "gui-vars.h"
#include "debug.h"

/* Globals */
Int32 iMode;           /* What the object mode is, see riskgame.h */

/* Private functions */
Int32  _RISK_GetMsgType(Int32 iField); 
void  *_RISK_GetMsg(Int32 iField, Int32 iIndex1, Int32 iIndex2);
void   _RISK_Replicate(Int32 iField, Int32 iIndex1, Int32 iIndex2);

/****************************************/
typedef struct _ContinentObject
{
  CString  strName;
  Int32    iValue;
  Int32    iNumCountries;
} ContinentObject;

/****************************************/ 
typedef struct _CountryObject
{
  CString   strName;            /* Name of the country */
  Int32     iContinent;         /* The continent it forms part of */
  Int32     iNumArmies;         /* Number of armies on the country */
  Int32     iTextX, iTextY;     /* Where to write the text */
  Int32     piOwner;            /* The player it belongs to */
  Int32     piAdjCountries[6];  /* All attackable countries */
} CountryObject;

/****************************************/
typedef struct _PlayerObject
{	
  Int32      iAttackMode, iDiceMode, iMsgDstMode;
  Int32      iSpecies;
  Flag       iState;
  Int32      iClient;
  CString    strName, strColor;
  Int32      iCountriesOwned, iNumArmies, iNumCards;
  Int32      piCards[MAX_CARDS];
} PlayerObject;

/****************************************/
typedef struct _Game
{
  void               (*MessageCallback)(Int32, void *, Int32, Int32);
  void               (*FailureCallback)(CString, Int32);

  Int32              iNumPlayers;
  Int32              iNumLivePlayers;

  /* The databases */
  ContinentObject    pContinents[NUM_CONTINENTS];
  PlayerObject       pPlayers[MAX_PLAYERS];
  CountryObject      pCountries[NUM_COUNTRIES];
} Game;

/* The object (eventually do this in a loop?) */
Game RiskGame = 
{
  NULL, NULL,
  0, 0, 
  {
    "North America",  5, 9,
    "South America",  2, 4,
    "Africa",         3, 6,
    "Australia",      2, 4,
    "Asia",           7, 12,
    "Europe",         5, 7,
  },
  {
    { 1, 3, 0, 0, TRUE, -1, NULL, NULL, 0, 0, 0, {0,0,0,0,0,0,0,0,0,0} }, 
    { 1, 3, 0, 0, TRUE, -1, NULL, NULL, 0, 0, 0, {0,0,0,0,0,0,0,0,0,0} }, 
    { 1, 3, 0, 0, TRUE, -1, NULL, NULL, 0, 0, 0, {0,0,0,0,0,0,0,0,0,0} }, 
    { 1, 3, 0, 0, TRUE, -1, NULL, NULL, 0, 0, 0, {0,0,0,0,0,0,0,0,0,0} }, 
    { 1, 3, 0, 0, TRUE, -1, NULL, NULL, 0, 0, 0, {0,0,0,0,0,0,0,0,0,0} }, 
    { 1, 3, 0, 0, TRUE, -1, NULL, NULL, 0, 0, 0, {0,0,0,0,0,0,0,0,0,0} }, 
    { 1, 3, 0, 0, TRUE, -1, NULL, NULL, 0, 0, 0, {0,0,0,0,0,0,0,0,0,0} }, 
    { 1, 3, 0, 0, TRUE, -1, NULL, NULL, 0, 0, 0, {0,0,0,0,0,0,0,0,0,0} }, 
  },
  { 
    { "Greenland",   CNT_NORTHAMERICA,  0, 264,  32, -1, 
	{1, 5, 10, 11, -1, -1} },
    { "Iceland",     CNT_EUROPE,        0, 342,  51, -1, 
	{0, 7, 14, -1, -1, -1} },
    { "Siberia",     CNT_ASIA,          0, 622,  91, -1, 
	{3, 6, 13, 21, 22, -1} },
    { "Ural",        CNT_ASIA,          0, 557, 106, -1, 
	{2, 8, 15, 22, -1, -1} },
    { "Alaska",      CNT_NORTHAMERICA,  0,  13,  61, -1, 
	{5, 9, 12, -1, -1, -1} },
    { "Northwest Territories", CNT_NORTHAMERICA, 0, 61, 62, -1, 
	{0, 4, 11, 12, -1, -1} },
    { "Irkutsk",      CNT_ASIA,          0, 700,  71, -1, 
	{2, 9, 13, -1, -1, -1} },
    { "Scandinavia", CNT_EUROPE,        0, 387,  85, -1, 
	{1, 8, 14, 16, -1, -1} },
    { "Ukraine",      CNT_EUROPE,        0, 450, 103, -1, 
	{3, 7, 15, 16, 20, 24}},
    { "Kamchatka",   CNT_ASIA,          0, 731, 121, -1, 
	{4, 6, 13, 21, 23, -1} },
    { "Quebec",    CNT_NORTHAMERICA,  0, 181,  95, -1, 
	{0, 11, 18, -1, -1, -1} },
    { "Ontario",     CNT_NORTHAMERICA,  0, 116,  97, -1, 
	{0, 5, 10, 12, 17, 18} },
    { "Alberta",     CNT_NORTHAMERICA,  0,  55,  90, -1, 
	{4, 5, 11, 17, -1, -1} },
    { "Yakutsk",     CNT_ASIA,          0, 690, 111, -1, 
	{2, 6, 9, 21, -1, -1} },
    { "Great Britain", CNT_EUROPE,      0, 338, 110, -1, 
	{1, 7, 16, 19, -1, -1} },
    { "Afghanistan",  CNT_ASIA,          0, 534, 141, -1,
	{3, 8, 22, 24, 28, -1} },
    { "Northern Europe", CNT_EUROPE,    0, 386, 113, -1,
	{7, 8, 14, 19, 20, -1} },
    { "Western United States", CNT_NORTHAMERICA, 0, 62, 126, -1,
	{11, 12, 18, 25, -1, -1} },
    { "Eastern United States", CNT_NORTHAMERICA, 0, 126, 145, -1,
	{10, 11, 17, 25, -1, -1} },
    { "Western Europe",  CNT_EUROPE,    0, 338, 148, -1,
	{14, 16, 20, 26, -1, -1} },
    { "Southern Europe", CNT_EUROPE,    0, 416, 134, -1,
	{8, 16, 19, 24, 26, 27} },
    { "Mongolia",    CNT_ASIA,          0, 685, 139, -1,
	{2, 9, 13, 22, 23, -1} }, 
    { "China",       CNT_ASIA,          0, 632, 163, -1,
	{2, 3, 15, 21, 28, 29} },
    { "Japan",       CNT_ASIA,          0, 735, 166, -1, 
	{9, 21, -1, -1, -1, -1} },
    { "Middle East", CNT_ASIA,          0, 477, 193, -1, 
	{8, 15, 20, 27, 28, 30} },
    { "Central America", CNT_NORTHAMERICA, 0, 87, 165, -1, 
	{17, 18, 32, -1, -1, -1}}, 
    { "Northern Africa", CNT_AFRICA,    0, 339, 214, -1, 
	{19, 20, 27, 30, 33, 35} },
    { "Egypt",       CNT_AFRICA,        0, 407, 194, -1, 
	{20, 24, 26, 30, -1, -1} },
    { "India",       CNT_ASIA,          0, 572, 194, -1,
	{15, 22, 24, 29, -1, -1} },
    { "Siam",        CNT_ASIA,          0, 637, 207, -1, 
	{22, 28, 31, -1, -1, -1} },
    { "Eastern Africa", CNT_AFRICA,     0, 437, 231, -1, 
	{24, 26, 27, 35, 37, 40} },
    { "Indonesia",   CNT_AUSTRALIA,     0, 672, 262, -1, 
	{29, 36, 38, -1, -1, -1} },
    { "Venezuela",   CNT_SOUTHAMERICA,  0, 188, 230, -1, 
	{25, 33, 34, -1, -1, -1} },
    { "Brazil",      CNT_SOUTHAMERICA,   0, 233, 277, -1, 
	{26, 32, 34, 41, -1, -1} },
    { "Peru",        CNT_SOUTHAMERICA,   0, 194, 294, -1, 
	{32, 33, 41, -1, -1, -1}},
    { "Congo",       CNT_AFRICA,         0, 410, 271, -1, 
	{26, 30, 37, -1, -1, -1} },
    { "New Guinea",  CNT_AUSTRALIA,      0, 751, 276, -1, 
	{31, 38, 39, -1, -1, -1} },
    { "South Africa", CNT_AFRICA,        0, 416, 326, -1, 
	{30, 35, 40, -1, -1, -1} },
    { "Western Australia", CNT_AUSTRALIA, 0, 700, 335, -1, 
	{31, 36, 39, -1, -1, -1} },
    { "Eastern Australia", CNT_AUSTRALIA, 0, 756, 343, -1, 
	{36, 38, -1, -1, -1, -1} },
    { "Madagascar",  CNT_AFRICA,         0, 479, 321, -1, 
	{30, 37, -1, -1, -1, -1} },
    { "Argentina",   CNT_SOUTHAMERICA,   0, 189, 352, -1, 
	{33, 34, -1, -1, -1, -1} },
  }
};


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     07.17.94  ESF  Created.
 *     08.13.94  ESF  Rewrote.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void RISK_InitObject(Int32 iThisMode, 
		     void (*MessageCallback)(Int32, void *, Int32, Int32),
		     void (*FailureCallback)(CString, Int32))
{
  iMode = iThisMode;
  RiskGame.MessageCallback = MessageCallback;
  RiskGame.FailureCallback = FailureCallback;
}


/************************************************************************ 
 *  FUNCTION: _RISK_Replicate
 *  HISTORY: 
 *     05.03.94  ESF  Created.
 *     06.24.94  ESF  Fixed memory leak.
 *     07.16.94  ESF  Added assert.
 *     08.16.94  ESF  Changed so that both can get callback (Server/Client).
 *     08.18.94  ESF  Changed so that callback handles actual replication.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void _RISK_Replicate(Int32 iField, Int32 iIndex1, Int32 iIndex2)
{
  void   *pvMess;
  Int32   iMessType;

  /* Get the message and the message type */
  pvMess    = _RISK_GetMsg(iField, iIndex1, iIndex2);
  iMessType = _RISK_GetMsgType(iField);
  
  D_Assert(pvMess, "Got back NULL message!");
  
  /* Call the message handler.  If there isn't one, then
   * we have a problem, since it handles the actual replication.
   * This involves sending a message to the server, or broadcasting
   * a message out, depending on whether a client or server has
   * registered this callback.  The last parameter doesn't matter in
   * this case, because it's an outgoing message (the last parameter
   * is the message source).
   */
  
  D_Assert(RiskGame.MessageCallback, "MessageCallback needs to be set!");
  RiskGame.MessageCallback(iMessType, pvMess, MESS_OUTGOING, -1);
  
  /* Delete the memory the message was taking */
  MEM_Free(pvMess);
}


/************************************************************************ 
 *  FUNCTION: RISK_SelectiveReplicate
 *  HISTORY: 
 *     05.06.94  ESF  Created.
 *     05.10.94  ESF  Added needed check for existence of callback.
 *     07.16.94  ESF  Fixed loop bug, added assertions.
 *     08.28.94  ESF  Changed to take indices, instead of ranges.
 *     01.01.95  ESF  Fixed to remove a memory leak.
 *  PURPOSE: 
 *     Sends data to a client, for purposes of data synchronicity.
 *  NOTES: 
 ************************************************************************/
void RISK_SelectiveReplicate(Int32 iSocket, Int32 iField, Int32 iIndex1, 
			     Int32 iIndex2)
{
  void *pvMess;

  (void)RISK_SendMessage(iSocket, 
			 _RISK_GetMsgType(iField), 
			 (pvMess = _RISK_GetMsg(iField, iIndex1, iIndex2)));
  
  /* Delete the memory the message took up */
  MEM_Free(pvMess);
}


/************************************************************************ 
 *  FUNCTION: RISK_ResetObj
 *  HISTORY: 
 *     05.02.94  ESF  Created.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void RISK_ResetObj(void)
{
  Int32 i;

  RISK_SetNumPlayers(0);
  RISK_SetNumLivePlayers(0);

  /* Init the needed fields of the players */
  for (i=0; i!=MAX_PLAYERS; i++)
    {
      RISK_SetAttackModeOfPlayer(i, 1);
      RISK_SetStateOfPlayer(i, TRUE);
      RISK_SetClientOfPlayer(i, -1);
      RISK_SetNumCountriesOfPlayer(i, 0);
      RISK_SetNumArmiesOfPlayer(i, 0);
      RISK_SetNumCardsOfPlayer(i, 0);
    }

  /* Init the needed fields of the countries */
  for (i=0; i!=NUM_COUNTRIES; i++)
    {
      RISK_SetNumArmiesOfCountry(i, 0);
      RISK_SetOwnerOfCountry(i, -1);
    }
}


/************************************************************************ 
 *  FUNCTION: RISK_ResetGame
 *  HISTORY: 
 *     05.12.94  ESF  Created.
 *  PURPOSE:
 *     New game, keep players.
 *  NOTES: 
 ************************************************************************/
void RISK_ResetGame(void)
{
  Int32 i;

  /* Bring the players to life again */
  RISK_SetNumLivePlayers(RISK_GetNumPlayers());

  /* Init the needed fields of the players */
  for (i=0; i!=MAX_PLAYERS; i++)
    {
      RISK_SetStateOfPlayer(i, TRUE);
      RISK_SetNumCountriesOfPlayer(i, 0);
      RISK_SetNumCardsOfPlayer(i, 0);
    }

  /* Init the needed fields of the countries */
  for (i=0; i!=NUM_COUNTRIES; i++)
    {
      RISK_SetNumArmiesOfCountry(i, 0);
      RISK_SetOwnerOfCountry(i, -1);
    }
}


/************************************************************************ 
 *  FUNCTION: RISK_ProcessMessage
 *  HISTORY: 
 *     05.02.94  ESF  Created.
 *     05.10.94  ESF  Added needed check for existence of callback.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void RISK_ProcessMessage(Int32 iMessType, void *pvMess, Int32 iSrc)
{
  Int32               iIndex1, iIndex2, iValue, iField;
  CString             strValue;
  MsgObjIntUpdate    *pIntMess = (MsgObjIntUpdate *)pvMess;
  MsgObjStrUpdate    *pStrMess = (MsgObjStrUpdate *)pvMess;

  D_Assert(pvMess, "pMess is NULL!");

  if (iMessType == MSG_OBJINTUPDATE)
    {
      iField  = pIntMess->iField;
      iIndex1 = pIntMess->iIndex1;
      iIndex2 = pIntMess->iIndex2;
      iValue  = pIntMess->iNewValue;
    }
  else
    {
      iField  = pStrMess->iField;
      iIndex1 = pStrMess->iIndex1;
      iIndex2 = pStrMess->iIndex2;
      strValue  = pStrMess->strNewValue;
    }

  D_Assert(RiskGame.MessageCallback, "MessageCallback needs to be set!");
  RiskGame.MessageCallback(iMessType, pvMess, MESS_INCOMING, iSrc);
  
  switch (iField)
    {
    case PLR_ATTACKMODE:
      RiskGame.pPlayers[iIndex1].iAttackMode = iValue;
      break;
    case PLR_DICEMODE:
      RiskGame.pPlayers[iIndex1].iDiceMode = iValue;
      break;
    case PLR_MSGDSTMODE:
      RiskGame.pPlayers[iIndex1].iMsgDstMode = iValue;
      break;
    case PLR_STATE:
      RiskGame.pPlayers[iIndex1].iState = iValue;
      break;
    case PLR_CLIENT:
      RiskGame.pPlayers[iIndex1].iClient = iValue;
      break;
    case PLR_NUMCOUNTRIES:
      RiskGame.pPlayers[iIndex1].iCountriesOwned = iValue;
      break;
    case PLR_NUMARMIES:
      RiskGame.pPlayers[iIndex1].iNumArmies = iValue;
      break;
    case PLR_NUMCARDS:
      RiskGame.pPlayers[iIndex1].iNumCards = iValue;
      break;
    case PLR_SPECIES:
      RiskGame.pPlayers[iIndex1].iSpecies = iValue;
      break;
    case PLR_CARD:
      RiskGame.pPlayers[iIndex1].piCards[iIndex2] = iValue;
      break;
    case CNT_NUMARMIES:
      RiskGame.pCountries[iIndex1].iNumArmies = iValue;
      break;
    case CNT_OWNER:
      RiskGame.pCountries[iIndex1].piOwner = iValue;
      break;
    case GEN_NUMPLAYERS:    
      RiskGame.iNumPlayers = iValue;
      break;
    case GEN_NUMLIVEPLAYERS:
      RiskGame.iNumLivePlayers = iValue;
      break;
    case PLR_NAME:
      if (RiskGame.pPlayers[iIndex1].strName != NULL)
	MEM_Free(RiskGame.pPlayers[iIndex1].strName);
      RiskGame.pPlayers[iIndex1].strName = 
	(CString)MEM_Alloc(strlen(strValue)+1);
      strcpy(RiskGame.pPlayers[iIndex1].strName, strValue);
      break;
    case PLR_COLORSTRING:
      if (RiskGame.pPlayers[iIndex1].strColor != NULL)
	MEM_Free(RiskGame.pPlayers[iIndex1].strColor);
      RiskGame.pPlayers[iIndex1].strColor = 
	(CString)MEM_Alloc(strlen(strValue)+1);
      strcpy(RiskGame.pPlayers[iIndex1].strColor, strValue);
      break;

    default:
      D_Assert(FALSE, "Trying to modify read-only field of object!");
    }
}


/***************************************/
void RISK_SetNumPlayers(Int32 iNumPlayers) 
{
  RiskGame.iNumPlayers = iNumPlayers;
  _RISK_Replicate(GEN_NUMPLAYERS, 0, 0);
}

/***************************************/
void RISK_SetAttackModeOfPlayer(Int32 iPlayer, Int32 iMode)
{
  RiskGame.pPlayers[iPlayer].iAttackMode = iMode;
  _RISK_Replicate(PLR_ATTACKMODE, iPlayer, 0);
}

/***************************************/
void RISK_SetDiceModeOfPlayer(Int32 iPlayer, Int32 iMode)
{
  RiskGame.pPlayers[iPlayer].iDiceMode = iMode;
  _RISK_Replicate(PLR_DICEMODE, iPlayer, 0);
}

/***************************************/
void RISK_SetMsgDstModeOfPlayer(Int32 iPlayer, Int32 iMode)
{
  RiskGame.pPlayers[iPlayer].iMsgDstMode = iMode;
  _RISK_Replicate(PLR_MSGDSTMODE, iPlayer, 0);
}

/***************************************/
void RISK_SetStateOfPlayer(Int32 iPlayer, Int32 iState)
{
  RiskGame.pPlayers[iPlayer].iState = iState;
  _RISK_Replicate(PLR_STATE, iPlayer, 0);
}

/***************************************/
void RISK_SetClientOfPlayer(Int32 iPlayer, Int32 iClient)
{
  RiskGame.pPlayers[iPlayer].iClient = iClient;
  _RISK_Replicate(PLR_CLIENT, iPlayer, 0);
}

/***************************************/
void RISK_SetNumCountriesOfPlayer(Int32 iPlayer, Int32 iNumCountries)
{
  RiskGame.pPlayers[iPlayer].iCountriesOwned = iNumCountries;
  _RISK_Replicate(PLR_NUMCOUNTRIES, iPlayer, 0);
}

/***************************************/
void RISK_SetNumArmiesOfPlayer(Int32 iPlayer, Int32 iNumArmies)
{
  RiskGame.pPlayers[iPlayer].iNumArmies = iNumArmies;
  _RISK_Replicate(PLR_NUMARMIES, iPlayer, 0);
}

/***************************************/
void RISK_SetNumCardsOfPlayer(Int32 iPlayer, Int32 iNumCards)
{
  RiskGame.pPlayers[iPlayer].iNumCards = iNumCards;
  _RISK_Replicate(PLR_NUMCARDS, iPlayer, 0);
}

/***************************************/
void RISK_SetNameOfPlayer(Int32 iPlayer, CString strName)
{
  if (RiskGame.pPlayers[iPlayer].strName != NULL)
    MEM_Free(RiskGame.pPlayers[iPlayer].strName);
  RiskGame.pPlayers[iPlayer].strName = (CString)MEM_Alloc(strlen(strName)+1);
  strcpy(RiskGame.pPlayers[iPlayer].strName, strName);

  _RISK_Replicate(PLR_NAME, iPlayer, 0);
}

/***************************************/
void RISK_SetColorCStringOfPlayer(Int32 iPlayer, CString strColor)
{
  if (RiskGame.pPlayers[iPlayer].strColor != NULL)
    MEM_Free(RiskGame.pPlayers[iPlayer].strColor);
  RiskGame.pPlayers[iPlayer].strColor = (CString)MEM_Alloc(strlen(strColor)+1);
  strcpy(RiskGame.pPlayers[iPlayer].strColor, strColor);

  _RISK_Replicate(PLR_COLORSTRING, iPlayer, 0);
}

/***************************************/
void RISK_SetCardOfPlayer(Int32 iPlayer, Int32 iCard, Int32 iValue)
{
  RiskGame.pPlayers[iPlayer].piCards[iCard] = iValue;
  _RISK_Replicate(PLR_CARD, iPlayer, iCard);
}

/***************************************/
void RISK_SetSpeciesOfPlayer(Int32 iPlayer, Int32 iSpecies)
{
  RiskGame.pPlayers[iPlayer].iSpecies = iSpecies;
  _RISK_Replicate(PLR_SPECIES, iPlayer, iSpecies);
}

/***************************************/
void RISK_SetNumArmiesOfCountry(Int32 iCountry, Int32 iNumArmies)
{
  RiskGame.pCountries[iCountry].iNumArmies = iNumArmies;
  _RISK_Replicate(CNT_NUMARMIES, iCountry, 0);
}

/***************************************/
void RISK_SetOwnerOfCountry(Int32 iCountry, Int32 iOwner)
{
  RiskGame.pCountries[iCountry].piOwner = iOwner;
  _RISK_Replicate(CNT_OWNER, iCountry, 0);
}

/***************************************/
void RISK_SetNumLivePlayers(Int32 iNumLivePlayers)
{
  RiskGame.iNumLivePlayers = iNumLivePlayers;
  _RISK_Replicate(GEN_NUMLIVEPLAYERS, 0, 0);
}


/***************************************/
Int32 RISK_GetNumPlayers(void) 
{
  return RiskGame.iNumPlayers;
}

/***************************************/
Int32 RISK_GetAttackModeOfPlayer(Int32 iPlayer)
{
  return RiskGame.pPlayers[iPlayer].iAttackMode;
}

/***************************************/
Int32 RISK_GetMsgDstModeOfPlayer(Int32 iPlayer)
{
  return RiskGame.pPlayers[iPlayer].iMsgDstMode;
}

/***************************************/
Int32 RISK_GetDiceModeOfPlayer(Int32 iPlayer)
{
  return RiskGame.pPlayers[iPlayer].iDiceMode;
}

/***************************************/
Int32 RISK_GetStateOfPlayer(Int32 iPlayer)
{
  return RiskGame.pPlayers[iPlayer].iState;
}

/***************************************/
Int32 RISK_GetClientOfPlayer(Int32 iPlayer)
{
  return RiskGame.pPlayers[iPlayer].iClient;
}

/***************************************/
Int32 RISK_GetNumCountriesOfPlayer(Int32 iPlayer)
{
  return RiskGame.pPlayers[iPlayer].iCountriesOwned;
}

/***************************************/
Int32 RISK_GetNumArmiesOfPlayer(Int32 iPlayer)
{
  return RiskGame.pPlayers[iPlayer].iNumArmies;
}

/***************************************/
Int32 RISK_GetNumCardsOfPlayer(Int32 iPlayer)
{
  return RiskGame.pPlayers[iPlayer].iNumCards;
}

/***************************************/
Int32 RISK_GetSpeciesOfPlayer(Int32 iPlayer)
{
  return RiskGame.pPlayers[iPlayer].iSpecies;
}

/***************************************/
Int32 RISK_GetValueOfContinent(Int32 iContinent)
{
  return RiskGame.pContinents[iContinent].iValue;
}

/***************************************/
CString RISK_GetNameOfContinent(Int32 iContinent)
{
  return RiskGame.pContinents[iContinent].strName;
}

/***************************************/
Int32 RISK_GetNumCountriesOfContinent(Int32 iContinent)
{
  return RiskGame.pContinents[iContinent].iNumCountries;
}

/***************************************/
CString RISK_GetNameOfPlayer(Int32 iPlayer) 
{ 
  return RiskGame.pPlayers[iPlayer].strName;
}

/***************************************/
CString RISK_GetColorCStringOfPlayer(Int32 iPlayer)
{
  return RiskGame.pPlayers[iPlayer].strColor;
}

/***************************************/
Int32 RISK_GetCardOfPlayer(Int32 iPlayer, Int32 iCard)
{
  return RiskGame.pPlayers[iPlayer].piCards[iCard];
}

/***************************************/
CString RISK_GetNameOfCountry(Int32 iCountry)
{
  return RiskGame.pCountries[iCountry].strName;
}

/***************************************/
Int32 RISK_GetContinentOfCountry(Int32 iCountry)
{
  return RiskGame.pCountries[iCountry].iContinent;
}

/***************************************/
Int32 RISK_GetNumArmiesOfCountry(Int32 iCountry)
{
  return RiskGame.pCountries[iCountry].iNumArmies;
}

/***************************************/
Int32 RISK_GetOwnerOfCountry(Int32 iCountry)
{
  return RiskGame.pCountries[iCountry].piOwner;
}

/***************************************/
Int32 RISK_GetAdjCountryOfCountry(Int32 iCountry, Int32 iIndex)
{
  return RiskGame.pCountries[iCountry].piAdjCountries[iIndex];
}

/***************************************/
Int32 RISK_GetNumLivePlayers(void)
{
  return RiskGame.iNumLivePlayers;
}

/***************************************/
Int32 RISK_GetTextXOfCountry(Int32 iCountry)
{
  return RiskGame.pCountries[iCountry].iTextX;
}

/***************************************/
Int32 RISK_GetTextYOfCountry(Int32 iCountry)
{
  return RiskGame.pCountries[iCountry].iTextY;
}

/***************************************/
Int32 _RISK_GetMsgType(Int32 iField)
{
  switch (iField)
    {
    case PLR_ATTACKMODE:
    case PLR_DICEMODE:
    case PLR_MSGDSTMODE:
    case PLR_STATE:     
    case PLR_SPECIES:
    case PLR_CLIENT:    
    case PLR_NUMCOUNTRIES:
    case PLR_NUMARMIES:   
    case PLR_NUMCARDS:    
    case PLR_CARD:        
    case CNT_CONTINENT:   
    case CNT_NUMARMIES:   
    case CNT_OWNER:     
    case CNT_ADJCOUNTRY:  
    case CNT_TEXTX:       
    case CNT_TEXTY:       
    case CON_VALUE:         
    case CON_NUMCOUNTRIES:  
    case GEN_NUMPLAYERS:    
    case GEN_NUMLIVEPLAYERS:
      return MSG_OBJINTUPDATE;
      break;
      
    case PLR_NAME:        
    case PLR_COLORSTRING: 
    case CNT_NAME:        
    case CON_NAME:
      return MSG_OBJSTRUPDATE;
      break;    
      
    default:
      D_Assert(FALSE, "Shouldn't be here!");
    }
  
  /* For the compiler */
  return (0);
}

/***************************************/
void *_RISK_GetMsg(Int32 iField, Int32 iIndex1, Int32 iIndex2)
{
  MsgObjIntUpdate *pIntMess = 
    (MsgObjIntUpdate *)MEM_Alloc(sizeof(MsgObjIntUpdate));
  MsgObjStrUpdate *pStrMess = 
    (MsgObjStrUpdate *)MEM_Alloc(sizeof(MsgObjStrUpdate));
  
  pIntMess->iField  = pStrMess->iField  = iField;
  pIntMess->iIndex1 = pStrMess->iIndex1 = iIndex1;
  pIntMess->iIndex2 = pStrMess->iIndex2 = iIndex2;

  switch (iField)
    {
    case PLR_ATTACKMODE:
      pIntMess->iNewValue = RiskGame.pPlayers[iIndex1].iAttackMode;
      MEM_Free(pStrMess);
      return (void *)pIntMess;
      break;
    case PLR_DICEMODE:
      pIntMess->iNewValue = RiskGame.pPlayers[iIndex1].iDiceMode;
      MEM_Free(pStrMess);
      return (void *)pIntMess;
      break;
    case PLR_MSGDSTMODE:
      pIntMess->iNewValue = RiskGame.pPlayers[iIndex1].iMsgDstMode;
      MEM_Free(pStrMess);
      return (void *)pIntMess;
      break;
    case PLR_STATE:
      pIntMess->iNewValue = RiskGame.pPlayers[iIndex1].iState;
      MEM_Free(pStrMess);
      return (void *)pIntMess;
      break;
    case PLR_CLIENT:
      pIntMess->iNewValue = RiskGame.pPlayers[iIndex1].iClient;
      MEM_Free(pStrMess);
      return (void *)pIntMess;
      break;
    case PLR_SPECIES:
      pIntMess->iNewValue = RiskGame.pPlayers[iIndex1].iSpecies;
      MEM_Free(pStrMess);
      return (void *)pIntMess;
      break;
    case PLR_NUMCOUNTRIES:
      pIntMess->iNewValue = RiskGame.pPlayers[iIndex1].iCountriesOwned;
      MEM_Free(pStrMess);
      return (void *)pIntMess;
      break;
    case PLR_NUMARMIES:
      pIntMess->iNewValue = RiskGame.pPlayers[iIndex1].iNumArmies;
      MEM_Free(pStrMess);
      return (void *)pIntMess;
      break;
    case PLR_NUMCARDS:
      pIntMess->iNewValue = RiskGame.pPlayers[iIndex1].iNumCards;
      MEM_Free(pStrMess);
      return (void *)pIntMess;
      break;
    case PLR_CARD:
      pIntMess->iNewValue = RiskGame.pPlayers[iIndex1].piCards[iIndex2];
      MEM_Free(pStrMess);
      return (void *)pIntMess;
      break;
    case CNT_NUMARMIES:
      pIntMess->iNewValue = RiskGame.pCountries[iIndex1].iNumArmies;
      MEM_Free(pStrMess);
      return (void *)pIntMess;
      break;
    case CNT_OWNER:
      pIntMess->iNewValue = RiskGame.pCountries[iIndex1].piOwner;
      MEM_Free(pStrMess);
      return (void *)pIntMess;
      break;
    case GEN_NUMPLAYERS:
      pIntMess->iNewValue = RiskGame.iNumPlayers;
      MEM_Free(pStrMess);
      return (void *)pIntMess;
      break;
    case GEN_NUMLIVEPLAYERS:
      pIntMess->iNewValue = RiskGame.iNumLivePlayers;
      MEM_Free(pStrMess);
      return (void *)pIntMess;      
      break;

    case PLR_NAME:
      pStrMess->strNewValue = RiskGame.pPlayers[iIndex1].strName;
      MEM_Free(pIntMess);
      return (void *)pStrMess;
      break;
    case PLR_COLORSTRING:
      pStrMess->strNewValue = RiskGame.pPlayers[iIndex1].strColor;
      MEM_Free(pIntMess);
      return (void *)pStrMess;
      break;

    default:
      D_Assert(FALSE, "Shouldn't be here!");
    }

  /* For the compiler */
  return NULL;
}


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     08.03.94  ESF  Created.
 *     08.12.94  ESF  Made nicer, integrated with sister routine.
 *     08.17.94  ESF  Added string parameter.
 *     08.26.94  ESF  Deleted integer parameter :)
 *     08.28.94  ESF  Added callback for eventual recovery.
 *     10.01.94  ESF  Added assertion.
 *  PURPOSE: 
 *     Eventually this will handle fault tolerance.
 *  NOTES: 
 ************************************************************************/
void RISK_ObjectFailure(CString strReason, Int32 iCommLink)
{
  D_Assert(RiskGame.FailureCallback, "Need to register a failure callback!");
  RiskGame.FailureCallback(strReason, iCommLink);
}


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     05.15.94  ESF  Created.
 *     07.25.94  ESF  Fixed a bug with the for loop, off-by-one.
 *     08.08.94  ESF  Moved here from server.c
 *     08.27.94  ESF  Renamed.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
Int32 RISK_GetNthLivePlayer(Int32 iIndex)
{
  Int32 i, iCount;

  D_Assert(iIndex>=0 && iIndex<RISK_GetNumLivePlayers(), 
	   "GetNthLivePlayer passed a bogus index!");

  /* Increment, because the index passed in will start at zero */
  iIndex++;

  /* Loop through, looking for the iIndex'th player
   * who's state is TRUE (alive).
   */

  for (i=0, iCount=0; i!=MAX_PLAYERS && iIndex!=iCount; i++)
    if (RISK_GetClientOfPlayer(i) != -1 &&
	RISK_GetStateOfPlayer(i) == TRUE)
      iCount++;
  
  if (iIndex == iCount)
    return (i-1);
  
  /* Something wierd... */
  D_Assert(FALSE, "Something wierd happened!");
  
  /* Make the compiler happy */
  return (-1);
}


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     10.02.94  ESF  Created.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
Int32 RISK_GetNthPlayerAtClient(Int32 iClient, Int32 iIndex)
{
  Int32 i, iCount;

  D_Assert(iIndex>=0 && iIndex<RISK_GetNumPlayers(), 
	   "GetNthPlayerAtClient passed a bogus index!");

  /* Increment, because the index passed in will start at zero */
  iIndex++;

  /* Loop through, looking for the iIndex'th player */
  for (i=0, iCount=0; i!=MAX_PLAYERS && iIndex!=iCount; i++)
    if (RISK_GetClientOfPlayer(i) == iClient)
      iCount++;
  
  if (iIndex == iCount)
    return (i-1);

  /* Something wierd... */
  D_Assert(FALSE, "Something wierd happened!");

  /* Make the compiler happy */
  return (-1);
}


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     08.27.94  ESF  Created.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
Int32 RISK_GetNthPlayer(Int32 iIndex)
{
  Int32 i, iCount;

  D_Assert(iIndex>=0 && iIndex<RISK_GetNumPlayers(), 
	   "GetNthPlayer passed a bogus index!");

  /* Increment, because the index passed in will start at zero */
  iIndex++;

  /* Loop through, looking for the iIndex'th player */
  for (i=0, iCount=0; i!=MAX_PLAYERS && iIndex!=iCount; i++)
    if (RISK_GetClientOfPlayer(i) != -1)
      iCount++;
  
  if (iIndex == iCount)
    return (i-1);

  /* Something wierd... */
  D_Assert(FALSE, "Something wierd happened!");

  /* Make the compiler happy */
  return (-1);
}


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     08.12.94  ESF  Created.
 *     08.17.94  ESF  Fixed the failure handling.
 *     10.01.94  ESF  Added return parameter.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
Int32 RISK_SendMessage(Int32 iDest, Int32 iMessType, void *pvMessage)
{
  /* Try to send it, if it fails, call the appropriate recovery routines */
  if (NET_SendMessage(iDest, iMessType, pvMessage) < 0)
    {
      RISK_ObjectFailure("SendMessage failed.", iDest);
      return 0;
    }
  else
    return 1;
}


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     08.12.94  ESF  Created.
 *     08.17.94  ESF  Fixed the failure handling.
 *     10.01.94  ESF  Added return parameter.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
Int32 RISK_ReceiveMessage(Int32 iSource, Int32 *piMessType, void **ppvMessage)
{
  /* Try to send it, if it fails, call the appropriate recovery routines */
  if (NET_RecvMessage(iSource, piMessType, ppvMessage) < 0)
    {
      RISK_ObjectFailure("ReceiveMessage failed.", iSource);
      return 0;
    }
  else
    return 1;
}


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     08.12.94  ESF  Created.
 *     08.17.94  ESF  Fixed the failure handling.
 *     10.01.94  ESF  Added return parameter.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
Int32 RISK_SendSyncMessage(Int32 iCommLink, Int32 iMessType, void *pvMessage, 
			 Int32 iReturnMessType,
			 void (*CBK_MessageReceived)(Int32, void *))
{
  if (NET_SendSyncMessage(iCommLink, iMessType, pvMessage,
			  iReturnMessType, CBK_MessageReceived) < 0)
    {
      RISK_ObjectFailure("SendSyncMessage failed", iCommLink);
      return 0;
    }
  else
    return 1;
}


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     08.31.94  ESF  Created.
 *  PURPOSE: 
 *  NOTES: 
 *     This is a worst case O^2 algorithm.  It could be sped up to
 *   constant time if the number of players was kept track of, but
 *   I doubt this will be much of an overhead.
 ************************************************************************/
Int32 RISK_GetNumPlayersOfClient(Int32 iClient)
{
  Int32 i, iCount;

  D_Assert(iClient>=0 && iClient<MAX_CLIENTS, "Bogus client!");

  /* Count up all of the players at this client */
  for (i=iCount=0; i!=RISK_GetNumPlayers(); i++)
    if (RISK_GetClientOfPlayer(RISK_GetNthPlayer(i)) == iClient)
      iCount++;

  return iCount;
}


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     08.31.94  ESF  Created.
 *     10.01.94  ESF  Fixed a bug, changed NumPlayers to NumLivePlayers.
 *  PURPOSE: 
 *  NOTES: 
 *     This is a worst case O^2 algorithm.  It could be sped up to
 *   constant time if the number of live players was kept track of, but
 *   I doubt this will be much of an overhead.
 ************************************************************************/
Int32 RISK_GetNumLivePlayersOfClient(Int32 iClient)
{
  Int32 i, iCount;

  D_Assert(iClient>=0 && iClient<MAX_CLIENTS, "Bogus client!");

  /* Count up all of the players at this client */
  for (i=iCount=0; i!=RISK_GetNumLivePlayers(); i++)
    if (RISK_GetClientOfPlayer(RISK_GetNthLivePlayer(i)) == iClient)
      iCount++;

  return iCount;
}


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     09.14.94  ESF  Created.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void RISK_SaveObject(CString strFileName)
{
  /* I need to write _RISK_[Read|Write][CString|Int32eger], just like
   * for network.  Use the htonl() so that files saved on one
   * endianness can be read on another.  I don't need to save the
   * read-only fields, like the ContinentDatabase stuff.  Since I
   * will save the ID of the field, old game files should work for
   * future version of the game, as long as I don't fiddle with the IDs.
   */

  D_Assert(FALSE, "Not yet implemented.");
}


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     09.14.94  ESF  Created.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void RISK_LoadObject(CString strFileName)
{
  D_Assert(FALSE, "Not yet implemented.");
}
