/*----------------------------------------------------------------------*/
/* (C) Copyright 1994 Stefanos Kiakas                                   */
/* All rights reserved.                                                 */
/*----------------------------------------------------------------------*/

/* Generated by Interface Builder */

#import "NGameView.h"
#import "NQueueView.h"
#import "NPipeCell.h"

/*----------------------------------------------------------------------*/
/* Purpose      : This is wrapper function to call the step method. It  */ 
/*                is invoked DPSTimedEntryat a constant interval to     */
/*                update the flow.                                      */
/*                                                                      */
/* Parameters   : As defined for a DPSTimedEntry().                     */
/*                                                                      */
/* Return value : none                                                  */ 
/*----------------------------------------------------------------------*/
void stepFun (DPSTimedEntry timedE, double timeN, void *data)
{
  [(id)data step ];
}





@implementation NGameView
/*----------------------------------------------------------------------*/
/* Purpose      : Method to initialize ( create matrix and associated   */ 
/*                cells ) game view.                                    */
/*                                                                      */
/* Parameters   : none                                                  */
/*                                                                      */
/* Return value : none                                                  */ 
/*----------------------------------------------------------------------*/
- initializeGame
{
   NXRect   matrixRect;
   NXSize   interCellSpacing = {0.0, 0.0};
   NXSize   cellSize = { 50.0, 50.0 };
   struct timeval tp;
   struct timezone tzp;

   gettimeofday( &tp, &tzp );
   srandom( (int ) tp.tv_sec );

   [self getBounds:&matrixRect];                      /* determine the matrix bounds */
   /* prepare a matrix to go inside our scrollView */
   gameMatrix = [[Matrix alloc] initFrame:&matrixRect
                                  mode:NX_RADIOMODE
                                  cellClass:[NPipeCell class]
                                  numRows:NP_GAME_ROWS
                                  numCols:NP_GAME_COLUMNS];

   /* we don't want any space between the matrix's cells  */
   [gameMatrix setIntercell:&interCellSpacing];

   /* resize the matrix's cells and size the matrix to contain them */
   [gameMatrix setCellSize:&cellSize];
   [gameMatrix sizeToCells];

   [gameMatrix setTarget:self];
   [gameMatrix setAction:@selector(singleClick:)];

   /* stick the matrix in our View */
   [self addSubview:gameMatrix];
   gameStatus = STOPPED;

   [queue initializePipeQueue];
   return self;
}






/*----------------------------------------------------------------------*/
/* Purpose      :                                                       */ 
/* Parameters   :                                                       */
/* Return value :                                                       */ 
/*----------------------------------------------------------------------*/
- newLevel
{
   int i;               // general purpose index
   int r;               // row counter
   int c;               // column counter
   id  currentCell;
   int startFlowRow;
   int startFlowColumn;

   gameLevel++;
   levelPiecesRemaining = ( gameLevel * 2 ) + 5;
   [theScore setIntValue: gameScore];
   [theLevel setIntValue: gameLevel];
   [theBonus setIntValue: levelBonus];
   [theRest  setIntValue: levelPiecesRemaining];

   //---------------------------------------------------------------------- 
   // Set up all cells for new game.
   //---------------------------------------------------------------------- 
   for( c = 0; c < NP_GAME_COLUMNS; c++ )
   {
      for( r = 0; r < NP_GAME_ROWS; r++ )
      {
          currentCell = [gameMatrix cellAt:r :c ];
          [currentCell assignPipe: PIPE_UNASSIGNED ]; 
      }
   }
   [gameMatrix display];

   //---------------------------------------------------------------------- 
   // Calculate new (random) position of water source.
   //---------------------------------------------------------------------- 
   startFlowRow = random() % NP_GAME_ROWS;
   startFlowColumn = random() % ( NP_GAME_COLUMNS - 1 );    // source must not be againt the right edge
   currentCell = [gameMatrix cellAt:startFlowRow :startFlowColumn ];
   [currentCell assignPipe: PIPE_SOURCE];
   flowCell = currentCell;
   [flowCell setFlowEntryDirection: SOUTH ];
  
   //----------------------------------------------------------------------
   // Add obstacles to game matrix.
   //----------------------------------------------------------------------
   for( i = 1; i < ( gameLevel < MAX_OBSTACLES ? gameLevel: MAX_OBSTACLES) ; i++ )
   {
      //----------------------------------------------------------------------
      // Make sure we don't put any obstacles on top or directly to the right
      // of the source pipe .
      //----------------------------------------------------------------------
      do
      {
         r = random() % NP_GAME_ROWS;
         c = random() % NP_GAME_COLUMNS;
      }
      while( r == startFlowRow && ( c == startFlowColumn || c == (startFlowColumn + 1) ) );

      //----------------------------------------------------------------------
      // Get cell at selected position ( row r and collumn c ) assign is as
      // an obstacle and draw it.
      //----------------------------------------------------------------------
      currentCell = [gameMatrix cellAt:r :c ];
      [currentCell assignPipe: PIPE_OBSTACLE ];
   }

   flowTimer = DPSAddTimedEntry(TIMEINTERVAL,&stepFun,self,NX_BASETHRESHOLD);
   gameStatus = STARTED;
   if( gameLevel == 1 )
      [queue useMinimumPipeSet];
   else if( gameLevel == 6 )
      [queue useMediumPipeSet];
   else if( gameLevel == 11 )
      [queue useMaximumPipeSet ];
   [queue randomizePipeQueue];
   [gameMatrix display];
   return self;
}





