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

#include "config.h"

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

#ifdef SOUND_ON
#include "SDL/SDL_mixer.h"
#endif

#include "enum.h"

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

#include "extern.h"

//
// graphics primitives
//

SDL_Surface* loadImage(char* filename)
{
  SDL_Surface* loadedImage = NULL;
  SDL_Surface* optimizedImage = NULL;
  loadedImage = IMG_Load(filename);
  if (loadedImage != NULL)
  {
    optimizedImage = SDL_DisplayFormat(loadedImage);
    SDL_FreeSurface(loadedImage);
  }

  return optimizedImage;
}

void applySurface(int x, int y, SDL_Surface* source, SDL_Surface* destination, SDL_Rect* clip)
{
  SDL_Rect offset;
  offset.x = x;
  offset.y = y;

  SDL_BlitSurface(source, clip, destination, &offset);
}

//
// sound
//

#ifdef SOUND_ON

Mix_Music* loadBGM(char* filename)
{
  Mix_Music* bgmTrack = Mix_LoadMUS(filename);
  hasMusic = 1;
  return bgmTrack;
}

Mix_Chunk* loadSFX(char* filename)
{
  Mix_Chunk* sfxClip = Mix_LoadWAV(filename);
  return sfxClip;
}

#endif

//
// world
//

void renderBackground()
{
    applySurface(0, 0, rightHere->bgImage, screen, NULL);
    animate(rightHere);
}

void renderForeground()
{
  drawFgObjects1(rightHere);
  drawPeople(rightHere);
  drawPlayer(hero);
  drawFgObjects2(rightHere);
}

void interact()
{
  while(SDL_PollEvent(&event))
  {
    switch (event.type)
    {
      case SDL_KEYDOWN:
        switch (event.key.keysym.sym)
        {
          case DPAD_UP:
            hero->bearing.y = -2;
            break;
          case DPAD_LEFT:
            hero->bearing.x = -2;
            break;
          case DPAD_DOWN:
            hero->bearing.y = 2;
            break;
          case DPAD_RIGHT:
            hero->bearing.x = 2;
            break;
          case A_BUTTON:
            actionbutton = 1;
            break;
          case B_BUTTON:
            spellbutton = 1;
            break;
          case PAUSE_BUTTON:
            pausemenu();
            break;
          case FS_BUTTON:
            toggleFullscreen();
            break;
          default: break;
        }
        break;
      case SDL_KEYUP:
        switch( event.key.keysym.sym )
        {
          case DPAD_UP:
            if (hero->bearing.y < 0)
            {
              hero->bearing.y = 0;
            }
            break;
          case DPAD_LEFT:
            if (hero->bearing.x < 0)
            {
              hero->bearing.x = 0 ;
            }
            break;
          case DPAD_DOWN:
            if (hero->bearing.y > 0)
            {
              hero->bearing.y = 0;
            }
            break;
          case DPAD_RIGHT:
            if (hero->bearing.x > 0)
            {
              hero->bearing.x = 0;
            }
            break;
          case A_BUTTON:
            actionbutton = 0;
            break;
          case B_BUTTON:
            spellbutton = 0;
          default: break;
        }
        break;
      case SDL_QUIT: quit = 1; playing = 0; break;
    }
  }
}

void kListen(int* whichKaos)
{
  if (spellbutton)
  {
    spellFlag = 0;
    run(spellBook[0]);
    synergize();
    spellFlag = -1;
  }
  if (*whichKaos >= 0)
  {
    run(rightHere->eventTriggers[*whichKaos]);
    *whichKaos = -1;
  }
}

//
// system
//

int init(int argc, char* args[])
{
  printf("Initializing SDL\n");

#ifdef SOUND_ON
  if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_AUDIO) == -1) return 0;
  printf("Initialized\nOpening soundsystem\n");
  if (Mix_OpenAudio(22050,MIX_DEFAULT_FORMAT, 2, 4096) == -1) return 0;
  printf("Soundsystem open\nCreating window\n");

#else

  if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER) == -1) return 0;
  printf("Initialized\nCreating window\n");

#endif

  if (argc >= 2)
  {
    if (strcmp(args[1], "-w"))
    {
      screen = SDL_SetVideoMode( 320, 180, 32, SDL_FULLSCREEN|SDL_HWSURFACE);
      SDL_ShowCursor(0);
      fullscreen = 1;
    }
    else screen = SDL_SetVideoMode( 320, 180, 32, SDL_HWSURFACE|SDL_RESIZABLE);
  }
  else
  {
    screen = SDL_SetVideoMode( 320, 180, 32, SDL_FULLSCREEN|SDL_HWSURFACE);
    SDL_ShowCursor(0);
    fullscreen = 1;
  }
  if (screen == NULL) return 0;
  SDL_WM_SetCaption("Kaos Mage's Infinite Sidequest", NULL);
  printf("Window created\nInitializing fonts\n");
  if (TTF_Init() == -1) return 0;
  printf("Fonts initialized\nInitializing data\n");


  menuBG = newRoom("assets/img/backgrounds/mainmenu.png", 1);

  font = TTF_OpenFont("assets/charriot.ttf", 10);

  saveMenu = loadImage("assets/img/windows/saveMenu.gif");
  textBoxBG = loadImage("assets/img/windows/textBox.gif");
  choiceBox = loadImage("assets/img/windows/choiceBox.gif");
  nextArrow = loadImage("assets/img/windows/nextButton.gif");
  selectArrow = loadImage("assets/img/windows/selectArrow.gif");
  loadingTxt = TTF_RenderText_Solid(font, "loading map data...", textColor);
  hero = newPlayer("assets/img/characters/kmage.png", 160, 90);


  mapData = malloc(64*sizeof(Room*));
  mapBuffer= malloc(64*sizeof(Room*));
  dialogueData = malloc(124*sizeof(Room*));

