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

#include "config.h"

#include "SDL/SDL.h"
#include "SDL/SDL_image.h"
#include "SDL/SDL_ttf.h"
#include "SDL/SDL_mixer.h"

#include "enum.h"

#include "Engine.h"
#include "Player.h"
#include "Room.h"
#include "Kaos.h"
#include "HyperKaos.h"
#include "TextBox.h"
#include "Scene.h"
#include "Synergy.h"
#include "WorldData.h"

typedef struct timer Timer;
#include "extern.h"

void bufferData(enum dataChunks chunk)
{
  hasMusic = 0;
  applySurface(8,8, loadingTxt, screen, NULL);
  frameAdvance();
  printf("Loading map chunk\n");
  if(!worldBuilder(chunk))
  {
    quit = 1; playing = 0;
    printf("Fatal map error. Quitting\n");
    return;
  };
  printf("Loaded map chunk\n");
}

int hashCmd(char* x)
{
  int hash = 7951;
  int coef = 181;
  while (*x)
  {
    hash += coef*(*x);
    x++;
  }
  hash %= 256;
  return hash;
}

int worldBuilder(enum dataChunks chunk)
{
  char datafile[256];
  char lineBuffer[1024];
  char cmdBuffer[24];
  char cchunk[4];
  char propsBuffer[998];
  int savequery;
  int conditional = 0;
  int (*fp)(char* props);
  FILE* worldInfo;

  sprintf(cchunk, "%d", chunk);

  strcpy(datafile, "mapdata/");
  strcat(datafile, cchunk);
  strcat(datafile, ".txt");

  worldInfo = fopen(datafile, "r");

  if (!worldInfo)
  {
    printf("Couldn't read world data\n");
    return 1;
  }

  while (fgets(lineBuffer, 1024, worldInfo))
  {
    if (!strcmp(lineBuffer,"\n") || !strcmp(lineBuffer,"\r\n")) continue;
    if (lineBuffer[0] == '<')
    {
      conditional = 0;
      continue;
    }
    if (!conditional)
    {
      if (sscanf(lineBuffer, "%[^:]: %[^\t\n]", cmdBuffer, propsBuffer) < 2)
      {
        printf("World data line malformed\n");
        return 0;
      }
      else
      {
        switch (hashCmd(cmdBuffer))
        {
          case 106: //if
            if ((sscanf(propsBuffer, "%d", &savequery) == 1) && notCompleted(savequery))
            {
              conditional = 1;
              continue;
            }
            break;
          case 15: //ifNot
            if ((sscanf(propsBuffer, "%d", &savequery) == 1) && hasCompleted(savequery))
            {
              conditional = 1;
              continue;
            }
            break;
          case 200: //mkRoom
            fp = &buildRoom;
            break;
          case 45: //mkKaos
            fp = &buildKaos;
            break;
          case 109: //mkTextBox
            fp = &buildTextBox;
            break;
          case 181: //addText
            fp = &modTextBox;
            break;
          case 229: //loadFX
            fp = &buildSFX;
            break;
          case 125: //loadBGM
            fp = &buildBGM;
            break;
          case 100: //addSigil
            fp = &buildSynergy;
            break;
          case 47: //addPerson
            fp = &buildPerson;
            break;
          case 88: //addObstruction
            fp = &buildObstruction;
            break;
          case 141: //addImg
            fp = &buildFGImage;
            break;
          case 240: //addTrigger
            fp = &buildHyper;
            break;
          case 148: //chainKaos
            fp = &chainKaos;
            break;
          case 238: //addWarp
            fp = &buildWarp;
            break;
      }
      if (!fp(propsBuffer))
        return 0;
    }
  }}
  fclose(worldInfo);
  return 1;
}

int buildBGM(char* props)
{
  #ifdef SOUND_ON
  int slot;
  char filename[256];

  if (sscanf(props, "slot %u, file %[^\t\n]", &slot, filename) != 2)
    return 0;

  bgmData[slot] = loadBGM(filename);
  #endif
  return 1;
}