/*----------------------------------------------------------------------*/
/* Purpose      :                                                       */ 
/* Parameters   :                                                       */
/* Return value :                                                       */ 
/*----------------------------------------------------------------------*/
- endLevel
{
   DPSRemoveTimedEntry( flowTimer );
   gameStatus = PAUSED;
   if( continuePanel == nil )
   {
      if( ![ NXApp loadNibSection:"NContinue.nib" owner: self withNames: NO ] )
         return nil;
   }
   //[continuePanel setFloatingPanel: YES ];
   [continuePanel setBecomeKeyOnlyIfNeeded: YES];
   [continuePanel makeKeyAndOrderFront: nil ];
   return self;
}





/*----------------------------------------------------------------------*/
/* Purpose      :                                                       */ 
/* Parameters   :                                                       */
/* Return value :                                                       */ 
/*----------------------------------------------------------------------*/
- newGame
{
   printf("newGame\n");
   if( gameStatus == STARTED || gameStatus == FASTFORWARD )
      DPSRemoveTimedEntry( flowTimer );
   if( endGamePanel != nil )
      [endGamePanel close];
   gameScore = 0;
   gameLevel = 0;
   levelBonus = 0;
   levelPiecesRemaining = 0;

   [self newLevel];
   return self;
}



/*----------------------------------------------------------------------*/
/* Purpose      :                                                       */ 
/* Parameters   :                                                       */
/* Return value :                                                       */ 
/*----------------------------------------------------------------------*/
- pauseGame
{
   if( gameStatus == STARTED )
   {
      gameStatus = PAUSED;
      DPSRemoveTimedEntry( flowTimer );
   }
   else if( gameStatus == PAUSED )
   {
      gameStatus = STARTED;
      flowTimer = DPSAddTimedEntry(TIMEINTERVAL,&stepFun,self,NX_BASETHRESHOLD);      
   }
   return self;
}


/*----------------------------------------------------------------------*/
/* Purpose      :                                                       */ 
/* Parameters   :                                                       */
/* Return value :                                                       */ 
/*----------------------------------------------------------------------*/
- endGame
{
   DPSRemoveTimedEntry( flowTimer );
   gameStatus = STOPPED;
   if( endGamePanel == nil )
   {
      if( ![ NXApp loadNibSection:"NEnd.nib" owner: self withNames: NO ] )
         return nil;
   }
   // [endGamePanel setFloatingPanel: YES ];
   [endGamePanel setBecomeKeyOnlyIfNeeded: YES];
   [endGamePanel makeKeyAndOrderFront: nil ];
   return self;
}



/*----------------------------------------------------------------------*/
/* Purpose      :                                                       */ 
/* Parameters   :                                                       */
/* Return value :                                                       */ 
/*----------------------------------------------------------------------*/
- continueGame:sender
{
   [continuePanel close];
   gameScore += levelBonus;
   gameScore -= unusedPieces * UNUSED_PIECE_PENALTY_POINTS; 
   levelBonus = 0;

   [self newLevel];
   return self;
}


