#ifndef STACK_H
#define STACK_H

#include "StackEntry.h"

#define MAX_STACK_SIZE HeightMax

#define SIZE_OF_CALL_POSITIONS_TO_INDEX   1024                
#define MAX_CALL_INDICES_TO_POSITIONS     MAX_CALL_POSITIONS

typedef struct
{
  int                   nMaxSize;
  int                   nCount;
  StackEntry            stackEntry[MAX_STACK_SIZE];

  int                   CallPositionToIndexMap[SIZE_OF_CALL_POSITIONS_TO_INDEX];
  int                   CallIndexToPositionMap[MAX_CALL_INDICES_TO_POSITIONS];

  int                   nStack_UpdateSearchWindowForEntry;

} Stack;

extern Stack g_Stack;

Stack* Stack_Construct(/* Stack* pThis */);

void Stack_Destruct(/* Stack* pThis */);

void Stack_Init(/* Stack* pThis */);

INLINE StackEntry* Stack_PeekAt(/* Stack* pThis, */ int i)
{
#if DEBUG

  StackEntry* pStackEntryResult;

  pStackEntryResult = NULL;

/*
  if(pThis)
  {
*/
    if(g_Stack.nCount > 0)
    {
      pStackEntryResult = &g_Stack.stackEntry[i];
    }
/*
  }
*/

  return pStackEntryResult;

#else

  return &g_Stack.stackEntry[i];

#endif
}

INLINE StackEntry* Stack_Peek(/* Stack* pThis */)
{
#if DEBUG

  StackEntry* pStackEntryResult;

  pStackEntryResult = NULL;

/*
  if(pThis)
  {
*/
    if(g_Stack.nCount > 0)
    {
      pStackEntryResult = &g_Stack.stackEntry[g_Stack.nCount - 1];
    }
/*
  }
*/

  return pStackEntryResult;

#else

  return &g_Stack.stackEntry[g_Stack.nCount - 1];

#endif
}

INLINE StackEntry* Stack_Pop(/* Stack* pThis */)
{
#if DEBUG

  StackEntry* pStackEntryResult;

  pStackEntryResult = NULL;

/*
  if(pThis)
  {
*/
    if(g_Stack.nCount > 0)
    {
      pStackEntryResult = &g_Stack.stackEntry[--g_Stack.nCount];
    }
/*
  }
*/

  return pStackEntryResult;

#else

  return &g_Stack.stackEntry[--g_Stack.nCount];

#endif
}

INLINE void Stack_PopFast(/* Stack* pThis */)
{
#if DEBUG

/*
  if(pThis)
  {
*/
    if(g_Stack.nCount > 0)
    {
      --g_Stack.nCount;
    }
/*
  }
*/

#else

  --g_Stack.nCount;

#endif
}

INLINE void Stack_PushFullRoot(/* Stack*    pThis, */
                               list_t*   list,
                               board_t*  board, 
                               int       alpha, 
                               int       beta, 
                               int       depth, 
                               int       height, 
                               int       search_type,
                               int       nReturnPosition)
{
  StackEntry* pStackEntryResult;
//StackEntry* pStackEntryTmp;

/*
  ASSERT(pThis);
*/

  ASSERT(g_Stack.nCount < g_Stack.nMaxSize);

/*
  if(pThis)
  {
    if(g_Stack.nCount < g_Stack.nMaxSize)
    {
*/
      pStackEntryResult = &g_Stack.stackEntry[g_Stack.nCount++]; // kh 26.07.06 top element

      StackEntry_Init(pStackEntryResult);

      pStackEntryResult->nCallType       = CALL_TYPE_FULL_ROOT;
      pStackEntryResult->nReturnPosition = nReturnPosition;

#if DEBUG
      pStackEntryResult->nResult         = STACK_ENTRY_RESULT_UNDEFINED_FULL_ROOT;
#endif

      pStackEntryResult->parameters.fullRootPar.list        = list;
      pStackEntryResult->parameters.fullRootPar.board       = board;
      pStackEntryResult->parameters.fullRootPar.alpha       = alpha;
      pStackEntryResult->parameters.fullRootPar.beta        = beta;      
      pStackEntryResult->parameters.fullRootPar.depth       = depth;     
      pStackEntryResult->parameters.fullRootPar.height      = height;    
      pStackEntryResult->parameters.fullRootPar.search_type = search_type;        

// kh 09.10.06 to be able to access the correct board state in full_new_depth(... called by Stack_HasSendWorkToSlave(... 
// when searching for distributable work the board is saved here

// kh 10.10.06 update for comment from 09.10.06 below:
// the full_new_depth(... calculation is delegated to the slave now
/*
// kh 09.10.06 for a better performance the using full_new_depth(... could be avoided or 
// only the really necessary board data has to be saved here
// for the root moves the performance problem seems to be acceptable anyway
      board_copy(&pStackEntryResult->boardMemo, board);
*/

// kh 18.10.06 used for CALL_TYPE_FULL_SEARCH (in connection with doing/undoing moves) to differentiate 
// on the basis of the return position the move type (i.e. MoveNone, MoveNull, <normal move>) 
// kh 18.10.06 defensive, with CALL_TYPE_FULL_SEARCH there will never be a call to Stack_PushFullRoot(... 
// and nCount is always 1 

      ASSERT(g_Stack.nCount == 1);

// kh 02.05.08

/*
      if(g_Stack.nCount > 1)
      {
        pStackEntryTmp = &g_Stack.stackEntry[g_Stack.nCount - 2]; // kh 18.10.06 previous element
        pStackEntryTmp->nReturnPositionOfNextLevel = pStackEntryResult->nReturnPosition; 
      }
*/

/*
    }                                       
  }
*/
}

