/********************************************************************************/
/*																				*/
/*	NetSynch.c																	*/
/*																				*/
/*	This file defines the synchronisation API									*/
/*																				*/
/********************************************************************************/
 
 
/*      Author: David Fournier
 *
 *      Creation Date:      10/04/97
 */
 
 
#include "warnings.h"
#include "NetMemCo.h"
#include <stdlib.h>
#include <stdio.h>
#include "Privnetser.h"
#include "PrivNetSynch.h"
 
typedef struct
{
  NetLib_uxSynchroId uxSyncId;               /* Synchro Id. */
  unsigned long ulSizeMes;                   /* Size of message. */
}NetLib_tdstMesSynchro;
 
typedef struct _stRecSynchro
{
  NetLib_tduxPlayerId PId;                   /* Player Id. */
  struct _stRecSynchro *pNext;               /* Next synchro message receipt. */
  NetLib_uxSynchroId uxSyncId;               /* Synchro Id. */
  unsigned long ulSizeMes;                   /* Size of message. */
}NetLib_tdstRecSynchro;
 
typedef struct
{
  NetLib_uxSynchroId uxSyncId;               /* Synchro Id. */
  NetLib_tduxPlayerId *pMarkPlayer;          /* Array of players mark. */
  NetLib_tduxPlayerId NbrPlayer;             /* Number of remote player. */
  NetLib_tdstRecSynchro *pRecSync;           /* Array of synchro receipt. */
  NetLib_tdulTimeInfo ulTimeEnd;             /* Time of end process (begin + time out). */
  NetLib_tdfnvSynchroCallback tdfnvCallback; /* CallBack for remote sync message. */
}NetLib_tdstInfoSynchro;
 
static NetLib_tdstInfoSynchro gs_tdstInfoSynchro;  /* Informations of the current synchro. */
 
/*****************************************************************************
 * Description: vNetLibInitSynchro
 * Initialyze synchro module.
 *****************************************************************************
 * Input:   none
 * Output:  none
 *****************************************************************************/
void _NET_CALLING_CONV_ vNetLibInitSynchro(void)
{
  gs_tdstInfoSynchro.uxSyncId=NetLib_C_InvalidSynchroId;
  gs_tdstInfoSynchro.pMarkPlayer=NULL;
  gs_tdstInfoSynchro.ulTimeEnd=0;
  gs_tdstInfoSynchro.pRecSync=NULL;
  gs_tdstInfoSynchro.tdfnvCallback=NULL;
}
 
/*****************************************************************************
 * Description: eNetLibFreeSynchro
 * Free synchro module.
 *****************************************************************************
 * Input:   none
 * Output:  none
 *****************************************************************************/
void _NET_CALLING_CONV_ vNetLibFreeSynchro(void)
{
  NetLib_tdstRecSynchro *pRecSync,*pNext;
  vFree(gs_tdstInfoSynchro.pMarkPlayer);
  gs_tdstInfoSynchro.pMarkPlayer=NULL;
  pRecSync=gs_tdstInfoSynchro.pRecSync;
  while (pRecSync)
  {
    pNext=pRecSync->pNext;
    vFree(pRecSync);
    pRecSync=pNext;
  }
  gs_tdstInfoSynchro.pRecSync=NULL;
  gs_tdstInfoSynchro.tdfnvCallback=NULL;
  gs_tdstInfoSynchro.uxSyncId=NetLib_C_InvalidSynchroId;
}
 
/*****************************************************************************
 * Description: eNetLibTestSynchro
 * Return the synchro state.
 *****************************************************************************/