#ifdef SOUND_ON
  bgmData = malloc(8*sizeof(Mix_Music*));
  sfxData = malloc(24*sizeof(Mix_Chunk*));
#endif

  kaosData = malloc(124*sizeof(Kaos*));
  theatre = malloc(8*sizeof(Scene*));

  writeSpellBook();

  printf("Init complete\n");
  return 1;
}

void writeSpellBook()
{
  HyperKaos* testSpell = newHyperKaos(1,0,0,0,0,0);
  Kaos* stopPlayer = newManip(hero, 0,0);
  Kaos* beam = newSpell_Beam();
  addKaos(testSpell, stopPlayer);
  addKaos(testSpell, beam);

  spellBook = malloc(4*sizeof(HyperKaos**));
  spellBook[0] = testSpell;
}

void burnSpellBook()
{
  cleanHyperKaos(spellBook[0]);
  deleteHyperKaos(spellBook[0]);
  free(spellBook);
}

void toggleFullscreen()
{
  if (!fullscreen)
  {
    SDL_SetVideoMode(320, 180, 32, SDL_FULLSCREEN|SDL_HWSURFACE);
    SDL_ShowCursor(0);
    fullscreen = 1;
  }
  else
  {
    SDL_SetVideoMode(320, 180, 32, SDL_HWSURFACE|SDL_RESIZABLE);
    SDL_ShowCursor(1);
    fullscreen = 0;
  }
}

void timeDilation()
{
  int i = getTicks(fps);

  if (i < 1000 / 30)
    SDL_Delay(1000/30 - i);
}

void cleanup()
{
  if(fullscreen)
    SDL_ShowCursor(1);

  unloadData(thisChunk);

  printf("Freeing global data\n");

  killPlayer(hero);
  deleteRoom(menuBG);

  free(mapData);
  free(kaosData);
  free(dialogueData);


#ifdef SOUND_ON
  free(bgmData);
  free(sfxData);
#endif

  free(theatre);

  burnSpellBook();

  SDL_FreeSurface(textBoxBG);
  SDL_FreeSurface(nextArrow);
  SDL_FreeSurface(saveMenu);
  SDL_FreeSurface(selectArrow);
  SDL_FreeSurface(loadingTxt);
  SDL_FreeSurface(screen);

  printf("Closing SDL\n");

#ifdef SOUND_ON
  Mix_CloseAudio();
#endif

  TTF_CloseFont(font);
  TTF_Quit();
  SDL_Quit();

  printf("Cleanup complete\n");
}

void intro()
{
  SDL_Color black = {0,0,0};

  SLayer* nebula = newSLayer(loadImage("assets/img/backgrounds/presents.png"), 0,0,0,0,255);
  SLayer* fogF = newSLayer(loadImage("assets/img/fx/fog.png"), 0,0,-1,0,128);
  SLayer* fogB = newSLayer(loadImage("assets/img/fx/plasma.png"), -320,0,1,0,56);
  SLayer* presents = newSLayer(TTF_RenderText_Solid(font, "nilFM presents", textColor), 120,84,0,0,128);

  SLayer* menuTransition = newSLayer(loadImage("assets/img/backgrounds/mainmenu.png"),0,0,0,0,255);

  Scene* intro = newScene(30,30, 200, black, black);
  Scene* transition = newScene(30,0,30, black, black);

  buildScene(intro, nebula);
  buildScene(intro, fogB);
  buildScene(intro, fogF);
  buildScene(intro, presents);

  buildScene(transition, menuTransition);

  playScene(intro);
  playScene(transition);

  deleteScene(intro);
  deleteScene(transition);
}

