/*
   spacemaze - Fly a space ship through a maze and collect all the items.
   Copyright (C) 2000, 2001, 2002 John Ericson 

   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

   2000-03-25 High / John Ericson john.ericson@home.se
*/

#include "level.h"

void LevelStartDocument(level_s *level);
void LevelEndDocument(level_s *level);
void LevelCharacters(level_s *level, const xmlChar *ch, int len);
void LevelStartElement(level_s *level, const xmlChar *name, const xmlChar **attrs);
void LevelEndElement(level_s *level, const xmlChar *name);
void LevelWarning(level_s *level, const char *msg, ...);
void LevelError(level_s *level, const char *msg, ...);
void LevelFatalError(level_s *level, const char *msg, ...);

static xmlSAXHandler LevelSAXParser = {
   0, /* internalSubset */
   0, /* isStandalone */
   0, /* hasInternalSubset */
   0, /* hasExternalSubset */
   0, /* resolveEntity */
   0, /* getEntity */
   0, /* entityDecl */
   0, /* notationDecl */
   0, /* attributeDecl */
   0, /* elementDecl */
   0, /* unparsedEntityDecl */
   0, /* setDocumentLocator */
   (startDocumentSAXFunc) LevelStartDocument, /* startDocument */
   (endDocumentSAXFunc) LevelEndDocument, /* endDocument */
   (startElementSAXFunc) LevelStartElement, /* startElement */
   (endElementSAXFunc) LevelEndElement, /* endElement */
   0, /* reference */
   (charactersSAXFunc) LevelCharacters, /* characters */
   0, /* ignorableWhitespace */
   0, /* processingInstruction */
   0, /* comment */
   (warningSAXFunc) LevelWarning, /* warning */
   (errorSAXFunc) LevelError, /* error */
   (fatalErrorSAXFunc) LevelFatalError, /* fatalError */
};

static state_s state;

/* Used to buffer input from the characters callback */
static char ch_buffer[1024];
static int  ch_buffer_len;

int LoadLevel(level_s *level, const char *filename) {
   int ret = 0;
   xmlParserCtxtPtr ctxt;

   ctxt = xmlCreateFileParserCtxt(filename);
   if (ctxt == NULL) return -1;
   ctxt->sax = &LevelSAXParser;
   ctxt->userData = level;

   xmlParseDocument(ctxt);

   if (ctxt->wellFormed)
      ret = 0;
   else
      ret = -1;
   if (&LevelSAXParser != NULL)
      ctxt->sax = NULL;
   xmlFreeParserCtxt(ctxt);

   return ret;
}

void LevelStartDocument(level_s *level) {
   int i;

   /* Clean out the level variable */
   strcpy(level->name, "(Noname)");
   strcpy(level->version, "1.0");
   level->gravity     = 0;
   level->sizew       = 640;
   level->sizeh       = 480;
   level->size        = 0;
   level->maxplayers  = 1;
   level->numstartpos = 0;
   level->numitems    = 0;


   /* Clean out the level.item array */
   for (i = 0; i < MAXITEMS; i++) {
      //level->item[i].x = &level->item[i].sprite.x;
      //level->item[i].y = &level->item[i].sprite.y;
      level->item[i].type = 0;
      /*level->item[i].sprite.x = 0;
      level->item[i].sprite.y = 0;
      level->item[i].sprite.image->w = 0;
      level->item[i].sprite.image->h = 0;*/
      level->item[i].color = 2;
      level->item[i].visible = true;
      level->item[i].activate = 0;
   }

   /* Clean out the level.playerstart array */
   for (i = 0; i < MAXPLAYERS; i++) {
      level->playerstart[i].x = 0;
      level->playerstart[i].y = 0;
   }

   /* Clean out the level.landscape array */
   for (i = 0; i < MAXLEVELSIZE; i++) {
      level->landscape[i].x = 0;
      level->landscape[i].y = 0;
      level->landscape[i].w = 0;
      level->landscape[i].h = 0;
      level->landscape[i].type = 1;
      level->landscape[i].visible = true;
      level->landscape[i].crashable = true;
      level->landscape[i].color = 4;
      level->landscape[i].activate = 0;
   }
   state = START;

   /* level->numstartpos = 0; */
}

void LevelEndDocument(level_s *level) {
   /*
   Since we increase the element everytime we leave them (LevelEndElement), 
   we have to decrease all one step at the end of the document parsing to get
   the right size.
   */

   level->size--;
   level->numstartpos--;
   level->numitems--;
}

