/**
 * @file 
 * File containing schemaGameUtility module
 */
#include "schemaGameUtility.h"

/** Identify the numeric base in which all parameters are inserted.*/
const unsigned int numericBase= 10;

/** Identify the maximum value that can be represented with the numeric
  * base sets into variable numericBase. */
const unsigned int maxValueOfNumericBase= 9;


/* Start function definition of schemaGameUtility module */

currentGameState initializeCurrentGameState()
{
    D1(printf("\nSCHEMAGAMEUTILITY.initializeCurrentGameState() - START\n"));

    currentGameState gameState;

    gameState.currentRow = 0;

    gameState.currentCol = 0;

    gameState.bestrouteweight = 0;

    gameState.iters = 0;

    gameState.currentlevel = 1;
    
    gameState.totalpoints = 0;

    gameState.starttime = 0;

    gameState.playing = false;

    gameState.numOfRound = 1;

    return gameState;

    D1(printf("\nSCHEMAGAMEUTILITY.initializeCurrentGameState() - END\n"));
}


double newgame( char schemafilename[], 
                unsigned int *bestrouteweight
              )
{
    D1(printf("\nSCHEMAGAMEUTILITY.newgame() - START\n"));

    unsigned short int errnum = 0; // No errors at start

    // Create the matrix gameMap, describing the schema
    errnum = loadCheckGame(schemafilename);
    D4(printf("\n'returns': errnum=%u\n",errnum));
	
    if(errnum > 0){
        D1(printf("\nSCHEMAGAMEUTILITY.newgame() - Error & END\n"));    
        return -1; //error/s
    }

    /* Only if debug modality (D1) is active, print the schema on stdout */
    printschema();

    clock_t tStart, tEnd;
    tStart=clock();

    // Finding START point, all DOORS and number of doors in the schema
    unsigned int *doorsRow = (unsigned int *)malloc(sizeof(unsigned int)*
                                        (schemaData.numRow*schemaData.numCol));

    unsigned int *doorsCol = (unsigned int *)malloc(sizeof(unsigned int)*
                                        (schemaData.numRow*schemaData.numCol));

    // return number of doors (number of start point is always 1)
    unsigned int numOfDoors = getdoors( doorsRow,doorsCol ); 
	
    // allocation of variables which containing the doors (exactly number)
    unsigned int *doorsRowExact = (unsigned int *)malloc(sizeof(unsigned int)*
                                                    numOfDoors              );

    unsigned int *doorsColExact = (unsigned int *)malloc(sizeof(unsigned int)*
                                                    numOfDoors              );
	
    unsigned int i=0;
    // assignement of doors without memory waste
    for(i=0;i<numOfDoors;i++)
    {
        doorsRowExact[i] = doorsRow[i];
        doorsColExact[i] = doorsCol[i];
    }

    // deallocation of uneless variables
    free(doorsRow);
    free(doorsCol);
    D1(printf("\nSCHEMAGAMEUTILITY.newgame() - after deallocation\n"));
	
    // route calculation
    unsigned int bestdoorid = numOfDoors+1;
    (*bestrouteweight) = bestroute( doorsRowExact,doorsColExact,
                                           numOfDoors, &bestdoorid     );
	
    D4(printf("(returns -> %u)\n",*bestrouteweight));
	
    if( ( bestdoorid > numOfDoors ) || ( *bestrouteweight < 1 ) ){
        D2(printf("\nSchema error! Need at least one reachable door."));
        D1(printf("\nSCHEMAGAMEUTILITY.newgame() - Error & END\n"));
        free(doorsRowExact);
        free(doorsColExact);
        return -1; //error/s
    }

    assert(!(( bestdoorid > numOfDoors ) || (*bestrouteweight < 1 )));

    D2(printf("\nBest route (%u,%u)->(%u,%u) has weight %u.",
            schemaData.startRow,schemaData.startCol,doorsRowExact[bestdoorid],
            doorsColExact[bestdoorid],*bestrouteweight   ));

    free(doorsRowExact);
    free(doorsColExact);

    tEnd=clock();
    // Elapsed time in seconds
    double elapsed = (double)(tEnd-tStart)/CLOCKS_PER_SEC; 
	
    D2(printf("\nSchema dimension(%uX%u):",
                 schemaData.startRow,schemaData.startCol));
    D5(printf("\nClock at start: %lu",tStart));
    D5(printf("\nClock at   end: %lu",tEnd));
    D5(printf("\nCPU clock per second: %lu",CLOCKS_PER_SEC));
    D1(printf("\nElapsed time: %f second(s)",elapsed));
    D1(printf("\nSCHEMAGAMEUTILITY.newgame() - END\n"));
    return elapsed;
}