INLINE void Stack_PushFullSearch(/* Stack*    pThis, */
                                 board_t*  board, 
                                 int       alpha, 
                                 int       beta, 
                                 int       depth, 
                                 int       height, 
                                 mv_t      pv[], 
                                 int       node_type,
                                 bool      extended,
                                 int       nReturnPosition)
{
  StackEntry* pStackEntryResult;
//StackEntry* pStackEntryTmp;

  ASSERT(g_Stack.nCount < g_Stack.nMaxSize);

/*
  if(pThis)
  {
    if(g_Stack.nCount < g_Stack.nMaxSize)
    {
*/
      pStackEntryResult = &g_Stack.stackEntry[g_Stack.nCount++]; // kh 21.07.06 top element

      StackEntry_Init(pStackEntryResult);

      pStackEntryResult->nCallType       = CALL_TYPE_FULL_SEARCH;
      pStackEntryResult->nReturnPosition = nReturnPosition;

#if DEBUG
      pStackEntryResult->nResult         = STACK_ENTRY_RESULT_UNDEFINED_FULL_SEARCH;
#endif

      pStackEntryResult->parameters.fullSearchPar.board     = board;
      pStackEntryResult->parameters.fullSearchPar.alpha     = alpha;
      pStackEntryResult->parameters.fullSearchPar.beta      = beta;      
      pStackEntryResult->parameters.fullSearchPar.depth     = depth;     
      pStackEntryResult->parameters.fullSearchPar.height    = height;    
      pStackEntryResult->parameters.fullSearchPar.ppv       = pv;        
      pStackEntryResult->parameters.fullSearchPar.node_type = node_type;
      pStackEntryResult->parameters.fullSearchPar.extended  = extended;

// kh 17.10.06 could be used to save the board state per stack level, but that would lead to
// enormous perfomance losses (e.g. from ca. 537000 nodes per second down to
// 174000 nodes per second (startpos, depth 12, release build P4 3GHz) 
//    board_copy(&pStackEntryResult->boardMemo, board);

// kh 18.10.06 used for CALL_TYPE_FULL_SEARCH (in connection with doing/undoing moves) to differentiate 
// on the basis of the return position the move type (i.e. MoveNone, MoveNull, <normal move>) 

/*
      if(g_Stack.nCount > 1)
      {
        pStackEntryTmp = &g_Stack.stackEntry[g_Stack.nCount - 2]; // kh 18.10.06 previous element
        pStackEntryTmp->nReturnPositionOfNextLevel = pStackEntryResult->nReturnPosition; 
      }
*/

/*
    }
  }
*/

}