void LevelCharacters(level_s *level, const xmlChar *ch, int len) {
   /*
   An XML parser isn't guaranteed to return the entire contents of a tag in a
   characters callback therefor we use a buffer to bundle it all togehter and
   process the string in the call to end_element.
   */

   char output[40];
    int i;

   /* Copy ch to output */
   for (i = 0; (i < len) && (i < 30);i++)
      output[i] = ch[i];

   /* Put an NULL at the end */
   output[i] = 0;

   strcat(ch_buffer, output);
   ch_buffer_len += len;
}

void LevelStartElement(level_s *level, const xmlChar *name, const xmlChar **attrs) {
   int i;

   if (strcmp(name, "Level") == 0) {
      state += LEVEL;

      /* Start parsing attrib line if there is any attribs */
      /* This code could be generalized since its useful on several places */
      if (attrs != NULL)
         if (strcmp(attrs[0], "version") == 0)
            strcpy(level->version, attrs[1]);
         else
            printf("Unknown attribut %s in %s found. Ignoring.\n", attrs[1], name);
   }
   else if (strcmp(name, "Info") == 0) state += INFO;
   else if (strcmp(name, "Name") == 0) state += NAME;
   else if (strcmp(name, "Author") == 0) state += AUTHOR;
   else if (strcmp(name, "Description") == 0) state += DESCRIPTION;
   else if (strcmp(name, "Gametype") == 0) state += GAMETYPE;
   else if (strcmp(name, "Settings") == 0) state += SETTINGS;
   else if (strcmp(name, "Gravity") == 0) state += GRAVITY;
   else if (strcmp(name, "Size") == 0) state += SIZE;
   else if (strcmp(name, "W") == 0) state += W;
   else if (strcmp(name, "H") == 0) state += H;
   else if (strcmp(name, "Items") == 0) state += ITEMS;
   else if (strcmp(name, "PlayerStart") == 0) {
      state += PLAYERSTART;
      level->numstartpos++;

      level->item[level->numitems].type = LEVEL_OBJ_STARTPOSPLAYER1;
   }
   else if (strcmp(name, "LandingPlate") == 0) {
      state += LANDINGPLATE;

      level->item[level->numitems].type = LEVEL_OBJ_LANDINGPLATE;
   }
   else if (strcmp(name, "X") == 0) state += X;
   else if (strcmp(name, "Y") == 0) state += Y;
   else if (strcmp(name, "Box") == 0) {
      state += BOX;

      level->item[level->numitems].type = LEVEL_OBJ_BOX;

      /* Start parsing attrib line if there is any attribs */
      if (attrs != NULL)
         for (i = 0; attrs[i] != NULL; i+=2)
            if (strcmp(attrs[i], "color") == 0)
               level->item[level->numitems].color = strtol(attrs[i+1], NULL, 0);
            else if (strcmp(attrs[i], "activate") == 0)
               level->item[level->numitems].activate = strtol(attrs[i+1], NULL, 0);
            else
               printf("Unknown attribut %s in %s found. Ignoring.\n", attrs[i], name);
   }
   else if (strcmp(name, "Landscape") == 0) state += LANDSCAPE;
   else if (strcmp(name, "Rectangle") == 0) {
      state += RECTANGLE;

      /* Start parsing attrib line if there is any attribs */
      if (attrs != NULL)
         for (i = 0; attrs[i] != NULL; i+=2)
            if (strcmp(attrs[i], "color") == 0)
               level->landscape[level->size].color = strtol(attrs[i+1], NULL, 0);
            else if (strcmp(attrs[i], "visible") == 0)
               level->landscape[level->size].visible = strtob((char *)attrs[i+1]);
            else if (strcmp(attrs[i], "crashable") == 0)
               level->landscape[level->size].crashable = strtob((char *)attrs[i+1]);
            else if (strcmp(attrs[i], "activate") == 0)
               level->landscape[level->size].activate = strtol(attrs[i+1], NULL, 0);
            else
               printf("Unknown attribut %s in %s found. Ignoring.\n", attrs[i], name);
   }
   else
      printf("Unknown Element <%s> found. Ignoring.\n", name);
}

