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

#include <SDL/SDL.h>
#include <SDL/SDL_audio.h>
#include <SDL/SDL_mixer.h>

/* P.K. */
#include "types.h"
#include "common.h"
#include "macros.h"
#include "sprites.h"

#include "main.h"
#include "options.h"
#include "evenements.h"

/* Various languages localization */
#include <locale.h>
#include <libintl.h>

/* Move some things to the the macros.h or common.h ? */

/* fill the dynamic structure of the nive field */
void chargeniv_dynamic(FILE *fich, Plan * p_plan, int tete, int danslemenu, int gamemode) {
  int x, y;    // coordinates in the plan
  int i, j, t; // index varibles
  char niv, c; // element chars
  int err;     // P.K. return value
  int positions[MAX_NPERSO]; // P.K. starting positions of the persones
  int nparams;               // total number of params of one element
  Parameter *localparams;    // local variable for all parametres has maximal p_plan->nivewidth x p_plan->niveheight elements
  char wrongchar[MAX_STRING_LENGTH];        // P.K. error message containing the wrong element

  if (tete) { // whether to read parametres from the fich file
    err = fscanf(fich, "REGEN=%i\n", &REGEN); // P.K. testing for errors isn't missing any more!
    if (err == 0 || err == EOF)
      errormessage(gettext("Error while scaning the REGEN value of the *.niv file!"), NULL);
    err = fscanf(fich, "BONUSFREQ=%g\n", &bonus_freq);
    if (err == 0 || err == EOF)
      errormessage(gettext("Error while scaning the BONUSFREQ value of the *.niv file!"), NULL);
    err = fscanf(fich, "NBOMBES=%i\n", &NBOMBES);
    if (err == 0 || err == EOF)
      errormessage(gettext("Error while scaning the NBOMBES value of the *.niv file!"), NULL);
    err = fscanf(fich, "REBOURS=%i\n", &REBOURS);
    if (err == 0 || err == EOF)
      errormessage(gettext("Error while scaning the REBOURS value of the *.niv file!"), NULL);

    //#define newnive
    //#ifdef new_nive_syntax
    /* The nive size */
    err = fscanf(fich, "XSIZE=%i\n", &(p_plan->nivewidth)); // pointer
    if (err == 0 || err == EOF)
      errormessage(gettext("Error while scaning the XSIZE value of the *.niv file!"), NULL);
    err = fscanf(fich, "YSIZE=%i\n", &(p_plan->niveheight)); // pointer
    if (err == 0 || err == EOF)
      errormessage(gettext("Error while scaning the YSIZE value of the *.niv file!"), NULL);
    /*
#else // new_nive_syntax
    p_plan->nivewidth = NIVEWIDTH;
    p_plan->niveheight = NIVEHEIGHT;
#endif // new_nive_syntax
    */

    // set the plan size
    resolution.plan_width = 32*p_plan->nivewidth;
    resolution.plan_height = 32*p_plan->niveheight;
    resolution.x_offset = (resolution.window_width-32*(p_plan->nivewidth+4))/2;
    resolution.y_offset = (resolution.window_height-32*p_plan->niveheight)/2;

    // Starting positions
    if (gamemode) { // P.K. whether to permuate the starting positions
      for (i=0; i < numperso; i++) {
	positions[i] = -1;  // No position is valid
      }
      for (i=0; i < numperso; i++) { // each of the starting position
	t = aleat(numperso-i);
	for (j=0; j<=t; j++) {
	  if (positions[j]!=-1) { // this position is already used
	    t++; // try the next position
	  }
	}
	positions[t] = i; // this is the t-th free position
      }
    }
    if (gamemode) // permutate only if in the gamemode
      for (i=0; i < numperso; i++) {
	err = fscanf(fich, "perso[%*i]:(%i,%i)\n", &x, &y);
        if (err == 0 || err == EOF)
          errormessage(gettext("Error while scaning the perso value of the *.niv file!"), NULL);
	perso[positions[i]].pos.x=x;
	perso[positions[i]].pos.y=y;
      }
    else
      for (i=0; i < numperso; i++) {
	err = fscanf(fich, "perso[%*i]:(%i,%i)\n", &x, &y);
        if (err == 0 || err == EOF)
          errormessage(gettext("Error while scaning the perso value of the *.niv file!"), NULL);
	perso[i].pos.x=x;
	perso[i].pos.y=y;
      }
  }

  /* The rest till the nive array */
  do {
    fread(&niv, 1, 1, fich);
  } while (niv!='{');

  /* allocate the gameplan */
  if (gamemode || danslemenu) {
    allocplan(p_plan);
  }
  else {
    p_plan->nivewidth += 2;
    allocplan(p_plan);
    p_plan->nivewidth -= 2;
  }

  /* allocate memory */
  localparams = (Parameter *) malloc(sizeof(Parameter)*(p_plan->niveheight)*(p_plan->nivewidth));

  /* fill the gameplan */
  fread(&c, 1, 1, fich);
  for(y=0; y < p_plan->niveheight; y++)
    for(x=0; x < p_plan->nivewidth; x++)
      {
	/* count for each element */
	nparams=0;

	/* skip all white spaces */
	niv=c;
	while (niv=='\n' || niv==' ') { // (newlines and spaces)
	  fread(&niv, 1, 1, fich);
	}

	fread(&c, 1, 1, fich); // the parametres
	while (c=='[') {
	  fscanf(fich, "%i]", &localparams[nparams]);
	  nparams++; // pointer to the nex one
	  fread(&c, 1, 1, fich);
	}

	/* fill the element */
	p_plan->element[x][y].elem = sol;
	p_plan->element[x][y].type = LIBRE;
	p_plan->element[x][y].anim = 0;
	p_plan->element[x][y].frame = 0;
	p_plan->element[x][y].bombe = 0;
	p_plan->element[x][y].sol = 0;
	p_plan->element[x][y].fixe = 0;
	p_plan->element[x][y].elemsg = NULL;
	p_plan->element[x][y].nparams = nparams;
	if (nparams > 0) {
	  p_plan->element[x][y].params = (Parameter *)malloc(sizeof(Parameter)*nparams); // allocate memory
	  /* all the parametres */
	  for (i=0; i<nparams; i++)
	    p_plan->element[x][y].params[i] = localparams[i];
	}
	else {
	  p_plan->element[x][y].params = NULL; // don't forget to initialize the NULL pointer
	}

	switch (niv) {
	case '_' : // space
	  {
	    if (!nparams)
	      {
		if (rand()>0.9*RAND_MAX) {// ?? why 0.9 - only sometimes?
		  p_plan->element[x][y].anim = 1;
		}
		p_plan->element[x][y].frame = aleat(sol->anims[p_plan->element[x][y].anim]->nframes);
	      }
	    else
	      {
		p_plan->element[x][y].anim=2;
		p_plan->element[x][y].frame=localparams[0];
		p_plan->element[x][y].fixe=1;
	      }
	  }break;
	case '#' : // block
	  {
	    p_plan->element[x][y].elem=bloc;
	    p_plan->element[x][y].type=DESTRUCTIBLE;
	    p_plan->element[x][y].anim=0;
	    p_plan->element[x][y].sol=1;
	    if (!nparams)
	      p_plan->element[x][y].frame=aleat( bloc->anims[0]->nframes);
	    else {
	      p_plan->element[x][y].frame=localparams[0];
	      p_plan->element[x][y].fixe=1;}
	  }break;
	case '+' : // wall
	  {
	    p_plan->element[x][y].elem=mur;
	    p_plan->element[x][y].type=INDESTRUCTIBLE;
	    //	    p_plan->element[x][y].sol=1; // it has it's own background
	    if (!nparams)
	      p_plan->element[x][y].frame=aleat( mur->anims[0]->nframes);
	    else {
	      p_plan->element[x][y].frame=localparams[0];
	      p_plan->element[x][y].fixe=1;}
	  }break;
	case '§' : // hidden pasage (it has it's own background)
	  {
	    p_plan->element[x][y].elem=mur; // like an ordinary wall  P.K.
	    p_plan->element[x][y].type=SECRETPASSAGE; // P.K.
	    //	    plan[x][y].sol=1; // it has it's own background
	    if (!nparams)
	      p_plan->element[x][y].frame=aleat(mur->anims[0]->nframes);
	    else {
	      p_plan->element[x][y].frame=localparams[0];
	      p_plan->element[x][y].fixe=1;}
	  }break;
	case '=' : // tunnel
	  {
	    p_plan->element[x][y].elem=tunnel;
	    p_plan->element[x][y].type=TUNNEL;
	    p_plan->element[x][y].frame=localparams[0];
	    p_plan->element[x][y].fixe=-1;
	  }break;
	case '>' : // one-way
	  {
	    p_plan->element[x][y].elem=fleche;
	    p_plan->element[x][y].type=SENSUNIQUE;
	    p_plan->element[x][y].anim=localparams[0];
	    p_plan->element[x][y].sol=1;
	    p_plan->element[x][y].fixe=-1;
	    if (danslemenu==2) /* Menu de choix du perso */
	      animsplus(fleche, localparams[0], 10, 0, x, y);
	  }break;
	case 'T' : // teleport
	  {
	    p_plan->element[x][y].elem=teleport;
	    p_plan->element[x][y].type=EVENEMENT;
	    p_plan->element[x][y].sol=1;
	    p_plan->element[x][y].fixe=-1;
	    /* only in the game activ */
	    if (gamemode) {
	      p_plan->element[x][y].evt=teleportation;
	      animsplus(teleport, 0, 5, 0, x, y);
	    }
	  }break;
	case 'O' : // button
	  {
	    p_plan->element[x][y].elem=bouton;
	    p_plan->element[x][y].type=EVENEMENT;
	    p_plan->element[x][y].sol=1;
	    p_plan->element[x][y].fixe=-1; // visible
	    /* only in the game activ */
	    if (gamemode) {
	      p_plan->element[x][y].evt=changecase;
	    }
	  }break;
	case '0' : // invisible button P.K.
	  {
	    p_plan->element[x][y].type=EVENEMENT;
	    if (gamemode) { // only in the game activ
	      p_plan->element[x][y].evt=changecase;
	    }
	    else {
	      p_plan->element[x][y].elem=bouton; // in the editor like an ordinary button
	      //??	      p_plan->element[x][y].sol=1;
	      p_plan->element[x][y].fixe=-2;     // invisible
	      p_plan->element[x][y].frame=bouton->anims[0]->nframes-1; // gray button
	    }
	  }break;
	case '*' : // fontaine
	  {
	    p_plan->element[x][y].elem=fontaine;
	    p_plan->element[x][y].type=EVENEMENT;
	    p_plan->element[x][y].sol=1;
	    p_plan->element[x][y].fixe=-1;
	    if (gamemode) {
	      p_plan->element[x][y].evt=recharge;
	      animsplus(fontaine, 0, 3, 0, x, y);
	    }
	  }break;
	case 'M' : //??
	  {
	    p_plan->element[x][y].elem=sol;
	    p_plan->element[x][y].type=EVENEMENT;
	    p_plan->element[x][y].evt=menuevt;
	    if (danslemenu==2 || (99<localparams[0] && localparams[0]<106)) /* Menu de choix du perso */
	      animsplus(bouton, 0, 3, 0, x, y);
	  }break;
	case 'P' : //??
	  {
	    p_plan->element[x][y].elem=sol;
	    fauxperso[localparams[0]].x=x;
	    fauxperso[localparams[0]].y=y;
	    p_plan->element[x][y].type=EVENEMENT;
	    p_plan->element[x][y].params[0]=localparams[0]+121;
	    p_plan->element[x][y].evt=menuevt;
	  }break;
	/* Les deux derniers sont pour l'écran des options */
	case 'R' : //??
	  {
	    p_plan->element[x][y].elem=sol;
	    p_plan->element[x][y].type=EVENEMENT;
	    p_plan->element[x][y].evt=reglage;
	    animsplus(bouton, 1, 0, 0, x, y);
	    evtsplus(dessineregl, 0, x, y, 0, 0, localparams[0], AVANT, NULL);
	  }break;
	case 'S' : //??
	  {
	    p_plan->element[x][y].elem=sol;
	    p_plan->element[x][y].type=EVENEMENT;
	    p_plan->element[x][y].evt=onoff;
	    evtsplus(dessinebouton, 0, x, y, 0, 0, localparams[0], AVANT, NULL);
	  }break;
	default :
	  {
	    sprintf(wrongchar, "'%c at (%i,%i)'", niv, x, y);
	    errormessage(gettext("Ce niveau contient des éléments spéciaux. "
			         "Vous ne pouvez pas l'éditer avec nived"), wrongchar);
	    //L	      erreur(gettext("Nive contents an invalid element spedification. "
	    //L                     "The editor nived is unable to handle this file."), NULL);
	  }break;
	}
      }
  
  /* Free memory */
  free(localparams);

  /* read till the end of the nive array */
  do {
    fread(&niv, 1, 1, fich);
  } while (niv!='}');

  return;
}