INLINE void Stack_PushFullNoNull(/* Stack*    pThis, */
                                 board_t*  board, 
                                 int       alpha, 
                                 int       beta, 
                                 int       depth, 
                                 int       height, 
                                 mv_t      pv[], 
                                 int       node_type,
                                 int       trans_move, 
                                 int*      best_move,
                                 bool      extended,
                                 int       nReturnPosition)
{
  StackEntry* pStackEntryResult;
//StackEntry* pStackEntryTmp;

/*
  ASSERT(pThis);
*/

  ASSERT(g_Stack.nCount < g_Stack.nMaxSize);

/*
  if(pThis)
  {
    if(g_Stack.nCount < g_Stack.nMaxSize)
    {
*/
      pStackEntryResult = &g_Stack.stackEntry[g_Stack.nCount++]; // kh 21.07.06 top element

      StackEntry_Init(pStackEntryResult);

      pStackEntryResult->nCallType       = CALL_TYPE_FULL_NO_NULL;
      pStackEntryResult->nReturnPosition = nReturnPosition;

#if DEBUG
      pStackEntryResult->nResult         = STACK_ENTRY_RESULT_UNDEFINED_FULL_NO_NULL;
#endif

      pStackEntryResult->parameters.fullNoNullPar.board       = board;
      pStackEntryResult->parameters.fullNoNullPar.alpha       = alpha;
      pStackEntryResult->parameters.fullNoNullPar.beta        = beta;      
      pStackEntryResult->parameters.fullNoNullPar.depth       = depth;     
      pStackEntryResult->parameters.fullNoNullPar.height      = height;    
      pStackEntryResult->parameters.fullNoNullPar.ppv         = pv;        
      pStackEntryResult->parameters.fullNoNullPar.node_type   = node_type;
      pStackEntryResult->parameters.fullNoNullPar.trans_move  = trans_move;
      pStackEntryResult->parameters.fullNoNullPar.pbest_move  = best_move;
      pStackEntryResult->parameters.fullNoNullPar.extended    = extended;

// kh 18.10.06 used for CALL_TYPE_FULL_SEARCH (in connection with doing/undoing moves) to differentiate 
// on the basis of the return position the move type (i.e. MoveNone, MoveNull, <normal move>) 
// kh 02.05.08

/*
      if(g_Stack.nCount > 1)
      {
        pStackEntryTmp = &g_Stack.stackEntry[g_Stack.nCount - 2]; // kh 18.10.06 previous element
        pStackEntryTmp->nReturnPositionOfNextLevel = pStackEntryResult->nReturnPosition; 
      }
*/

/*
    }                                       
  }
*/
}

INLINE void Stack_PushFullQuiescence(/* Stack*    pThis, */
                                     board_t*  board, 
                                     int       alpha, 
                                     int       beta, 
                                     int       depth, 
                                     int       height, 
                                     mv_t      pv[],
                                     int       nReturnPosition)
{
  StackEntry* pStackEntryResult;
//StackEntry* pStackEntryTmp;

/*
  ASSERT(pThis);
*/

  ASSERT(g_Stack.nCount < g_Stack.nMaxSize);

/*
  if(pThis)
  {
    if(g_Stack.nCount < g_Stack.nMaxSize)
    {
*/
      pStackEntryResult = &g_Stack.stackEntry[g_Stack.nCount++]; // kh 21.07.06 top element

      StackEntry_Init(pStackEntryResult);

      pStackEntryResult->nCallType       = CALL_TYPE_FULL_QUIESCENCE;
      pStackEntryResult->nReturnPosition = nReturnPosition;

#if DEBUG
      pStackEntryResult->nResult         = STACK_ENTRY_RESULT_UNDEFINED_FULL_QUIESCENCE;
#endif

      pStackEntryResult->parameters.fullQuiescencePar.board       = board;
      pStackEntryResult->parameters.fullQuiescencePar.alpha       = alpha;
      pStackEntryResult->parameters.fullQuiescencePar.beta        = beta;      
      pStackEntryResult->parameters.fullQuiescencePar.depth       = depth;     
      pStackEntryResult->parameters.fullQuiescencePar.height      = height;    
      pStackEntryResult->parameters.fullQuiescencePar.ppv         = pv; 

// kh 18.10.06 used for CALL_TYPE_FULL_SEARCH (in connection with doing/undoing moves) to differentiate 
// on the basis of the return position the move type (i.e. MoveNone, MoveNull, <normal move>) 

/*
      if(g_Stack.nCount > 1)
      {
        pStackEntryTmp = &g_Stack.stackEntry[g_Stack.nCount - 2]; // kh 18.10.06 previous element
        pStackEntryTmp->nReturnPositionOfNextLevel = pStackEntryResult->nReturnPosition; 
      }
*/
/*
    }                                       
  }
*/
}

void Stack_Clear(/* Stack* pThis */);

bool Stack_HasSendWorkToSlave(/* Stack* pThis, */ int nSlaveId, int nSlaveSyncPositionId);

int Stack_Stop(/* Stack* pThis */);

int Stack_StopEntry(/* Stack* pThis, */ StackEntry* pStackEntry);

int Stack_UpdateSearchWindowForEntry(/* Stack* pThis, */ StackEntry* pStackEntry, int nAlpha, int nBeta);

void Stack_UpdateSearchWindow(/* Stack* pThis, */ StackEntry* pStackEntryPrev, int nIndex);

int Stack_CallPositionToIndex(/* Stack* pThis, */ int nReturnPosition);

void Stack_DumpAwaitedSlaveResults(/* Stack* pThis */);

void Stack_Dump(/* Stack* pThis */);

#endif