bool checkRowColValues(unsigned short int *errnum, char *err)
{      
    D1(printf("\nSCHEMAGAMEUTILITY.checkRowColValues() - START\n"));

    bool schema_ok = true;

    // Parameter number of rows of the schema
    if((schemaData.numRow)<MINR){ 
        strcat(err,"\nNumber of rows lower then minimum allowed.");	
        *errnum = *errnum + 1;
        schema_ok = false; 
    }
    
    if((schemaData.numRow)>MAXR){
        strcat(err,"\nNumber of rows more then maximum allowed.");	
        *errnum = *errnum + 1; 
        schema_ok = false;
    }
    //End reading and checking of the parameter number of rows of the schema
    
    // Parameter number of columns of the schema
    if((schemaData.numCol)<MINC){   
        strcat(err,"\nNumber of columns lower then minimum allowed.");	
        *errnum = *errnum + 1; 
        schema_ok = false;
    }
    
    if((schemaData.numCol)>MAXC){
        strcat(err,"\nNumber of columns more then maximum allowed.");	
        *errnum = *errnum + 1; 
        schema_ok = false;
    }
    /* End reading and checking of the parameter number of columns of 
        the schema */

    D1(printf("\nSCHEMAGAMEUTILITY.checkRowColValues() - END\n"));
    return schema_ok;
}

void setSchemaDataStructures(char **schema_propr)
{
    D1(printf("\nSCHEMAGAMEUTILITY.setSchemaDataStructures() - START\n"));

    unsigned int i = 0, j = 0;
    // alloc rigth schema dimensions and visited cell structure
    schemaData.gameMap = (char **) 
                            malloc(sizeof(char *)*(schemaData.numRow));
                            
    schemaData.visitedCellInfo = (unsigned short int **)
                    malloc(sizeof(unsigned short int *)*(schemaData.numRow));
                    
	for(i=0; i<(schemaData.numRow); i++)
	{
	    schemaData.gameMap[i] = (char *)
	                        malloc(sizeof(char)*(schemaData.numCol));
	                        
	    schemaData.visitedCellInfo[i] = (unsigned short int *)
	              malloc(sizeof(unsigned short int)*(schemaData.numCol));
	              
	    for(j=0; j<(schemaData.numCol); j++)
	    {
		    schemaData.gameMap[i][j] = schema_propr[i][j];
		    schemaData.visitedCellInfo[i][j] = UNVISITED;
		}
	}
	    
    D1(printf("\nSCHEMAGAMEUTILITY.setSchemaDataStructures() - END\n"));
}

void checkDoorStartPoint(unsigned short int *errnum, char *err)
{
    D1(printf("\nSCHEMAGAMEUTILITY.checkDoorStartPoint() - START\n"));

    unsigned int i = 0, j = 0;
    bool doorok = false;
	unsigned int startok = 0;
	for(i=0; (i<(schemaData.numRow)) && (startok<=1); i++)
	    for(j=0; (j<(schemaData.numCol)) && (startok<=1) ;j++)
		{
		    if( (!doorok) && (schemaData.gameMap[i][j]==DOOR) )
		        doorok = true;

		    if(schemaData.gameMap[i][j]==START)
			{
				startok++;
				schemaData.startRow = i;
				schemaData.startCol = j;
			}
		}
	
	if(!doorok)    { strcat(err,"\nNo doors in the schema.\n");	
                        *errnum = *errnum + 1; }
	
	if(startok != 1){ 
            strcat(err,"\nWrong number of start point in the schema.\n");
            *errnum = *errnum + 1; }

    D1(printf("\nSCHEMAGAMEUTILITY.checkDoorStartPoint() - END\n"));
}