/*----------------------------------------------------------------------*/
/* Purpose      :                                                       */ 
/* Parameters   :                                                       */
/* Return value :                                                       */ 
/*----------------------------------------------------------------------*/
- (int) newCellPosition: (int *) newRow : (int *) newColumn
{
      switch( (int) [flowCell exitDirection ] )
      {
         case EAST:
            (*newColumn)++;
            break;
         case NORTH:
            (*newRow)--;
            break;
         case WEST:
            (*newColumn)--;
            break;
         case SOUTH:
            (*newRow)++;
            break;
         default:
            fprintf(stderr,"Invalid exit direction from cell. [ %s, %d]\n", __FILE__, __LINE__ );
            exit(1);
            break;
      }

      if( (*newColumn) < 0 || (*newColumn) >=  NP_GAME_COLUMNS )
         return FALSE;
      if( (*newRow) < 0 || (*newRow) >= NP_GAME_ROWS  )
         return FALSE;
      return TRUE;
}





/*----------------------------------------------------------------------*/
/* Purpose      :                                                       */ 
/* Parameters   :                                                       */
/* Return value :                                                       */ 
/*----------------------------------------------------------------------*/
- singleClick:sender
{
   int pipe;
   id currentCell;

   if( gameStatus == STARTED )
   {
      currentCell = [gameMatrix selectedCell];
      if( [currentCell isPipeReplaceable] )
      {
         pipe = [ queue getPipePiece ];
         [queue selectPipePiece];
         [currentCell assignPipe: pipe];
         unusedPieces++;

         [self lockFocus];
         [gameMatrix drawCellInside: currentCell ];
         [self unlockFocus];
      }
   }
   return self;
}






/*----------------------------------------------------------------------*/
/* Purpose      :                                                       */ 
/* Parameters   :                                                       */
/* Return value :                                                       */ 
/*----------------------------------------------------------------------*/
- step
{
   id prevFlowCell = 0;
   int newFlowRow;
   int newFlowColumn;

   
   [flowCell continueFlow];
   if( [flowCell isOverFlow] )
   {
      prevFlowCell = flowCell;
      //----------------------------------------------------------------------
      // Get next cell flow coordinates (row and column).
      //----------------------------------------------------------------------
      [gameMatrix getRow: (int *) &newFlowRow andCol: (int *) &newFlowColumn ofCell: flowCell ];
      if( ![self newCellPosition: &newFlowRow : &newFlowColumn] )
      {
         [self endLevel];
         return self;
      }
      flowCell = [gameMatrix cellAt:newFlowRow :newFlowColumn ];
      [flowCell setFlowEntryDirection: exit2entry( [ prevFlowCell exitDirection] ) ];
      if( ![flowCell isFlowPossible] )
      {
         if( levelPiecesRemaining > 0 )
            [self endGame];
         else
            [self endLevel];
         return self;
      }
      [flowCell continueFlow];
      unusedPieces--;
      if( levelPiecesRemaining > 0 ) 
      {
         levelPiecesRemaining--;
         [theRest  setIntValue: levelPiecesRemaining];
      }
   }
   [self lockFocus];
   if( prevFlowCell )
      [gameMatrix drawCellInside:prevFlowCell];
   [gameMatrix drawCellInside: flowCell ];
   [self unlockFocus];

   gameScore += [flowCell getScorePoints];
   levelBonus += [flowCell getBonusPoints];
   [theScore setIntValue: gameScore];
   [theBonus setIntValue: levelBonus];
   return self;
}






/*----------------------------------------------------------------------*/
/* Purpose      :                                                       */ 
/* Parameters   :                                                       */
/* Return value :                                                       */ 
/*----------------------------------------------------------------------*/
- free
{
   if( gameStatus == STARTED || gameStatus == FASTFORWARD )
   {
      gameStatus = PAUSED;
      DPSRemoveTimedEntry( flowTimer );
   }
   [gameMatrix free];
   return [super free];
}





/*----------------------------------------------------------------------*/
/* Purpose      :                                                       */
/* Parameters   :                                                       */
/* Return value :                                                       */
/*----------------------------------------------------------------------*/
- speedUpFlow
{
   if( gameStatus == STARTED )
   {
      gameStatus = FASTFORWARD;
      DPSRemoveTimedEntry( flowTimer );
      flowTimer = DPSAddTimedEntry(FASTINTERVAL,&stepFun,self,NX_BASETHRESHOLD);
   }
   return self;
}
@end
