/*****************************************************************************
 *                                                                           *
 *  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 <sys/types.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>

#include "utils.h"
#include "network.h"
#include "client.h"
#include "riskgame.h"
#include "gui-func.h"
#include "types.h"
#include "dice.h"
#include "debug.h"

/* Private data */
typedef struct _Client
{
  Int32  iCommLink;
} Client;

Client   pClients[MAX_CLIENTS];

/* Used for the UTIL_*Notify routines */ 
Flag     piCountryLightCount[NUM_COUNTRIES];

/* piPlayerColors is each client's copy of the function 
 * [player |--> X color index], because each client will
 * probably allocate colors in a different region of the 
 * colormap.
 */

Int32    piPlayerColors[MAX_PLAYERS];

Int32    iThisClient;
CString  strClientName;

extern  Char    strScratch[256];
extern  Int32   iReply;

/* Do something with these... */
void    FatalError(CString strError, Int32 iRetVal);


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     01.23.94  ESF  Created.
 *     02.22.94  ESF  Cleaned up a bit, removing warnings.
 *     08.10.94  ESF  Cleanup up to make more OS independant.
 *     01.17.95  ESF  Fixed memory leak.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void CLNT_Init(int argc, char **argv)
{
  struct sockaddr_in   server;
  struct hostent      *hp;
  char                 strHostName[MAXHOSTNAMELEN + 1];
  Int32                iCommLink, i;
  Int32                iMessageType;
  void                *pvMessage;
  MsgRegisterClient    msgRegisterClient;

  /* Init the client structures */
  for (i=0; i!=NUM_COUNTRIES; i++)
    CLNT_SetLightCountOfCountry(i, 0);

  for (i=0; i!=MAX_CLIENTS; i++)
    CLNT_SetCommLinkOfClient(i, -1);
  
  /* We want to ignore this signal, because we don't want a I/O
   * failure to terminate the program.
   */

  signal(SIGPIPE, SIG_IGN);

  /* Create sockets */
  if ((iCommLink = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    FatalError("CLIENT: Opening stream connection", 1);
  
  /* Connect socket using name specified by command line */
  server.sin_family = AF_INET;
  
  if ((hp = gethostbyname(argv[1])) == 0)
    {
      sprintf(strScratch, "CLIENT: The host `%s' is unknown.", argv[1]);
      FatalError(strScratch, 1);
    }
  
  memcpy(&server.sin_addr, hp->h_addr, hp->h_length);
  server.sin_port = htons(RISK_PORT);

  /* Send the server name information through the sockets so it can ID
   * the sending clients, and discover the pair of sockets that belong
   * to each client.  The server sends back the client ID.
   */

  gethostname(strHostName, sizeof(strHostName));
  msgRegisterClient.strClientAddress = 
    (CString)MEM_Alloc(strlen(strHostName)+16);
  sprintf(msgRegisterClient.strClientAddress, "%s[%d]", strHostName, getpid());
  strClientName = msgRegisterClient.strClientAddress;
  
  /* Connect to the server.  Connect the first socket, send 
   * MSG_REGISTER down it, then connect the second, do the same,
   * and then wait for a MSG_CLIENTIDENT message to be send back.
   */

  if (connect(iCommLink, (struct sockaddr *)&server, sizeof(server)) < 0)
    {
      printf("CLIENT: Cannot connect to a Frisk server on `%s'.\n"
	     "        One probably needs to be started by running\n"
	     "        `friskserver' on the machine.\n", argv[1]);
      FatalError(NULL, 0);
    }
  
  (void)RISK_SendMessage(iCommLink, MSG_REGISTERCLIENT, &msgRegisterClient);
  printf("CLIENT: Established CommLink to server.\n");
  printf("CLIENT: Waiting for server to send client ID...");
  fflush(stdout);
  (void)RISK_ReceiveMessage(iCommLink, &iMessageType, &pvMessage);
  printf("Done.\n");
  
  if (iMessageType == MSG_EXIT)
    FatalError("CLIENT: Can't connect, server is full!  I'm impressed...", -1);
  else if (iMessageType == MSG_CLIENTIDENT)
    {
      /* Set the ID of the client, and then set the sockets of the client */
      iThisClient = ((MsgClientIdent *)pvMessage)->iClientID;
      CLNT_SetCommLinkOfClient(iThisClient, iCommLink);
    }
  else
    FatalError("CLIENT: Server is not following protocol!", -1);

  /* Free up memory */
  NET_DeleteMessage(MSG_CLIENTIDENT, pvMessage);
}


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     08.28.94  ESF  Created.
 *     09.28.94  ESF  Fixed to popup a message before exiting.
 *  PURPOSE: 
 *  NOTES: 
 ************************************************************************/
void CLNT_RecoverFailure(CString strReason, Int32 iCommLink)
{
  sprintf(strScratch, "The server has failed (%s)", 
	  strReason);
  printf("CLIENT: %s\n", strScratch);
  printf("CLIENT: Cannot recover from this, future version will.\n");
  
  (void)UTIL_PopupDialog("Object Failure", 
			 "The server has failed, cannot recover.",
			 1, "Ok", NULL, NULL);
  exit(0);
}


/************************************************************************ 
 *  FUNCTION:
 *  HISTORY: 
 *     05.12.94  ESF  Created.
 *     05.15.94  ESF  Fixed race condition, go to server for player.
 *  PURPOSE:
 *  NOTES: 
 ************************************************************************/
Int32 CLNT_AllocPlayer(void (*pfMsgHandler)(Int32, void *))
{
  (void)RISK_SendSyncMessage(CLNT_GetCommLinkOfClient(iThisClient), 
			     MSG_ALLOCPLAYER, NULL,
			     MSG_REPLYPACKET, pfMsgHandler);
  return(iReply);
}


/************************************************************************ 
 *  FUNCTION: 
 *  HISTORY: 
 *     05.12.94  ESF  Created.
 *     05.15.94  ESF  Fixed race condition, go to server.
 *     07.16.94  ESF  Added assert.
 *  PURPOSE:
 *  NOTES: 
 ************************************************************************/
void CLNT_FreePlayer(Int32 i)
{
  MsgFreePlayer mess;

  /* Tell the server that this player ID is free */
  mess.iPlayer = i;

  (void)RISK_SendMessage(CLNT_GetCommLinkOfClient(iThisClient), 
			 MSG_FREEPLAYER, &mess);
}


/***************************************/
void CLNT_SetCommLinkOfClient(Int32 iNumClient, Int32 iCommLink)
{
  D_Assert(iNumClient>=0 && iNumClient<MAX_CLIENTS, 
	   "Client out of range!");
  
  pClients[iNumClient].iCommLink = iCommLink;
}

/***************************************/
Int32 CLNT_GetCommLinkOfClient(Int32 iNumClient)
{
  D_Assert(iNumClient>=0 && iNumClient<MAX_CLIENTS, 
	   "Client out of range!");

  return pClients[iNumClient].iCommLink;
}

/***************************************/
Int32 CLNT_GetLightCountOfCountry(Int32 iCountry)
{
  D_Assert(iCountry >= 0 && iCountry < NUM_COUNTRIES, "Country out of range");
  return piCountryLightCount[iCountry];
}

/***************************************/
void CLNT_SetLightCountOfCountry(Int32 iCountry, Int32 iLightCount)
{
  D_Assert(iCountry >= 0 && iCountry < NUM_COUNTRIES, "Country out of range");
  D_Assert(iLightCount>=0, "Bogus LightCount");
  piCountryLightCount[iCountry] = iLightCount;
}

/********************************************/
void FatalError(CString strError, Int32 iRetVal)
{
  if (strError)
    printf("%s\n", strError);
  exit(iRetVal);
}