void LevelEndElement(level_s *level, const xmlChar *name) {

   char *output;

   ch_buffer[ch_buffer_len] = 0;

   output = trim(ch_buffer);
   strcpy(ch_buffer, output);


   switch (state) {
   case (LEVEL + INFO + NAME):
      strcpy(level->name, ch_buffer);
      break;
   case (LEVEL + SETTINGS + GRAVITY):
      /* Why cant I use strtof? */
      level->gravity = (float)strtod(ch_buffer, NULL);
      break;
   case (LEVEL + SETTINGS + SIZE + W):
      level->sizew = strtol(ch_buffer, NULL, 0);
      break;
   case (LEVEL + SETTINGS + SIZE + H):
      level->sizeh = strtol(ch_buffer, NULL, 0);
      break;
   case (LEVEL + ITEMS + PLAYERSTART + X):
      /* level->numstartpos gets crap */
      level->playerstart[level->numstartpos].x = strtol(ch_buffer, NULL, 0);
      break;
   case (LEVEL + ITEMS + PLAYERSTART + Y):
      level->playerstart[level->numstartpos].y = strtol(ch_buffer, NULL, 0);
      break;
   case (LEVEL + ITEMS + LANDINGPLATE + X):
      level->item[level->numitems].x = strtol(ch_buffer, NULL, 0);
      break;
   case (LEVEL + ITEMS + LANDINGPLATE + Y):
      level->item[level->numitems].y = strtol(ch_buffer, NULL, 0);
      break;
   case (LEVEL + ITEMS + BOX + X):
      level->item[level->numitems].x = strtol(ch_buffer, NULL, 0);
      break;
   case (LEVEL + ITEMS + BOX + Y):
      level->item[level->numitems].y = strtol(ch_buffer, NULL, 0);
      break;
   case (LEVEL + LANDSCAPE + RECTANGLE + X):
      level->landscape[level->size].x = strtol(ch_buffer, NULL, 0);
      break;
   case (LEVEL + LANDSCAPE + RECTANGLE + Y):
      level->landscape[level->size].y = strtol(ch_buffer, NULL, 0);
      break;
   case (LEVEL + LANDSCAPE + RECTANGLE + W):
      level->landscape[level->size].w = strtol(ch_buffer, NULL, 0);
      break;
   case (LEVEL + LANDSCAPE + RECTANGLE + H):
      level->landscape[level->size].h = strtol(ch_buffer, NULL, 0);
      break;
   case (LEVEL + INFO + AUTHOR):
   case (LEVEL + INFO + DESCRIPTION):
   case (LEVEL + INFO + GAMETYPE):
      /* Info we dont use at the moment */
      break;
   }

   /* Reset characters buffer */
   ch_buffer[0] = 0;
   ch_buffer_len = 0;


   if (strcmp(name, "Level") == 0) state -= LEVEL;
   else if (strcmp(name, "Info") == 0) state -= INFO;
   else if (strcmp(name, "Name") == 0) state -= NAME;
   else if (strcmp(name, "Author") == 0) state -= AUTHOR;
   else if (strcmp(name, "Description") == 0) state -= DESCRIPTION;
   else if (strcmp(name, "Gametype") == 0) state -= GAMETYPE;
   else if (strcmp(name, "Settings") == 0) state -= SETTINGS;
   else if (strcmp(name, "Gravity") == 0) state -= GRAVITY;
   else if (strcmp(name, "Size") == 0) state -= SIZE;
   else if (strcmp(name, "W") == 0) state -= W;
   else if (strcmp(name, "H") == 0) state -= H;
   else if (strcmp(name, "Items") == 0) state -= ITEMS;
   else if (strcmp(name, "PlayerStart") == 0) {
      state -= PLAYERSTART;
      level->numstartpos++;
      level->numitems++;
   }
   else if (strcmp(name, "LandingPlate") == 0) {
      state -= LANDINGPLATE;
      level->numitems++;
   }
   else if (strcmp(name, "X") == 0) state -= X;
   else if (strcmp(name, "Y") == 0) state -= Y;
   else if (strcmp(name, "Box") == 0) {
      state -= BOX;
      level->numitems++;
   }
   else if (strcmp(name, "Landscape") == 0) state -= LANDSCAPE;
   else if (strcmp(name, "Rectangle") == 0) {
      state -= RECTANGLE;
      level->size++;
   }

}

void LevelWarning(level_s *level, const char *msg, ...) {
   va_list args;
   char buffer[255];

   strcpy(buffer, "Loading level warning: ");

   va_start(args, msg);
   vsprintf(&buffer[strlen(buffer)], msg, args);

   printf("%s", buffer);
   va_end(args);
}

void LevelError(level_s *level, const char *msg, ...) {
   va_list args;
   char buffer[255];

   strcpy(buffer, "Loading level error: ");

   va_start(args, msg);
   vsprintf(&buffer[strlen(buffer)], msg, args);

   printf("%s", buffer);
   va_end(args);
}

void LevelFatalError(level_s *level, const char *msg, ...) {
   va_list args;
   char buffer[255];

   strcpy(buffer, "Loading level fatal error: ");

   va_start(args, msg);
   vsprintf(&buffer[strlen(buffer)], msg, args);

   printf("%s", buffer);
   va_end(args);
}