/*
 * Stonage: a puzzle game
 * Copyright (C) 2001 Luc Saillard <luc@saillard.org>
 * 
 * This is a program to create a binary level from a ascii description.
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 * 
 */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#define _NO_GFX_
#include "stonage.h"
#include "level.h"
#include "level_io.h"
#include "square.h"

struct enum_list square_properties_text2enum[] =
{
   {"CAN_MOVE_TO_NORTH", CAN_MOVE_TO_NORTH},
   {"CAN_MOVE_TO_SOUTH", CAN_MOVE_TO_SOUTH},
   {"CAN_MOVE_TO_EAST", CAN_MOVE_TO_EAST},
   {"CAN_MOVE_TO_WEST", CAN_MOVE_TO_WEST},
   {"AFTER_MOVE_CRASH", AFTER_MOVE_CRASH},
   {"START", START},
   {"FINISH", FINISH},
   {"CAN_GO_ABOVE", CAN_GO_ABOVE},
   {"KEY_RED", KEY_RED},	
   {"KEY_BLUE", KEY_BLUE},	
   {"KEY_GREEN", KEY_GREEN},	
   {"NEED_KEY_RED", NEED_KEY_RED},
   {"NEED_KEY_BLUE", NEED_KEY_BLUE},
   {"NEED_KEY_GREEN", NEED_KEY_GREEN},
   {"HOURGLASS", HOURGLASS},
   {"TELEPORT1", TELEPORT1},
   {"TELEPORT2", TELEPORT2},
   {"TELEPORT3", TELEPORT3},
   {0,0 }
};


#define MAX_IMGS 1024
struct level_squares_desc
{
  unsigned int properties; /* Define properties of the square */
  unsigned int nimages;    /* number of images for animation  */
  unsigned int delay;      /* delay between 2 images in ms    */
  char **images;
};

static int do_convert(FILE *input, int output);
static void jump_space(unsigned char **ptr);
static void jump_line(unsigned char **ptr);
static void jump_word(unsigned char **ptr);
static void read_dataline(unsigned char **ptr, int sizex, unsigned int *dst);
static void read_datasquare(unsigned char **ptr,struct level_squares_desc *squares);


int verbose=1;