unsigned short int loadCheckGame( char *file_name )
{      
    D1(printf("\nSCHEMAGAMEUTILITY.loadCheckGame() - START\n"));
    D6(printf("\nparams: file_name=%s ",file_name));

    unsigned short int errnum = 0;

    // Cheching of parameters
    char err[MAX_ERR_BUFFER_LEN];
    strcpy(err,"\0"); // no errors
    
    char **schema_propr = NULL;
    
    // Start reading and checking schema
    bool schema_ok = false;
    schemaData.numRow=0;
    schemaData.numCol=0;
    schema_propr = readfile( file_name, 
                             &schema_ok, 
                             &schemaData.numRow, 
                             &schemaData.numCol, 
                             MAXR, 
                             MAXC, 
                             true 
                           );
    
    // if the file reading return an error
    if ( ! schema_ok  )
    {
        strcat(err,"\nAn error occurred in file reading before.");	
        errnum = errnum + 1;
    }
    else    
        schema_ok = checkRowColValues(&errnum, (err));
    
    if( schema_ok ) // if all is ok
    {
        setSchemaDataStructures(schema_propr);
        checkDoorStartPoint(&errnum, (err));
    }

    free(schema_propr);

    // Checking the errors.
    if(errnum != 0)
    {
        D1(printf("\nError in file %s : %s\n",file_name,err));
        schemaData.numRow = 0;
        schemaData.numCol = 0;
        schemaData.gameMap = (char **)malloc(sizeof(char *)); 
        // Unitary dimension fictitious
    }
    // End checking errors
    
    D1(printf("\nSCHEMAGAMEUTILITY.loadCheckGame() - END\n"));
    
    // Return the error number
    return errnum;
}


unsigned int getdoors(  unsigned int *doorsRow,  
                        unsigned int *doorsCol   
                     )
{                                    
  D1(printf("\nSCHEMAGAMEUTILITY.getdoors() - START\n"));

  unsigned int i = 0, j = 0;
  unsigned int numOfDoor = 0;
  
  for( i=0; i<schemaData.numRow; i++ )
    for( j=0; j<schemaData.numCol; j++ )
      if( schemaData.gameMap[i][j] == DOOR )
	  {
		doorsRow[numOfDoor] = i;
		doorsCol[numOfDoor] = j;
		numOfDoor++;
	  }

  D1(printf("\nSCHEMAGAMEUTILITY.getdoors() - END\n"));
  
  return numOfDoor;
}


bool isDirectory( char const *path )
{
    D5(printf("\nSCHEMAGAMEUTILITY.isDirectory() - START\n"));
    
    bool retvalue = true;

    struct stat st;
    if ( lstat( path, &st ) == -1 )
        retvalue = false;
    else
        retvalue = S_ISDIR(st.st_mode);

    D5(printf("\nPath %s is%s a directory\n", path,(retvalue)?(""):(" not") ));
    D5(printf("\nSCHEMAGAMEUTILITY.isDirectory() - END\n"));
    return retvalue;
}



