#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "config.h"
#include "rockdodger_globals.h"
#include "highscore_io.h"
#include "SFont.h"

// High score table
struct highscore high[EDITION_MAXIMUM_EDITION][MAXIMUM_HIGH_SCORE_ENTRIES] = {
  {
    {13000,"Pad",2,0},
    {12500,"Pad",2,0},
    {6500,"Pad",1,0},
    {5000,"Pad",1,0},
    {3000,"Pad",1,0},
    {2500,"Pad",1,0},
    {2000,"Pad",1,0},
    {1500,"Pad",1,0}
  }, {
    {4000, "Pad", 5, 0},
    {3200, "Pad", 4, 0},
    {2600, "Pad", 3, 0},
    {2200, "Pad", 3, 0},
    {1800, "RPK", 2, 0},
    {1500, "Pad", 2, 0},
    {1200, "RPK", 2, 0},
    {900, "RPK", 1, 0}
  }
};

long score = 0;
int scorerank = -1;
char name_input_buf[512];

static const char *HIGHSCORE_HEADER_FORMAT = "rockdodger highscore %x\n";
static const int HIGHSCORE_VERSION = 5;
static const char *HIGHSCORE_EDITION_FORMAT = "edition %d\n";

FILE *hs_fopen(const char *mode) {
  mode_t mask;
  char s[1024];
  FILE *f = NULL;

  s[sizeof(s) - 1] = '\0';
  snprintf(s, sizeof(s) - 1, "%s/rockdodger.scores", GAMESDIR);
  mask = umask(0111);
  if((f = fopen(s, mode)) == NULL) {
    umask(0117);
    snprintf(s, sizeof(s) - 1, "%s/.rockdodger_high", getenv("HOME"));
    if((f = fopen(s, mode)) == NULL) {
      fprintf(stderr, "Could not open highscore file '%s', mode '%s'!\n", s,
	      mode);
    }
  }
  umask(mask);
  return f;
}

void read_high_score_table() {
  FILE *f;
  int i, giveup = 0;
  int edition = 0;
  int got_edition = 1;

  if((f = hs_fopen("r")) != NULL) {
    // If the file exists, read from it
    if(fscanf(f, HIGHSCORE_HEADER_FORMAT, &i) == 1) {
#ifdef DEBUG
      printf(HIGHSCORE_HEADER_FORMAT, i);
#endif
      if(i == HIGHSCORE_VERSION) {
	do {
#ifdef DEBUG
	  char debugbuf[889];
	  long pos = ftell(f);
	  if(fgets(debugbuf, sizeof(debugbuf), f) == NULL) {
	    perror("fgets in read_high_score_table()");
	    break; //the loop
	  }
	  printf("next line pos=%04lX '%s'\n", pos, debugbuf);
	  fseek(f, pos, SEEK_SET);
#endif
	  i = fscanf(f, HIGHSCORE_EDITION_FORMAT, &edition);
#ifdef DEBUG
	  printf("i=%d: ", i);
	  printf(HIGHSCORE_EDITION_FORMAT, edition);
#endif
	  if(i == 1) /* read OK */ {
	    giveup = 0;
	    if(edition < 0 || edition >= EDITION_MAXIMUM_EDITION) {
	      fprintf(stderr, "Illegal edition $%X at pos $%lX encountered!\n", edition, ftell(f));
	      /* No gurus yet, what a
		 shame... guru_meditation(GM_FLAGS_RECOVERY |
		 GM_FLAGS_CHOICE | GM_FLAGS_ABORTIFY, GM_SS_Misc |
		 GM_GE_BadParm | GURU_SEC_highscore_io,
		 (void*)(0x01000000 | (edition & 0xffffff)));
	       */
	      continue;
	    }
	  } else /* none read, or error */ {
	    got_edition = 0;
	    giveup = 1;
	  }
	  for(i = 0; i < MAXIMUM_HIGH_SCORE_ENTRIES && !giveup; ++i) {
	    char s[1024];
	    long int highscore;
	    int scanret;
	    if(high[edition][i].allocated) {
	      free(high[edition][i].name);
	      high[edition][i].allocated = 0;
	    }
	    scanret = fscanf(f, "%ld %1023[^\n]", &highscore, s);
#ifdef DEBUG
	    printf("i=%d scanret=%d pos=%04lX highscore=%5ld '%s'\n", i, scanret, ftell(f), highscore, s);
#endif
	    switch (scanret) {
	    case 2:
	      high[edition][i].name = strdup(s);
	      high[edition][i].score = highscore;
	      high[edition][i].allocated = 1;
	      break;
	    case 1:
	      high[edition][i].name = "";
	      high[edition][i].score = highscore;
	      high[edition][i].allocated = 0;
	      break;
	    default:
	      giveup = 1;
	      break;
	    }
	  }
	  /* This is needed as the 'scanret = fscanf(f, "%ld
	     %1023[^\n]", &highscore, s)' reads until a LF is
	     encountered. *But* this LF is *not* consumed so, in order
	     of the rest of the loop ('i = fscanf(f,
	     HIGHSCORE_EDITION_FORMAT, &edition);') to work correctly,
	     we have to skip the LF. Stupid of me to forget this and
	     to do a lot of debugging and debug print output. Phew! */
	  i = fgetc(f);
	} while(got_edition && i != EOF);
      }
    }
    fclose(f);
  }
}