int buildSFX(char* props)
{
  #ifdef SOUND_ON
  int slot;
  char filename[256];

  if (sscanf(props, "slot %u, file %[^\t\n]", &slot, filename) != 2)
    return 0;

  sfxData[slot] = loadSFX(filename);
  #endif
  return 1;
}

int buildRoom(char* props)
{
  int slot;
  char filename[256];
  int breathe;

  if (sscanf(props, "slot %u, sprite %[^,], spd %u", &slot, filename, &breathe) != 3)
    return 0;
  printf("Building room in map slot %d: bg = %s; spd = %d\n", slot, filename, breathe);
  mapBuffer[slot] = newRoom(filename, breathe);
  return 1;
}

int buildTextBox(char* props)
{
  int slot;
  char portrait[256];

  if (sscanf(props, "slot %u, portrait %[^\n]",
              &slot, portrait) != 2)
    return 0;
  printf("building textbox...\n");
  if (strcmp(portrait, "none") != 0)
    dialogueData[slot] = newTextBox();
  else
    dialogueData[slot] = newGTextBox(loadImage(portrait));
  return 1;
}

int modTextBox(char* props)
{
  int slot;
  char textData[64];

  if (sscanf(props, "slot %u, %[^\n]", &slot, textData) != 2)
    return 0;

  printf("adding line of text to textbox...\n");
  addText(dialogueData[slot], textData);
  return 1;
}

int buildSynergy(char* props)
{
  int rm, x, y, w, h;
  char d;
  int spell, effect;
  if (sscanf(props, "room %u, x %d, y %d, w %u, h %u, spell %u, dir %c, effect %d",
               &rm, &x, &y, &w, &h, &spell, &d, &effect) != 8)
    return 0;

  printf("building synergy...\n");
  addSigil(mapBuffer[rm], newSynergy(spell, d, x, y, w, h, mapBuffer[rm]->eventTriggers[effect]));
  return 1;
}

int buildKaos(char* props)
{
  int slot;
  char kType, kProps[990];
  Kaos* (*fp)(char* args);
  fp = NULL;
  if (sscanf(props, "slot %u, class %c, %[^\n]",
              &slot, &kType, kProps) != 3)
    return 0;
  printf("building kaos...\n");
  switch (kType)
  {
    case 'C':
      fp = &newConversation;
      break;
    case 'W':
      fp = &newWait;
      break;
    case 'M':
      fp = &newManip;
      break;
    case 'L':
      fp = &newLook;
      break;
    case 'F':
      fp = &newFaceEachother;
      break;
    case 'Y':
      fp = &newChoice;
      break;
    case 'S':
#ifdef SOUND_ON
      fp = &newPlaySound;
      break;
#endif
    case 'T':
      fp = &newTeleport;
      break;
    case 'E':
      fp = &newErase;
      break;
  }
  if (fp && !(kaosData[slot] = fp(kProps)))
  {
    printf("Malformed args to Kaos or no memory!");
    return 0;
  }
  return 1;
}

int buildObstruction(char* props)
{
  int rm, x, y, w, h;

  if (sscanf(props, "room %u, x %d, y %d, w %u, h %u",
              &rm, &x, &y, &w, &h) != 5)
    return 0;

  printf("Building obstacle...\n");
  addObstacle(mapBuffer[rm], x, y, w, h);
  return 1;
}

int buildFGImage(char* props)
{
  int rm, x, y, w, h, f, d, a;
  char filename[256];
  if (sscanf(props, "room %u, x %d, y %d, w %u, h %u, sprite %[^,], frames %u, dual %u, alpha %u",
              &rm, &x, &y, &w, &h, filename, &f, &d, &a) !=9)
    return 0;

  printf("Building FG Image...\n");
  addFgObj(mapBuffer[rm], x, y, w, h, filename, f, d, a);
  return 1;
}