char *get_schema_name( unsigned short int level)
{      
    D1(printf("\nSCHEMAGAMEUTILITY.get_schema_name() - START\n"));
       
    struct dirent **eps;
    int numElemDir;
    
    unsigned int digitlen = countDigits( level );
    
    const unsigned short int separatorLen = 5;
    char generateddir[ strlen(GAMES_DIR) + 
                       strlen(GAMES_LEVEL_DIR) + 
                       digitlen + separatorLen    ];
                       
    sprintf(generateddir,"./%s/%s%u/",GAMES_DIR,GAMES_LEVEL_DIR,level);
    
    numElemDir = scandir (generateddir, &eps, 0, 0);
    
    D2(printf("\nScandir() returns %i elements.\n", numElemDir));

    // Check if there's at least one file in the directory
    unsigned int i = 0;
    bool atleast1file = false;
    if( numElemDir > 0)
    for( ; (i<numElemDir) && (!atleast1file); i++)
    {
        assert( (i < numElemDir) && (!atleast1file) );

        char *tmppath = (char *)malloc( sizeof(char) * 
                                        ( strlen(eps[i]->d_name) +
                                          strlen(generateddir) + 1  ) );
                                                
        sprintf(tmppath,"%s%s",generateddir,eps[i]->d_name);

        atleast1file = ! isDirectory( tmppath );

        free(tmppath);
    }
    
    if(!atleast1file) // No files in directory!
    {
        D1(printf("\nNo such file in %s\n", generateddir)); 
        char *filename = (char *)malloc(sizeof(char));
        filename[0] = NULLCHAR;
        D1(printf("\nSCHEMAGAMEUTILITY.get_schema_name() - error & END\n"));
        return filename;
    }
    assert( atleast1file );

    // Select an element random until it isn't a file   
    unsigned int rd = 0;
    srand((unsigned)time(0));
    char *tmppath = NULL;
    do{
        rd = rand() % (numElemDir);

        tmppath = (char *)malloc(sizeof(char)*( strlen(eps[rd]->d_name) +
                                                strlen(generateddir)     
                                                + 1                       ) );
                                                
        sprintf(tmppath,"%s%s",generateddir,eps[rd]->d_name);

    }while( isDirectory( tmppath ) );
    free(tmppath);
    
    // return the absolute path of file
    char *filename = (char *)malloc(sizeof(char)*( strlen(eps[rd]->d_name) +
                                                strlen(generateddir)      ));
                                                
    sprintf(filename,"%s%s",generateddir,eps[rd]->d_name);
    
    D1(printf("\nSCHEMAGAMEUTILITY.get_schema_name() - END\n"));
    
    return filename;
}



bool isallowedchar(unsigned short int typeCharSel , 
                   char char2check                   
                  )
{    
    D1(printf("\nSCHEMAGAMEUTILITY.isallowedchar() - START\n"));
    D5(printf("\nSelected:"));
    D5(printf("\nLowercase letter [%c]",(typeCharSel&LOWERCASE)?'Y':'N'));
    D5(printf("\nUppercase letter [%c]",(typeCharSel&UPPERCASE)?'Y':'N'));
    D5(printf("\nDigit            [%c]",(typeCharSel&DIGITS)?'Y':'N'));
    
    // lowercase letters
    if ((char2check>='a') && (char2check<='z') && (typeCharSel & LOWERCASE))
    {
        D4(printf("\nAllowed character %c is a lowercase letter.\n"
                                    ,char2check));
        D1(printf("\nSCHEMAGAMEUTILITY.isallowedchar() - END\n"));
        return true;
    }
    
    // uppercase letters
    if ((char2check>='A') && (char2check<='Z') && (typeCharSel & UPPERCASE))
    {
        D4(printf("\nAllowed character %c is an uppercase letter.\n"
                                ,char2check                         ));
        D1(printf("\nSCHEMAGAMEUTILITY.isallowedchar() - END\n"));
        return true;
    }
    
    // digits
    if ((char2check>='0') && (char2check<='9') && (typeCharSel & DIGITS)) 
    {
        D4(printf("\nAllowed character %c is a digit.\n",char2check));
        D1(printf("\nSCHEMAGAMEUTILITY.isallowedchar() - END\n"));
        return true;
    }
    
    D4(printf("\nCharacter %c (ASCII %i) is not allowed.\n",
                    char2check,     (int)char2check         ));
    D1(printf("\nSCHEMAGAMEUTILITY.isallowedchar() - END\n"));

    return false;
}