void mainmenu()
{
  int menucounter = 0;
  int select = 0;
  FILE* saveData = fopen("sram", "r");

  int level = 1;
  int roomNo = 0;
  int xPos = 180;
  int yPos = 90;
  long long int eventState = 2;
  warpto(menuBG);

#ifdef SOUND_ON
  if (menuBGM == NULL)
    Mix_HaltMusic();
  else
    Mix_PlayMusic(menuBGM, -1);
#endif

  while (!select)
  {
    timeStart(fps);
    applySurface(0, 0, rightHere->bgImage, screen, NULL);
    animate(rightHere);

    while(SDL_PollEvent(&event))
    {
      switch (event.type)
      {
        case SDL_KEYDOWN:
          switch (event.key.keysym.sym)
          {
            case DPAD_UP:
              menucounter--;
              break;
            case DPAD_DOWN:
              menucounter++;
              break;

            case A_BUTTON:
              select = 1;
              break;
            case FS_BUTTON:
              toggleFullscreen();
              break;
            default: break;
          }
          break;
        case SDL_QUIT: quit = 1; playing = 0; select = 1; break;
      }
      if (menucounter == 3) menucounter = 0;
      if (menucounter == -1) menucounter = 2;
    }
    switch (menucounter)
    {
      case 0:
        applySurface(230, 61, selectArrow, screen, NULL);
        break;
      case 1:
        applySurface(230, 83, selectArrow, screen, NULL);
        break;
      case 2:
        applySurface(230, 105, selectArrow, screen, NULL);
    }
    SDL_Flip(screen);
    timeDilation();
  }
  switch (menucounter)
  {
    case 0:
      thisChunk = LEVEL1;
      nextChunk = LEVEL1;

      savestate = eventState;
      bufferData(LEVEL1);
      pushBufferData();

      warpto(mapData[0]);
      hero->point.x = 160;
      hero->point.y = 90;

      playing = 1;
      fclose(saveData);
      break;

    case 1:
      if (saveData)
      {
        fscanf(saveData, "%d %d %d %d %lld", &level, &roomNo, &xPos, &yPos, &eventState);
      }
      thisChunk = (enum dataChunks)(level);
      nextChunk = (enum dataChunks)(level);

      savestate = eventState;
      bufferData((enum dataChunks)(level));
      pushBufferData();

      hero->point.x = xPos;
      hero->point.y = yPos;
      warpto(mapData[roomNo]);

      playing = 1;
      fclose(saveData);
      printf("Loaded save: level %d, room %d (%d,%d) state: %lld\n", level, roomNo, xPos, yPos, eventState);
      break;

    case 2:
       quit = 1;
       playing = 0;
       fclose(saveData);
  }
}

void pausemenu()
{
  int paused = 1;
  int menucounter = 0;
  int select = 0;
  int roomNo;
  int i;
  FILE* saveData;
  hero->bearing.x = 0;
  hero->bearing.y = 0;

#ifdef SOUND_ON
  Mix_VolumeMusic(MIX_MAX_VOLUME/3);
#endif

  for (i = 0; i < 64; i++)
  {
    if (rightHere == mapData[i])
    {
      roomNo = i;
      break;
    }
  }
  while (paused)
  {
    timeStart(fps);
    applySurface(60, 45, saveMenu ,screen, NULL);
    while(SDL_PollEvent(&event))
    {
      switch (event.type)
      {
        case SDL_KEYDOWN:
          switch (event.key.keysym.sym)
          {
            case DPAD_UP:
              menucounter--;
              break;
            case DPAD_DOWN:
              menucounter++;
              break;
            case A_BUTTON:
              select = 1;
              break;
            case PAUSE_BUTTON:
              paused = 0;
              break;
            case FS_BUTTON:
              toggleFullscreen();
              break;
            default: break;
          }
          break;
        case SDL_QUIT: quit = 1; paused = 0; playing = 0; break;
      }
      if (menucounter == 3) menucounter = 0;
      if (menucounter == -1) menucounter = 2;
    }

    switch (menucounter)
    {
      case 0:
        applySurface(93, 60, selectArrow, screen, NULL);
        break;
      case 1:
        applySurface(93, 82, selectArrow, screen, NULL);
        break;
      case 2:
        applySurface(93, 104, selectArrow, screen, NULL);
    }

    if (select)
    {
      switch (menucounter)
      {
        case 0:
        {
          printf("Saving data: level %d, room %d (%d,%d) state: %lld\n", thisChunk, roomNo, hero->point.x, hero->point.y, savestate);
          saveData = fopen("sram", "w");
          fprintf(saveData, "%d %d %d %d %lld", (int)(thisChunk), roomNo, hero->point.x, hero->point.y, savestate);
          paused = 0;
          fclose(saveData);
          break;
        }
        case 1:
        {
          printf("Saving data: level %d, room %d (%d,%d) state: %lld\n", thisChunk, roomNo, hero->point.x, hero->point.y, savestate);
          saveData = fopen("sram", "w");
          fprintf(saveData, "%d %d %d %d %lld", (int)(thisChunk), roomNo, hero->point.x, hero->point.y, savestate);
          paused = 0;
          playing = 0;
          fclose(saveData);
          break;
        }
        case 2:
          paused = 0;
          playing = 0;
          break;
      }
    }
    SDL_Flip(screen);
    timeDilation();
  }

#ifdef SOUND_ON
  Mix_VolumeMusic(MIX_MAX_VOLUME);
#endif

}