NetLib_tdeErrorStatus eNetLibTestSynchro(int bWithoutAck,NetLib_tduxPlayerId ucMaster)
{
  NetLib_tduxPlayerId i;
  NetLib_tdstRecSynchro *pRecSync;
  if (!bWithoutAck)
  {
    for (i=0;i<gs_tdstInfoSynchro.NbrPlayer;i++)
    {
      if (gs_tdstInfoSynchro.pMarkPlayer[i]==C_uxNetInvalidId)
      {
        return NetLib_E_es_SynchroNoAck;
      }
    }
  }
  if (ucMaster==C_uxNetInvalidId)
  {
    for (i=0;i<gs_tdstInfoSynchro.NbrPlayer;i++)
    {
      pRecSync=gs_tdstInfoSynchro.pRecSync;
      while (pRecSync)
      {
        if ((pRecSync->uxSyncId==gs_tdstInfoSynchro.uxSyncId) &&
            (pRecSync->PId==gs_tdstInfoSynchro.pMarkPlayer[i])) break;
        pRecSync=pRecSync->pNext;
      }
      if (!pRecSync) return NetLib_E_es_SynchroNoSync;
    }
  }
  else
  {
    pRecSync=gs_tdstInfoSynchro.pRecSync;
    while (pRecSync)
    {
      if ((pRecSync->uxSyncId==gs_tdstInfoSynchro.uxSyncId) &&
          (pRecSync->PId==ucMaster)) break;
      pRecSync=pRecSync->pNext;
    }
    if (!pRecSync) return NetLib_E_es_SynchroNoSync;
  }
  return NetLib_E_es_SynchroOk;
}
 
/*****************************************************************************
 * Description: vNetLib_AckSynchro
 * Aknowledgment of a synchro message.
 *****************************************************************************/
static void _NET_CALLING_CONV_ vNetLib_AckSynchro(NetLib_tduxPlayerId PId,
                 NetLib_uxReadReceiptId AId,NetLib_ucReadReceiptFlag Flag,long Param)
{
  NetLib_tduxPlayerId i;
  if ((Flag==NetLib_C_ucReadReceiptSuccess) && (AId==gs_tdstInfoSynchro.uxSyncId))
  {
    for (i=0;i<gs_tdstInfoSynchro.NbrPlayer;i++)
    {
      if (gs_tdstInfoSynchro.pMarkPlayer[i]==C_uxNetInvalidId)
      {
        gs_tdstInfoSynchro.pMarkPlayer[i]=PId;
        break;
      }
      else if (gs_tdstInfoSynchro.pMarkPlayer[i]==PId) break;
    }
  }
}
 
/*****************************************************************************
 * Description: MemSwap
 * Swap memory.
 *****************************************************************************/
static void MemSwap(void *p,unsigned long s)
{
  char *p1,*p2,c;
  p1=(char *)p;
  p2=p1+s;
  s/=2;
  while (s--)
  {
    c=*p1;
    *(p1++)=*(--p2);
    *p2=c;
  }
}
 
/*****************************************************************************
 * Description: eSwapLittleBigEndianSysMsgSynchro
 * Swap synchro message.
 *****************************************************************************/
NetLib_tdeErrorStatus _NET_CALLING_CONV_ eSwapLittleBigEndianSysMsgSynchro(tdstNetMessage *pstMes,tdeNetProtocol eProt,tduwNetChannel uwChannel)
{
  NetLib_tdstMesSynchro *pMessage;
  pMessage=(NetLib_tdstMesSynchro *)(pstMes+1);
  MemSwap(&(pMessage->ulSizeMes),sizeof(unsigned long));
  return NetLib_E_es_NoError;
}
 
/*****************************************************************************
 * Description: eHandleNetSysMsgSynchro
 * Receive a synchro message.
 *****************************************************************************/