inline unsigned int countDigits(unsigned int number)
{
    unsigned int numDigit = 1;

    while( number > maxValueOfNumericBase ){
        number /= numericBase;
        numDigit++;
    }

    return numDigit;
}


inline bool check_if_text_is_number( const bool runme,
                                     const char *text,
                                     char *err_buff,
                                     const char *err_label
                                    )
{
    if( !runme )
        return false;
    
    bool returnValue = true;
    
    unsigned int i;
    for(i = 0; i<strlen(text); i++ )
        if( ! isallowedchar( DIGITS , text[i] ) )
        {
          sprintf(err_buff,"Error in %s:\n\n", err_label );
          sprintf(err_buff,"%s   Character position is %u\n", err_buff, i);
          sprintf(err_buff,"%s   Display character is %c\n", err_buff,text[i]);
          sprintf(err_buff,"%s   ASCII value is %i\n", err_buff, (int)text[i]);
          
          returnValue = false;
        }
   
    return returnValue;
}

unsigned int calcSchemaResults( char *labeltxt,
                                currentGameState gameState
                              )
{
    D1(printf("\nGUIHANDLER.calcSchemaResults() - START\n"));

    unsigned int totalpoints = gameState.totalpoints;
    
    time_t *tmpend=NULL;
    unsigned long endtime = time(tmpend);
    free(tmpend);
    
    //We need only diff in seconds (no more precision required)
    unsigned long elapsedtime = endtime - gameState.starttime; 
    
    D2(printf("\nElapsed time ( %lu - %lu ) = %lu second(s)\n",
                            endtime,gameState.starttime,elapsedtime       ));
    
    double correctperc = ((double)gameState.bestrouteweight
                                        /
                          (double)gameState.iters)*100;
    
    D2(printf("\nCorrect prec. ( %u / %u ) = %.2lf\n",
                      gameState.bestrouteweight,gameState.iters,correctperc ));
    
    unsigned int bonus = (correctperc>=GIVE_BONUS_PERC)    ? 
                         (BONUS_POINTS) : (NO_BONUS_POINTS) ;
    
    D2(printf("\nBonus = %u points (min. correct perc. is %i)\n",
                      bonus,                    GIVE_BONUS_PERC ));
    
    /*
     Points increase when increase the level multiplied by the percentage of 
     correctness ( + bonus), points decrease when the elapsed time surpass the
     (7) seconds for the first level, the (14) for the second, the (21) for the
     third, ... [ the denominator increase of one unity only when the division
     - integer - increase of (7) seconds ]
     (Considering SEC_PER_LEVEL = 7)
    */
    unsigned long points = ceil (gameState.currentlevel * (bonus + correctperc)
                                                    / 
                                ( elapsedtime / 
                                 ( 1 + SEC_PER_LEVEL * gameState.currentlevel )
                                                                       + 1 ) );
    
    D2(printf("\nPoints round(%u*(%u+%lf)/(%lu/(1+%i*%u)+1)) = %lu \n",
                 gameState.currentlevel,bonus,correctperc,elapsedtime,
                 SEC_PER_LEVEL,gameState.currentlevel,points                ));
    
    totalpoints += points;
    
        
    sprintf(labeltxt,  "- Current schema results:\n\n");
    sprintf(labeltxt,"%s     Correctness: %.2lf%c\n",labeltxt,correctperc,'%');
    sprintf(labeltxt,"%s     Bonus: %u points\n",labeltxt,bonus);
    sprintf(labeltxt,"%s     Time: %lu second(s)\n",labeltxt,elapsedtime);
    sprintf(labeltxt,"%s     Points: %lu\n\n",labeltxt,points);
    sprintf(labeltxt,"%s- Total points: %u\n\n",labeltxt,totalpoints);
    sprintf(labeltxt,"%s- Level %u of %u",labeltxt,
                                          gameState.currentlevel,
                                          gameState.numOfRound);

    D1(printf("\nGUIHANDLER.calcSchemaResults() - END\n"));
    return totalpoints;
}


/* End function definition of schemaGameUtility module */