int main(int argc, char **argv)
{
  FILE *input;
  int output;

  
  if (argc!=3)
   {
     fprintf(stderr,"\n%s\nUsage: input output\n",argv[0]);
     exit(1);
   }

  input=fopen(argv[1],"r");
  if (input==NULL)
   {
     perror("Can't read input file");
     exit(2);
   }
  output=creat(argv[2],S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
  if (output==-1)
   {
     perror("Can't create output file");
     exit(2);
   }

  do_convert(input,output);

  if (input)
    fclose(input);
  if (output!=-1)
    close(output);

  return 0;
}

static int do_convert(FILE *input, int output)
{
  int size;
  unsigned char *buffer, *ptr;

  int mapx,mapy;
  unsigned char *comment, *background;
  unsigned int *map;
  int current_line, current_square;
  struct level_squares_desc squares[1024]; /* FIXME */
  char header[4]="LVL1";

  /* Read thei nput file in memory */
  fseek(input,0,SEEK_END);
  size = ftell(input);
  rewind(input);

  buffer=malloc(size+1);
  if (buffer==NULL)
   {
     fprintf(stderr,"Can't alloc %d bytes\n",size+1);
     exit(3);
   }
  if (fread((void *)buffer,1,size,input)!=size)
   {
     fprintf(stderr,"We have not read all the input file\nAborted\n");
     exit(2);
   }
  
  /* init */
  mapx=mapy=0;
  background=comment=NULL;
  current_square=1;
  ptr=buffer;
  while (ptr<buffer+size)
   {
     if ((*ptr=='#') ||/* Est ce que c'est un commentaire ? */
	 (*ptr=='\n')) /* Est ce que c'est une ligne vide ? */
      {
	jump_line(&ptr);
      }
     else /* C'est une ligne valide, chaque paramètre est séparé par un ou plusieurs espaces */
      {
	switch (*ptr)
	 {
	  case 'S':
	    jump_word(&ptr);
	    jump_space(&ptr);
	    sscanf(ptr,"%d",&mapx);
	    jump_word(&ptr);
	    jump_space(&ptr);
	    sscanf(ptr,"%d",&mapy);
	    if (verbose)
	      printf("mapx=%d mapy=%d\n",mapx,mapy);
	    if (mapx>0 && mapy>0)
	     {
	       map=(unsigned int *)malloc(mapx*mapy*sizeof(int));
	       if (map==NULL)
		{
		  fprintf(stderr,"Can't allocate %d bytes for the map\n",mapx*mapy*sizeof(int));
		  exit(3);
		}
	       memset(map,0,mapx*mapy*sizeof(int));
	       current_line=0;
	     }
	    else
	     {
	       fprintf(stderr,"Waring size of the map isn't positive [%d,%d]\n",mapx,mapy);
	     }
	    jump_line(&ptr);
	    break;
	  case 'B':
	     {
	       unsigned char *begin;
	       jump_word(&ptr);
	       jump_space(&ptr);
	       begin=ptr;
	       jump_line(&ptr);
	       *--ptr=0;
	       background=(unsigned char *)strdup(begin);
	       ptr++;
	       if (verbose)
		 printf("Background: <%s>\n",background);
	       break;
	     }
	  case 'C':
	     {
	       unsigned char *begin;
	       jump_word(&ptr);
	       jump_space(&ptr);
	       begin=ptr;
	       jump_line(&ptr);
	       *--ptr=0;
	       comment=(unsigned char *)strdup(begin);
	       ptr++;
	       if (verbose)
		 printf("Comment: <%s>\n",comment);
	       break;
	     }
	  case 'L':
	    if (mapx==0 || mapy==0 || map==NULL)
	     {
	       fprintf(stderr,"Size of map must be declared before data\n");
	       jump_line(&ptr);
	     }
	    jump_word(&ptr);
	    jump_space(&ptr);
	    read_dataline(&ptr,mapx,map+(current_line*mapx));
	    if (verbose)
	     {
	       int i;
	       for (i=0;i<mapx;i++)
		{
		  printf("%.2x ",map[(current_line*mapx)+i]);
		}
	       printf("\n");
	     }
	    current_line++;
	    break;
	  case 'Z':
	    jump_word(&ptr);
	    jump_space(&ptr);
	    read_datasquare(&ptr,squares+current_square);
	    if (verbose)
	     {
	       int i;
	       printf("Square %d: %4x ",current_square,squares[current_square].properties);
	       for (i=0;i<squares[current_square].nimages;i++)
		{
		  printf("%s ",squares[current_square].images[i]);
		}
	       printf("\n");
	     }
	    current_square++;
	    break;
	  default:
	    fprintf(stderr,"Warning, this character %c isn't reconized\n",*ptr);
	    jump_line(&ptr);
	 }
      }
   }


  /* Write the header */
   {
     struct level_desc lvl;
     memcpy(lvl.version,header,4);
     *(unsigned short int *)lvl.sizex=htons((unsigned short int)mapx);
     *(unsigned short int *)lvl.sizey=htons((unsigned short int)mapy);
     *(unsigned short int *)lvl.nsquares=htons((unsigned short int)current_square);
     write(output,&lvl,sizeof(struct level_desc));
   }
  /* Write the background ilage name */
  if (background==NULL)
   {
     unsigned char null=0;
     write(output,&null,1);
   }
  else
   {
     write(output,background,strlen(background)+1);
   }

  /* Write the square definition */
   {
     int i,j,l,founded_start=0,founded_finish=0;
     struct square_desc s;
     for (i=1;i<current_square;i++)
      {
	/* Calc the lenght of the block string */
	for (l=0,j=0;j<squares[i].nimages;j++)
	  l+=strlen(squares[i].images[j])+1;
	*(unsigned int *)s.properties = htonl(squares[i].properties);
	*(unsigned short int *)s.nimages = htons(squares[i].nimages);
	*(unsigned short int *)s.delay = htons(squares[i].delay);
	*(unsigned short int *)s.filenamesz = htons(l);
	write(output,&s,sizeof(struct square_desc));
	for (j=0;j<squares[i].nimages;j++)
	 {
	   write(output,squares[i].images[j],strlen(squares[i].images[j])+1);
	 }
	/* Sanity check */
	if (squares[i].properties&START)
	  founded_start=1;
	if (squares[i].properties&FINISH)
	  founded_finish=1;
      }
     if (founded_start==0)
       printf("Warning: your level doesn't contain any square to start\n");
     if (founded_finish==0)
       printf("Warning: your level doesn't contain any square to finish\n");
   }
  /* Write the map definition */
   {
     int x,y;
     for (y=0;y<mapy;y++)
      {
	for (x=0;x<mapx;x++)
	 {
	   if (current_square<256)
	    {
	      unsigned char byte=(unsigned char)(map[(y*mapx)+x]);
	      write(output,&byte,1);
	    }
	   else
	    {
	      unsigned short int widebyte=htons((unsigned short)(map[(y*mapx)+x]));
	      write(output,&widebyte,2);
	    }
	 }
      }
   }

  free(buffer);
  return 1;
}

/*
 * Functions to parse file
 */
static void jump_space(unsigned char **ptr)
{
  char *p=*ptr;
  while (*p==' ' || *p=='\t' || *p=='\n')
    p++;
  if (*(p-1)=='\n')
    p--;
  *ptr=p;
}

static void jump_word(unsigned char **ptr)
{
  char *p=*ptr;
  while (*p!=' ' && *p!='\t' && *p!='\n')
    p++;
  *ptr=p;
}

static void jump_line(unsigned char **ptr)
{
  char *p=*ptr;
  while (*p!='\n')
    p++;
  *ptr=++p;
}

static int is_hexadecimal(const unsigned char *p)
{
  while (*p>='0' && *p<='9')
    p++;
  if ((*p>='A' && *p<='F') ||
      (*p>='a' && *p<='f'))
   {
     return 1;
   }
  return 0;
}

static void read_dataline(unsigned char **ptr, int sizex, unsigned int *dst)
{
  char *p=*ptr;

  while (*p!='\n')
   {
     /* If the number contain one non numeric char, asume is it hexadecimal */
     if (is_hexadecimal(p))
       sscanf(p,"%x",dst);
     else
       sscanf(p,"%d",dst);
     dst++;
     sizex--;
     while (*p!=' ' && *p!='\t' && *p!='\n')
       p++; 
     while (*p==' ' || *p=='\t' || *p=='\n')
       p++;
     if (*(p-1)=='\n')
       p--;
   }
  if (sizex!=0)
   {
     *p=0;
     fprintf(stderr,"Warning: data line is too %s <%s>\n",(sizex<0?"long":"short"),*ptr);
   }
  *ptr=++p;
}

/*
 * Parse une chaine formatté comme ceci:
 * CAN_MOVE_TO_EAST,CAN_GO_ABOVE arrow_right.tga,arrow_right1.tga,arrow_right2.tga
 */
static void read_datasquare(unsigned char **ptr, struct level_squares_desc *square)
{
  char *p=*ptr;
  char *begin,saved;
  int part=0;
  int flags=0;

  square->nimages=0;
  square->delay=50;
  square->images=malloc(sizeof(char *)*MAX_IMGS); /* FIXME: no more 1024 images for animation */

  while (*p!='\n')
   {
     begin=p;
     while (*p!=' ' && *p!='\t' && *p!='\n' && *p!=',')
       p++;
     saved=*p;
     *p=0;
     if (!part)
      {
	int i;
	for (i=0;square_properties_text2enum[i].text!=NULL;i++)
	 {
	   if (strcmp(begin,square_properties_text2enum[i].text)==0)
	    {
	      flags|=square_properties_text2enum[i].flags;
	      break;
	    }
	 }
	if (square_properties_text2enum[i].text==NULL)
	 {
	   if (strcmp(begin,"NONE")==0)
	     flags=0;
	   else
	    {
	      fprintf(stderr,"Warning, this capabity isn't reconize <%s>\n",begin);
	    }
	 }
      }
     else
      {
	/* Add this image to the list */
	square->images[square->nimages++]=(char *)strdup(begin);
	if (square->nimages>MAX_IMGS)
	 {
	   fprintf(stderr,"Warning, please increase MAX_IMGS (%d)\n",MAX_IMGS);
	   exit(3);
	 }
      }
     *p=saved;
     if (saved!=',')
      {
	part++;
	while (*p==' ' || *p=='\t' || *p=='\n')
	  p++;
	if (*(p-1)=='\n')
	  p--;
      }
     else
       p++;
   }
  *ptr=++p;

  square->properties=flags;
}

#if 0
static int if_yes(unsigned char **ptr, int flag)
{
  if (**ptr=='Y' || **ptr=='1' || **ptr=='J' || **ptr=='O')
   {
     jump_word(ptr);
     jump_space(ptr);
     return flag;
   }
  else if (**ptr=='N' || **ptr=='0')
   {
     jump_word(ptr);
     jump_space(ptr);
     return 0;
   }
  else
   {
     fprintf(stderr,"Warning boolean value unknow: %c\n",**ptr);
     jump_word(ptr);
     jump_space(ptr);
     return 0;
   }
}

#endif