NetLib_tdeErrorStatus _NET_CALLING_CONV_ eHandleNetSysMsgSynchro(tdstNetMessage *pstMes,tdeNetProtocol eProt,tduwNetChannel uwChannel)
{
  NetLib_tdstMesSynchro *pMessage;
  NetLib_tdstRecSynchro **ppRecSync,*pRecSync,*pF;
 
  if(pstMes->m_uxReadReceiptId!=C_uxNetInvalidId)
    eNetSendReadReceiptMessage(pstMes);
 
  pMessage=(NetLib_tdstMesSynchro *)(pstMes+1);
  ppRecSync=&(gs_tdstInfoSynchro.pRecSync);
  while (*ppRecSync)
  {
    pRecSync=*ppRecSync;
    if ((pRecSync->uxSyncId==pMessage->uxSyncId) && (pRecSync->PId==pstMes->uxSenderId))
    {                     /* If an old synchro with same Id and same player */
      pF=*ppRecSync;      /* is already in the file, delete it. */
      *ppRecSync=(*ppRecSync)->pNext;
      vFree(pF);
    }
    else ppRecSync=&((*ppRecSync)->pNext);
  }
  pRecSync=(NetLib_tdstRecSynchro *)pMalloc(sizeof(NetLib_tdstRecSynchro)+pMessage->ulSizeMes);
  pRecSync->PId=pstMes->uxSenderId;
  pRecSync->pNext=NULL;
  pRecSync->uxSyncId=pMessage->uxSyncId;
  pRecSync->ulSizeMes=pMessage->ulSizeMes;
  memcpy(pRecSync+1,pMessage+1,pMessage->ulSizeMes);
  *ppRecSync=pRecSync;
  vFree(pstMes);
  return NetLib_E_es_NoError;
}
 
/*****************************************************************************
 * Description: eNetLibWaitSynchro
 * Send a synchro message and wait synchro messages from all other players or from master.
 *****************************************************************************
 * Input:  uxSyncId = Synchro id
 *         ulTimeOut = when time elapsed, function return NetLib_E_es_SynchroNoAck or NetLib_E_es_SynchroNoSync.
 *         pBufMes = Adress of the send message.
 *         ulSizeMes = Size of the send message.
 *         tdfnvCallback = CallBack for receipt remote player message.
 *         bWithoutAck = true (!=0) if send without acknoledment.
 *         ucMaster = Id of master if the synchro is master/slave.
 *                    C_uxNetInvalidId if no master.
 * Output: An error code.
 *   NetLib_E_es_SynchroWait : Synchro not finish.
 *   NetLib_E_es_SynchroOk : Synchro ok. All synchro messages players receipt
 *                           and all player receive our message.
 *   NetLib_E_es_SynchroNoAck : Time out, acknoledgment missing for one or many players,
 *   NetLib_E_es_SynchroNoSync : or one or many synchro message missing.
 *   NetLib_E_es_NotEnoughMemory : Not enough memory for perform synchro.
 *****************************************************************************/