int buildWarp(char* props)
{
  int r, x, y, w, h, dC, dR, dX, dY;
  if (sscanf(props, "room %u, x %d, y %d, w %u, h %u, dest %u,%u, dX %d, dY %d",
              &r, &x, &y, &w, &h, &dC, &dR, &dX, &dY) != 9)
    return 0;

  printf("Building warp...\n");
  addWarp(mapBuffer[r], x, y, w, h, dC, dR, dX, dY);
  return 1;
}

int buildHyper(char* props)
{
  int rm, id, tType, x, y, w, h;
  if (sscanf(props, "room %u, id %u, type %u, x %d, y %d, w %u, h %u",
              &rm, &id, &tType, &x, &y, &w, &h) != 7)
    return 0;

  printf("Building hyperkaos...\n");
  HyperKaos* temp = newHyperKaos(id, tType, x, y, w, h);
  addTrigger(mapBuffer[rm], temp);
  return 1;
}

int chainKaos(char* props)
{
  int rm, trig, k;
  if (sscanf(props, "room %u, trigger %u, kaos %u",
              &rm, &trig, &k) != 3)
    return 0;

  printf("Chaining kaos...\n");
  addKaos(mapBuffer[rm]->eventTriggers[trig], kaosData[k]);
  return 1;
}

int buildPerson(char* props)
{
  int rm, x, y;
  char filename[256];

  if (sscanf(props, "room %u, sprite %[^,], x %d, y%d",
              &rm, filename, &x, &y) != 4)
    return 0;

  printf("Building person...\n");
  Player* temp = newPlayer(filename, x, y);
  addPerson(mapBuffer[rm], temp);
  return 1;
}

int* countMapThings(int* count, enum dataChunks chunk)
{
  char datafile[256];
  char lineBuffer[1024];
  char cmdBuffer[24];
  char propsBuffer[998];
  char cchunk[4];

  FILE* worldInfo;

  sprintf(cchunk, "%d", chunk);

  strcpy(datafile, "mapdata/");
  strcat(datafile, cchunk);
  strcat(datafile, ".txt");

  worldInfo = fopen(datafile, "r");
  while (fgets(lineBuffer, 1024, worldInfo))
  {
    sscanf(lineBuffer, "%[^:]: %[^\t\n]", cmdBuffer, propsBuffer);
    switch(hashCmd(cmdBuffer))
    {
      case 200:
        count[0]++;
        break;
      case 45:
        count[1]++;
        break;
      case 109:
        count[2]++;
        break;
      case 229:
        count[3]++;
        break;
      case 125:
        count[4]++;
        break;
    }
  }
  return count;
}

void dataPurge(int* objs)
{
  int i;
  for (i = 0; i < objs[0]; i++)
    deleteRoom(mapData[i]);
  for (i = 0; i < objs[2]; i++)
    deleteTextBox(dialogueData[i]);
  for (i = 0; i < objs[1]; i++)
    kaosData[i]->destroy(kaosData[i]);

#ifdef SOUND_ON
  for (i = 0; i < objs[3]; i++)
    Mix_FreeMusic(bgmData[i]);
  for (i = 0; i < objs[4]; i++)
    Mix_FreeChunk(sfxData[i]);
#endif

}

void unloadData(enum dataChunks chunk)
{
  int thingsCount[5] = {0,0,0,0,0};
  printf("Unloading old map chunk\n");
  dataPurge(countMapThings(thingsCount, chunk));
  printf("Unloaded old map chunk\n");
}

void pushBufferData()
{
  printf("Pushing map buffer\n");
  mapData = mapBuffer;
  mapBuffer = (Room**)malloc(64*sizeof(Room*));

#ifdef SOUND_ON
  if (hasMusic)
    Mix_PlayMusic(bgmData[0], -1);
#endif

  printf("Map buffer clean\n");
}

void pager()
{
  if (thisChunk != nextChunk)
  {

#ifdef SOUND_ON
    if (Mix_PlayingMusic())
    {
      Mix_HaltMusic();
    }
#endif

    unloadData(thisChunk);
    pushBufferData();
    thisChunk=nextChunk;
  }
}