void write_high_score_table() {
  FILE *f;
  int edition, i;

  if((f = hs_fopen("w")) != NULL) {
    // If the file exists, write to it
    fprintf(f, HIGHSCORE_HEADER_FORMAT, HIGHSCORE_VERSION);
    for(edition = 0; edition < EDITION_MAXIMUM_EDITION; ++edition) {
      fprintf(f, HIGHSCORE_EDITION_FORMAT, edition);
      for(i = 0; i < MAXIMUM_HIGH_SCORE_ENTRIES; ++i) {
	fprintf(f, "%ld %s\n", high[edition][i].score, high[edition][i].name);
      }
    }
    fclose(f);
  }
/* #ifdef DEBUG */
/*   FILE *debugfile = fopen("$HIGHSCORE:MEMORY", "w"); */
/*   if(debugfile) { */
/*     fwrite(high, sizeof(name_input_buf), 1, debugfile); */
/*     fseek(debugfile, 0x1000, SEEK_SET); */
/*     fwrite(high, sizeof(struct highscore), MAXIMUM_HIGH_SCORE_ENTRIES, debugfile); */
/*     for(i = 0; i < MAXIMUM_HIGH_SCORE_ENTRIES; ++i) { */
/*       fseek(debugfile, 0x2000 + 0x0100 * i, SEEK_SET); */
/*       fwrite(high[i].name, sizeof(char), 0x0100, debugfile); */
/*     } */
/*     fclose(debugfile); */
/*   } */
/* #endif */
}


void inc_score(int x, int y, long dscore) {
  score += dscore;
}

long clear_score(void) {
  long old = score;
  score = 0.0;
  return old;
}

void game_over() {
  int i;

  clearBuffer();
  state_timeout = 5.0e6;
  
  if(score >= high[current_edition][MAXIMUM_HIGH_SCORE_ENTRIES - 1].score) {
    // Read the high score table from the storage file
    read_high_score_table();
    
    // Find ranking of this score, store as scorerank
    for(i = 0; i < MAXIMUM_HIGH_SCORE_ENTRIES; ++i) {
      if(high[current_edition][i].score <= score) {
	scorerank = i;
	break;
      }
    }
    
    // Lose the lowest name forever (loser!)
    if(high[current_edition][MAXIMUM_HIGH_SCORE_ENTRIES - 1].allocated) {
      high[current_edition][MAXIMUM_HIGH_SCORE_ENTRIES - 1].allocated = 0;
      free(high[current_edition][MAXIMUM_HIGH_SCORE_ENTRIES - 1].name);
    }
    // Move all lower scores down a notch
    for(i = MAXIMUM_HIGH_SCORE_ENTRIES - 1; i > scorerank; --i) {
      high[current_edition][i] = high[current_edition][i - 1];
    }

    // Insert blank high score
    high[current_edition][scorerank].score = score;
    high[current_edition][scorerank].name = "";
    high[current_edition][scorerank].allocated = 0;
  }
}