NetLib_tdeErrorStatus _NET_CALLING_CONV_ eNetLibWaitSynchro(NetLib_uxSynchroId uxSyncId,
               NetLib_tdulTimeInfo ulTimeOut,void *pBufMes,unsigned long ulSizeMes,
               NetLib_tdfnvSynchroCallback tdfnvCallback,int bWithoutAck,
               NetLib_tduxPlayerId ucMaster)
{
  NetLib_tduxPlayerId PId,OwnId;
  NetLib_tdstSendDesc tdstSendDesc;
  NetLib_tdstMesSynchro *pMessage;
  NetLib_tdeErrorStatus eStatus;
  NetLib_tdstRecSynchro *pRecSync;
 
  if (gs_tdstInfoSynchro.uxSyncId!=uxSyncId)
  {
    gs_tdstInfoSynchro.uxSyncId=NetLib_C_InvalidSynchroId;
    vFree(gs_tdstInfoSynchro.pMarkPlayer);
    gs_tdstInfoSynchro.NbrPlayer=NetLib_uwGetTotalNumberPlayer()-1;
    OwnId=NetLib_uxGetOwnPlayerId();
    if (gs_tdstInfoSynchro.NbrPlayer==0) return NetLib_E_es_SynchroOk;
    gs_tdstInfoSynchro.pMarkPlayer=pMalloc(sizeof(NetLib_tduxPlayerId)*gs_tdstInfoSynchro.NbrPlayer);
    if (!gs_tdstInfoSynchro.pMarkPlayer) return NetLib_E_es_NotEnoughMemory;
    pMessage=(NetLib_tdstMesSynchro *)pMalloc(ulSizeMes+sizeof(NetLib_tdstMesSynchro));
    if (!pMessage)
    {
      vFree(gs_tdstInfoSynchro.pMarkPlayer);
      gs_tdstInfoSynchro.pMarkPlayer=NULL;
      return NetLib_E_es_NotEnoughMemory;
    }
    if (OwnId==ucMaster) ucMaster=C_uxNetInvalidId;
    for (PId=0;PId<gs_tdstInfoSynchro.NbrPlayer;PId++)
    {
      gs_tdstInfoSynchro.pMarkPlayer[PId]=C_uxNetInvalidId;
    }
    pMessage->uxSyncId=uxSyncId;
    pMessage->ulSizeMes=ulSizeMes;
    memcpy(pMessage+1,pBufMes,ulSizeMes);
 
    gs_tdstInfoSynchro.uxSyncId=uxSyncId;
    gs_tdstInfoSynchro.ulTimeEnd=GetTickCount()+ulTimeOut;
    gs_tdstInfoSynchro.tdfnvCallback=tdfnvCallback;
    if (ucMaster==C_uxNetInvalidId) tdstSendDesc.m_tduxRecipientId=C_uxNetBroadcastId;
    else tdstSendDesc.m_tduxRecipientId=ucMaster;
    tdstSendDesc.m_ulMaxWaitingTime=0;
    tdstSendDesc.m_pMessageData=(char*)pMessage;
    tdstSendDesc.m_uwMessageLength=ulSizeMes+sizeof(NetLib_tdstMesSynchro);
    tdstSendDesc.m_uxPriority=0;
    tdstSendDesc.m_uxReplaceType=0;
    tdstSendDesc.m_uxReplaceFlag=NetLib_C_uxNoReplace;
    if (!bWithoutAck)
    {
      tdstSendDesc.m_ucReadReceiptFlag=NetLib_C_ucReadReceiptRequired;
      tdstSendDesc.m_stReadReceiptDesc.m_ulMaxWaitingTime=ulTimeOut;
      tdstSendDesc.m_stReadReceiptDesc.m_ulReemitFreq=250;
      tdstSendDesc.m_stReadReceiptDesc.m_uxReadReceiptId=uxSyncId;
      tdstSendDesc.m_stReadReceiptDesc.m_lCallbackParam=uxSyncId;
      tdstSendDesc.m_stReadReceiptDesc.p_fnv_ReadReceiptCallback=vNetLib_AckSynchro;
    }
    else
    {
      tdstSendDesc.m_ucReadReceiptFlag=NetLib_C_ucNoReadReceipt;
    }
    eNetInternalSendMessage(&tdstSendDesc,E_Net_mt_SysSynchro);
    vFree(pMessage);
  }
  NetLib_vEngine();
  eStatus=eNetLibTestSynchro(bWithoutAck,ucMaster);
  if ((eStatus!=NetLib_E_es_SynchroOk) && (GetTickCount()<gs_tdstInfoSynchro.ulTimeEnd))
  {
    eStatus=NetLib_E_es_SynchroWait;
  }
  if (eStatus!=NetLib_E_es_SynchroWait)
  {
    if (tdfnvCallback)
    {
      pRecSync=gs_tdstInfoSynchro.pRecSync;
      while (pRecSync)
      {
        if (pRecSync->uxSyncId==uxSyncId)
        {
          tdfnvCallback(pRecSync->PId,pRecSync->uxSyncId,pRecSync+1,pRecSync->ulSizeMes);
        }
        pRecSync=pRecSync->pNext;
      }
    }
    vNetLibFreeSynchro();
  }
  return eStatus;
}
 