/* to separate common parts for bi from the special parts for nived */
void finishniv_dynamic_for_bi(Plan *p_plan, int tete, int danslemenu, int gamemode)
{
  int x, y;    // coordinates in the plan
  int i, j, t; // index varibles
  int positions[MAX_NPERSO]; // P.K. starting positions of the persones
  //?  int nparams;               // total number of params of one element
  Parameter *localparams;    // local variable for all parametres has maximal p_plan->nivewidth x p_plan->niveheight elements
  int tmpx, tmpy;            // for exchange only

  // permutation of the starting positions
  for (i=0; i < numperso; i++) {
    positions[i] = -1;  // No position is valid
  }
  for (i=0; i < numperso; i++) { // each of the starting position
    t = aleat(numperso-i);
    for (j=0; j<=t; j++) {
      if (positions[j]!=-1) { // this position is already used
	t++; // try the next position
      }
    }
    positions[t] = i; // this is the t-th free position
  }
  // set the starting positions permutated
  for (i=0; i < numperso; i++) {
    tmpx = perso[i].pos.x;
    perso[i].pos.x = perso[positions[i]].pos.x;
    perso[positions[i]].pos.x = tmpx;

    tmpy = perso[i].pos.y;
    perso[i].pos.y = perso[positions[i]].pos.y;
    perso[positions[i]].pos.y = tmpy;
  }

  // add some element atributes
  for(y=0; y < p_plan->niveheight; y++) {
    for(x=0; x < p_plan->nivewidth; x++) {

      if (p_plan->element[x][y].elem == fleche) { // one-way
	if (danslemenu==2) /* Menu de choix du perso */
	  animsplus(fleche, localparams[0], 10, 0, x, y);
      }

      if (p_plan->element[x][y].elem == teleport) { // teleport
	if (gamemode) {
	  p_plan->element[x][y].evt=teleportation;
	  animsplus(teleport, 0, 5, 0, x, y);
	}
      }

      if (p_plan->element[x][y].elem == bouton) { // button
	if (gamemode) {
	  p_plan->element[x][y].evt=changecase;
	}
      }

      /*
      if (plan[x][y].elem == hiddenbouton) { // invisible button P.K.
	if (gamemode) { // only in the game activ
	  plan[x][y].evt=changecase;
	}
      }
      */

      if (p_plan->element[x][y].elem == fontaine) { // fontaine
	if (gamemode) {
	  p_plan->element[x][y].evt=recharge;
	  animsplus(fontaine, 0, 3, 0, x, y);
	}
      }

	  /*
	case 'M' : //??
	  {
	    plan[x][y].elem=sol;
	    plan[x][y].type=EVENEMENT;
	    plan[x][y].evt=menuevt;
	    if (danslemenu==2 || (99<localparams[0] && localparams[0]<106)) // Menu de choix du perso
	      animsplus(bouton, 0, 3, 0, x, y);
	  }break;
	case 'P' : //??
	  {
	    plan[x][y].elem=sol;
	    fauxperso[localparams[0]].x=x;
	    fauxperso[localparams[0]].y=y;
	    plan[x][y].type=EVENEMENT;
	    plan[x][y].params[0]=localparams[0]+121;
	    plan[x][y].evt=menuevt;
	  }break;
	  // Les deux derniers sont pour l'écran des options
	case 'R' : //??
	  {
	    plan[x][y].elem=sol;
	    plan[x][y].type=EVENEMENT;
	    plan[x][y].evt=reglage;
	    animsplus(bouton, 1, 0, 0, x, y);
	    evtsplus(dessineregl, 0, x, y, 0, 0, localparams[0], AVANT, NULL);
	  }break;
	case 'S' : //??
	  {
	    plan[x][y].elem=sol;
	    plan[x][y].type=EVENEMENT;
	    plan[x][y].evt=onoff;
	    evtsplus(dessinebouton, 0, x, y, 0, 0, localparams[0], AVANT, NULL);
	  }break;
	  */

    }
  }
  return;
}

void finishniv_dynamic_for_nived(Plan p_plan, int tete, int danslemenu, int nivedmode)
{
  /*
  int x, y;    // coordinates in the plan

      if (plan[x][y].elem == hiddenbouton) { // invisible button P.K.
	if (nivedmode) { // only in the game activ
	      plan[x][y].elem=bouton; // in the editor like an ordinary button
	      plan[x][y].sol=1;
	      plan[x][y].fixe=-2;     // invisible
	      plan[x][y].frame=bouton->anims[0]->nframes-1; // gray button
	}
      }
  */
  return;
}
