#include "ResultDispatcher.h"

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

#include "YBWCManager.h"
extern YBWCManager* g_pYBWCManagerInstance;

#include "protocol.h"

ResultDispatcher* ResultDispatcher_Construct(ResultDispatcher* pThis)
{
  pThis->bDispatchActive = FALSE;

  return pThis;
}

void ResultDispatcher_Destruct(ResultDispatcher* pThis)
{
  if(pThis)
  {
    free(pThis);
  }
}

int ResultDispatcher_Dispatch(ResultDispatcher* pThis, Result* pResult)
{
  int               nResult;

  ProcessorHandler* pProcessorHandler;

  nResult = Result_GetParameterFromBuffer(pResult);
  if(nResult == MPI_SUCCESS)
  {
    switch(pResult->nType)
    {
    case RESULT_TYPE_UNDEFINED:
      nResult = MPI_ERR_OTHER;
      if(g_pYBWCManagerInstance->pFruitConfiguration->nFruitDebugLevel >= FRUIT_DEBUG_LEVEL_ERROR_PRIO_1)
      {
        YBWCManager_Printf(g_pYBWCManagerInstance, "ERROR ResultDispatcher_Dispatch(... failed at switch(pResult->nType) (RESULT_TYPE_UNDEFINED), errorcode = %d\n", nResult);
      }

      break;

    case RESULT_TYPE_NON_ROOT_MASTER_LOGON:
      ASSERT(g_pYBWCManagerInstance->bRootMaster);
      ASSERT(pResult->nSourceId != 0)

      ASSERT(g_pYBWCManagerInstance->nState == YBWC_MANAGER_STATE_INITIALIZED);

      pProcessorHandler = g_pYBWCManagerInstance->processorHandlerPool[pResult->nSourceId];

      ASSERT(pProcessorHandler->nState == PROCESSOR_HANDLER_STATE_INITIALIZED);

      pProcessorHandler->bLoggedOn = TRUE;
//    pProcessorHandler->nState    = PROCESSOR_HANDLER_STATE_LOGGED_ON;

      pProcessorHandler->sProcessorName[0] = '\0';
      strncat(pProcessorHandler->sProcessorName, pResult->sProcessorName, MPI_MAX_PROCESSOR_NAME);

      g_pYBWCManagerInstance->nLogonCount++;

// kh 03.08.06 force output of this information
      if(g_pYBWCManagerInstance->pFruitConfiguration->nFruitDebugLevel >= FRUIT_DEBUG_LEVEL_FORCE_OUTPUT)
      {
        YBWCManager_Printf(g_pYBWCManagerInstance, 
                           "Processor <%d> (%d/%d) on <%s> logged on\n",
                           pProcessorHandler->nId,
                           g_pYBWCManagerInstance->nLogonCount,
                           g_pYBWCManagerInstance->nSize - 1,
                           pProcessorHandler->sProcessorName);
      }
      break;

    case RESULT_TYPE_NON_ROOT_MASTER_LOGOFF:
      ASSERT(g_pYBWCManagerInstance->bRootMaster);
      ASSERT(pResult->nSourceId != 0)

      ASSERT(g_pYBWCManagerInstance->nState == YBWC_MANAGER_STATE_IDLE);

      pProcessorHandler = g_pYBWCManagerInstance->processorHandlerPool[pResult->nSourceId];
      pProcessorHandler->bLoggedOn  = FALSE;
      pProcessorHandler->nState     = PROCESSOR_HANDLER_STATE_LOGGED_OFF;

      g_pYBWCManagerInstance->nTotalNodesNonRootMaster += pResult->nTotalNodes;

      g_pYBWCManagerInstance->nLogonCount--;
      ASSERT(g_pYBWCManagerInstance->nLogonCount >= 0);

      g_pYBWCManagerInstance->nLogoffCount++;

// kh 03.08.06 force output of this information
      if(g_pYBWCManagerInstance->pFruitConfiguration->nFruitDebugLevel >= FRUIT_DEBUG_LEVEL_FORCE_OUTPUT)
      {
        YBWCManager_Printf(g_pYBWCManagerInstance, 
                           "Processor <%d> (%d/%d) on <%s> logged off\n",
                           pProcessorHandler->nId,
                           g_pYBWCManagerInstance->nLogoffCount,
                           g_pYBWCManagerInstance->nSize - 1,
                           pProcessorHandler->sProcessorName);
      }
      break;

    case RESULT_TYPE_NON_ROOT_MASTER_SHUTDOWN:
      ASSERT(g_pYBWCManagerInstance->bRootMaster);
      ASSERT(pResult->nSourceId != 0)

      ASSERT(g_pYBWCManagerInstance->nState == YBWC_MANAGER_STATE_IDLE);

      pProcessorHandler = g_pYBWCManagerInstance->processorHandlerPool[pResult->nSourceId];
      pProcessorHandler->bLoggedOn  = FALSE;

      ASSERT(pProcessorHandler->nState == PROCESSOR_HANDLER_STATE_LOGGED_OFF);

      pProcessorHandler->nState     = PROCESSOR_HANDLER_STATE_SHUTDOWN;

      g_pYBWCManagerInstance->nShutdownCount++;

// kh 03.08.06 force output of this information
      if(g_pYBWCManagerInstance->pFruitConfiguration->nFruitDebugLevel >= FRUIT_DEBUG_LEVEL_FORCE_OUTPUT)
      {
        YBWCManager_Printf(g_pYBWCManagerInstance, 
                           "Processor <%d> (%d/%d) on <%s> has shutdown\n",
                           pProcessorHandler->nId,
                           g_pYBWCManagerInstance->nShutdownCount,
                           g_pYBWCManagerInstance->nSize - 1,
                           pProcessorHandler->sProcessorName);
      }
      break;

    case RESULT_TYPE_NON_ROOT_MASTER_REMOTE_TEST:
      ASSERT(g_pYBWCManagerInstance->bRootMaster);
      ASSERT(pResult->nSourceId != 0)
    
      ASSERT(g_pYBWCManagerInstance->nState                 == YBWC_MANAGER_STATE_SEARCHING_REMOTE);
      ASSERT(g_pYBWCManagerInstance->nRemoteTestProcessorId == pResult->nSourceId);

// kh 10.08.06 use Fruit functions
      fruit_actions_for_remote_test_result_received(pResult);
      break;

    case RESULT_TYPE_NON_ROOT_MASTER_SYNC_NEW_GAME:
      ASSERT(g_pYBWCManagerInstance->bRootMaster);
      ASSERT(pResult->nSourceId != 0)

//    ASSERT(g_pYBWCManagerInstance->nState == YBWC_MANAGER_STATE_INITIALIZED);

      pProcessorHandler = g_pYBWCManagerInstance->processorHandlerPool[pResult->nSourceId];

//    ASSERT(pProcessorHandler->nState == PROCESSOR_HANDLER_STATE_INITIALIZED);

      pProcessorHandler->bSyncedNewGame = TRUE;

      g_pYBWCManagerInstance->nSyncedNewGameCount++;

// kh 05.01.07 force output of this information
      if(g_pYBWCManagerInstance->pFruitConfiguration->nFruitDebugLevel >= FRUIT_DEBUG_LEVEL_FORCE_OUTPUT)
      {
        YBWCManager_Printf(g_pYBWCManagerInstance, 
                           "Processor <%d> (%d/%d) on <%s> synced new game\n",
                           pProcessorHandler->nId,
                           g_pYBWCManagerInstance->nSyncedNewGameCount,
                           g_pYBWCManagerInstance->nSize - 1,
                           pProcessorHandler->sProcessorName);
      }
      break;

    case RESULT_TYPE_NON_ROOT_MASTER_SYNC_SET_OPTION:
      ASSERT(g_pYBWCManagerInstance->bRootMaster);
      ASSERT(pResult->nSourceId != 0)

//    ASSERT(g_pYBWCManagerInstance->nState == YBWC_MANAGER_STATE_INITIALIZED);

      pProcessorHandler = g_pYBWCManagerInstance->processorHandlerPool[pResult->nSourceId];

//    ASSERT(pProcessorHandler->nState == PROCESSOR_HANDLER_STATE_INITIALIZED);

      pProcessorHandler->bSyncedSetOption = TRUE;

      g_pYBWCManagerInstance->nSyncedSetOptionCount++;

// kh 07.02.08 force output of this information
      if(g_pYBWCManagerInstance->pFruitConfiguration->nFruitDebugLevel >= FRUIT_DEBUG_LEVEL_FORCE_OUTPUT)
      {
        YBWCManager_Printf(g_pYBWCManagerInstance, 
                           "Processor <%d> (%d/%d) on <%s> synced set option\n",
                           pProcessorHandler->nId,
                           g_pYBWCManagerInstance->nSyncedSetOptionCount,
                           g_pYBWCManagerInstance->nSize - 1,
                           pProcessorHandler->sProcessorName);
      }
      break;

    case RESULT_TYPE_MASTER_NO_WORK_AVAILABLE:
      ASSERT(!g_pYBWCManagerInstance->bRootMaster);                // kh 06.09.06 the root master is never a slave and will never have asked ask for work 
      ASSERT(pResult->nSourceId != g_pYBWCManagerInstance->nRank); // kh 06.09.06 a slave will never have tried to get work from himself

// kh 19.09.06 the original ask for work has to be answered 
// and such a request for work will only be send if the non root master was "logged on"
// so the "logged on state" can not change in between
      ASSERT(g_pYBWCManagerInstance->bLoggedOn);

// kh 06.09.06 "out of sync" problems are exceptionally not expected, so the assertion should hold
      ASSERT(g_pYBWCManagerInstance->nState    == YBWC_MANAGER_STATE_REQUEST_FOR_WORK_SEND);

      ASSERT(g_pYBWCManagerInstance->nMasterId == pResult->nSourceId);

// kh 20.09.06 the master has originally send work transmitting his actual sync position id 
// in the request 
// this sync position id was now send back from the potential master in pResult->nSyncPositionId
// and received here
// the sync position id must not have changed in between (because in the potential case of a 
// positive answer the master depends on getting his subproblem processed below at
// case RESULT_TYPE_MASTER_WORK_AVAILABLE)
// so the slave has to "wait" in any case for a RESULT_TYPE_MASTER_NO_WORK_AVAILABLE or a
// RESULT_TYPE_MASTER_WORK_AVAILABLE before e.g. the next sync position request may be handled 
      ASSERT(g_pYBWCManagerInstance->nSyncPositionId == pResult->nSyncPositionId);

      if(g_pYBWCManagerInstance->nState == YBWC_MANAGER_STATE_REQUEST_FOR_WORK_SEND)
      {
        g_pYBWCManagerInstance->nMasterId = -1;
        g_pYBWCManagerInstance->nState    = YBWC_MANAGER_STATE_SUPPORT_SEARCH;
        g_pYBWCManagerInstance->nNoWorkAvailableInARow++;

        YBWCManager_DispatchDelayedRequests(g_pYBWCManagerInstance);
      }
      else
      {
        // kh 06.09.06 skip
      }
      break;

    case RESULT_TYPE_MASTER_WORK_AVAILABLE:
      ASSERT(!g_pYBWCManagerInstance->bRootMaster);                // kh 06.09.06 the root master is never a slave and will never have asked ask for work 
      ASSERT(pResult->nSourceId != g_pYBWCManagerInstance->nRank); // kh 06.09.06 a slave will never have tried to get work from himself

// kh 19.09.06 the original ask for work has to be answered 
// and such a request for work will only be send if the non root master was "logged on"
// so the "logged on state" can not change in between 
// (see also comment from 19.09.06 for case RESULT_TYPE_MASTER_NO_WORK_AVAILABLE above)
      ASSERT(g_pYBWCManagerInstance->bLoggedOn);

// kh 13.09.06 as a future expansion there could be a more rigorous stop via broadcast implemented
// setting the state forced to idle and affecting the assertions below

// kh 06.09.06 "out of sync" problems are exceptionally not expected, so the assertion should hold
      ASSERT(g_pYBWCManagerInstance->nState    == YBWC_MANAGER_STATE_REQUEST_FOR_WORK_SEND);

      ASSERT(g_pYBWCManagerInstance->nMasterId == pResult->nSourceId);

// kh 13.09.06 the master has send work and depends on receiving and processing his subproblem here
      ASSERT(g_pYBWCManagerInstance->nSyncPositionId == pResult->nSyncPositionId);

      if(g_pYBWCManagerInstance->nState == YBWC_MANAGER_STATE_REQUEST_FOR_WORK_SEND) // kh 13.09.06 defensive
      {
        g_pYBWCManagerInstance->nNoWorkAvailableInARow = 0;

        if(g_pYBWCManagerInstance->nSyncPositionId == pResult->nSyncPositionId)
        {

// kh 06.09.06 use Fruit functions
          fruit_actions_for_work_available_received(pResult);

          if(g_pYBWCManagerInstance->pFruitConfiguration->nDebugWaitForWorkRate > 0)
          {
            CountdownTimer_Restart(g_pYBWCManagerInstance->pCountdownTimerDebugWaitForWork);
          }
        }
        else
        {

// kh 13.09.06 defensive (will probably not help any more...)
          g_pYBWCManagerInstance->nState = YBWC_MANAGER_STATE_SUPPORT_SEARCH;
          YBWCManager_DispatchDelayedRequests(g_pYBWCManagerInstance);
        }
      }
      else
      {
        // kh 06.09.06 skip
        ASSERT(false);
      }
      break;

    case RESULT_TYPE_SYNC_AFTER_BROADCAST:
      pProcessorHandler = g_pYBWCManagerInstance->processorHandlerPool[pResult->nSourceId];
      pProcessorHandler->bSyncedAfterBroadcast = TRUE;

      g_pYBWCManagerInstance->nSyncedAfterBroadcastCount++;

// kh 07.02.08 force output of this information
      if(g_pYBWCManagerInstance->pFruitConfiguration->nFruitDebugLevel >= FRUIT_DEBUG_LEVEL_FORCE_OUTPUT)
      {
        YBWCManager_Printf(g_pYBWCManagerInstance, 
                           "Processor <%d> (%d/%d) on <%s> synced after broadcast\n",
                           pProcessorHandler->nId,
                           g_pYBWCManagerInstance->nSyncedAfterBroadcastCount,
                           g_pYBWCManagerInstance->nSize - 1,
                           pProcessorHandler->sProcessorName);
      }
      break;

	  default:
      nResult = MPI_ERR_OTHER;
      if(g_pYBWCManagerInstance->pFruitConfiguration->nFruitDebugLevel >= FRUIT_DEBUG_LEVEL_ERROR_PRIO_1)
      {
        YBWCManager_Printf(g_pYBWCManagerInstance, "ERROR ResultDispatcher_Dispatch(... failed at switch(pResult->nType) (default), errorcode = %d\n", nResult);
      }
		  break;
	  }
  }
  else
  {
    if(g_pYBWCManagerInstance->pFruitConfiguration->nFruitDebugLevel >= FRUIT_DEBUG_LEVEL_ERROR_PRIO_1)
    {
      YBWCManager_Printf(g_pYBWCManagerInstance, "ERROR ResultDispatcher_Dispatch(... failed at Result_GetParamterFromBuffer(..., errorcode = %d\n", nResult);
    }
  }

  return nResult;
}
