#include <stdio.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 "HyperKaos.h"
#include "Synergy.h"

typedef struct timer Timer;
typedef struct textBox TextBox;
typedef struct scene Scene;
#include "extern.h"

Obstruction* newObstruction(int x, int y, int w, int h)
{
  Obstruction* self = malloc(sizeof(Obstruction));
  self->domain.x = x;
  self->domain.y = y;
  self->domain.w = w;
  self->domain.h = h;
  self->tombStone = 0;

  return self;
}

void deleteObstruction(Obstruction* target)
{
  free(target);
}

FGImage* newFGImage(int x, int y, int w, int h, int f, int dual, char* filename, int alpha)
{
  FGImage* self = malloc(sizeof(FGImage));

  self->location.x = x;
  self->location.y = y;
  self->location.w = w;
  self->location.h = h;

  self->frames = f;
  self->frameNow = 0;
  self->dualLayer = dual;
  self->tombStone = 0;

  self->spriteSheet = loadImage(filename);
  if (alpha != 255)
    SDL_SetAlpha(self->spriteSheet, SDL_SRCALPHA|SDL_RLEACCEL, alpha);

  return self;
}

void deleteFGImage(FGImage* target)
{
  SDL_FreeSurface(target->spriteSheet);
  free(target);
}

WarpZone* newWarpZone(int x, int y, int w, int h, enum dataChunks chunk, int dest, int dX, int dY)
{
  WarpZone* self = malloc(sizeof(WarpZone));

  self->location.x = x;
  self->location.y = y;
  self->location.w = w;
  self->location.h = h;

  self->chunk = chunk;
  self->destination = dest;
  self->x = dX;
  self->y = dY;
  self->tombStone = 0;

  return self;
}

void deleteWarpZone(WarpZone* target)
{
  free(target);
}

Room* newRoom(char* filename, int a)
{
  Room* self = (Room*)malloc(sizeof(Room));

  self->obstacle = malloc(4*sizeof(Obstruction*));
  self->numberOfObstacles = 0;
  self->maxNumberOfObstacles = 4;

  self->fgObject = malloc(4*sizeof(FGImage*));
  self->numberOfObj = 0;
  self->maxNumberOfObj = 4;
  self->objSpeed = a;
  self->objIterator = 0;

  self->warps = malloc(4*sizeof(WarpZone*));
  self->numberOfWarps = 0;
  self->maxNumberOfWarps = 4;

  self->eventTriggers = malloc(4*sizeof(HyperKaos*));
  self->numberOfTriggers = 0;
  self->maxNumberOfTriggers =4;

  self->people = malloc(4*sizeof(Player*));
  self->numberOfPeople = 0;
  self->maxNumberOfPeople = 4;

  self->sigils = malloc(4*sizeof(Synergy*));
  self->numberOfSigils = 0;
  self->maxNumberOfSigils = 4;

  self->clip.x=0;
  self->clip.y=0;
  self->clip.w=320;
  self->clip.h=180;
  self->spriteSheet = loadImage(filename);
  self->frameNo = 0;

  return self;
}

void deleteRoom(Room* target)
{
  int i;
  for (i = 0; i < target->numberOfObstacles; i++)
    deleteObstruction(target->obstacle[i]);
  free(target->obstacle);
  target->obstacle = NULL;

  for (i = 0; i < target->numberOfObj; i++)
    deleteFGImage(target->fgObject[i]);
  free(target->fgObject);
  target->fgObject=NULL;

  for (i = 0; i < target->numberOfWarps; i++)
    deleteWarpZone(target->warps[i]);
  free(target->warps);
  target->warps = NULL;

  for (i = 0; i < target->numberOfTriggers; i++)
    deleteHyperKaos(target->eventTriggers[i]);
  free(target->eventTriggers);
  target->eventTriggers = NULL;

  for (i = 0; i < target->numberOfPeople; i++)
    killPlayer(target->people[i]);
  free(target->people);
  target->people=NULL;

  for (i = 0; i < target->numberOfSigils; i++)
    deleteSynergy(target->sigils[i]);
  free(target->sigils);
  target->sigils = NULL;

  SDL_FreeSurface(target->spriteSheet);
  target->spriteSheet = NULL;

  free(target);
}

//
// room bg graphics
//

void animate(Room* self)
{

  if (self->frameNo == 0)
  {
    self->clip.y = 180;
  }
  if (self->frameNo == 10)
  {
    self->clip.y = 360;
  }
  if (self->frameNo == 20)
  {
    self->clip.y = 540;
  }
  if (self->frameNo == 30)
  {
    self->clip.y = 360;
  }
  if (self->frameNo == 40)
  {
    self->clip.y = 180;
  }
  if (self->frameNo == 50)
  {
    self->clip.y = 0;
  }
  self->frameNo++;
  if (self->frameNo == 60) self->frameNo = 0;
}

//
// collision detection
//

int checkCollision(Room* self, Player* player, Obstruction** box)
{
  int i;

  for (i = 1; i <= self->numberOfObstacles; i++)
  {
    if (!((*box)->tombStone) && playerIsInRect(player, &(*box)->domain))
    {
      return 1;
    }
    box++;
  }
  return 0;
}

int checkWCollision(Room* self, Player* player, WarpZone** warpBoxes, int* whichWarp)
{
  int i;

  for (i = 1; i <= self->numberOfWarps; i++)
  {
    if (!((*warpBoxes)->tombStone) && playerIsInRect(player, &(*warpBoxes)->location))
    {
      *whichWarp = i-1;
      return 1;
    }
    warpBoxes++;
  }
  return 0;
}

int checkKCollision(Room* self, Player* player, HyperKaos** triggers, int* whichTrigger, int* kType, int iType)
{
  int i;
  int (*checkFunc)(Player*, SDL_Rect*);

  switch (iType)
  {
    case 0:
      checkFunc = &playerIsInRect; break;
    default:
      checkFunc = &playerFacesRect; break;
  }

  for (i = 1; i <= self->numberOfTriggers; i++)
  {
      if (!((*triggers)->tombStone) && checkFunc(player, &(*triggers)->domain))
      {
        if ((*triggers)->eventType == 0)
        {
          *whichTrigger = i-1;
          *kType = 0;
          return 1;
        }
        else
        {
         *whichTrigger = i-1;
         *kType = 1;
         return 1;
        }
      }
    triggers++;
  }
  return 0;
}

//
// obstacles
//

void addObstacle(Room* self, int x, int y, int w, int h)
{
  int i;
  if (self->numberOfObstacles+1 > self->maxNumberOfObstacles)
  {
    self->maxNumberOfObstacles *= 2;
    Obstruction** temp = malloc(self->maxNumberOfObstacles*sizeof(Obstruction*));
    for (i = 0; i < self->numberOfObstacles; i++)
      temp[i] = self->obstacle[i];
    free(self->obstacle);
    self->obstacle = temp;
    temp = NULL;
  }


  self->obstacle[self->numberOfObstacles] = newObstruction(x, y, w, h);

  self->numberOfObstacles++;
}

void deleteObstacle(Room* self, int i)
{
  self->obstacle[i]->tombStone = 1;
}

//
// fg objects
//

void addFgObj(Room* self, int x, int y, int w, int h, char* filename, int f, int dual, int alpha)
{
  int i;
  if (self->numberOfObj+1 > self->maxNumberOfObj)
  {
    self->maxNumberOfObj *= 2;
    FGImage** temp = malloc(self->maxNumberOfObj*sizeof(FGImage*));;
    for (i = 0; i < self->numberOfObj; i++)
      temp[i] = self->fgObject[i];
    free(self->fgObject);
    self->fgObject = temp;
    temp = NULL;
  }

  self->fgObject[self->numberOfObj] = newFGImage(x, y, w, h, f, dual, filename, alpha);

  self->numberOfObj++;

}

void deleteFgObj(Room* self, int i)
{
  self->fgObject[i]->tombStone = 1;
}

void drawFgObjects1(Room* self)
{
  int i;
  SDL_Rect clip;
  clip.y = 0;

  if (self->numberOfObj != 0)
  {
    self->objIterator++;

    for (i = 0; i < self->numberOfObj; i++)
    {
      if (!(self->fgObject[i]->tombStone))
      {
        clip.w = self->fgObject[i]->location.w;
        clip.x = self->fgObject[i]->frameNow*clip.w;
        clip.h = self->fgObject[i]->location.h;
        applySurface(self->fgObject[i]->location.x, self->fgObject[i]->location.y, self->fgObject[i]->spriteSheet, screen, &clip );
        if (self->objIterator == self->objSpeed)
          self->fgObject[i]->frameNow++;
        if (self->fgObject[i]->frameNow == self->fgObject[i]->frames)
          self->fgObject[i]->frameNow = 0;
      }
    }
    if (self->objIterator == self->objSpeed)
      self->objIterator = 0;
  }
}

void drawFgObjects2(Room* self)
{
  int i;
  SDL_Rect clip;
  clip.y = 0;

  if (self->numberOfObj != 0)
  {
    self->objIterator++;

    for (i = 0; i < self->numberOfObj; i++)
    {
      if (!(self->fgObject[i]->tombStone) && self->fgObject[i]->dualLayer)
      {
        clip.w = self->fgObject[i]->location.w;
        clip.x = self->fgObject[i]->frameNow*clip.w;
        clip.h = self->fgObject[i]->location.h;
        clip.y = clip.h;
        applySurface(self->fgObject[i]->location.x, self->fgObject[i]->location.y, self->fgObject[i]->spriteSheet, screen, &clip );
        if (self->objIterator == self->objSpeed)
          self->fgObject[i]->frameNow++;
        if (self->fgObject[i]->frameNow == self->fgObject[i]->frames)
          self->fgObject[i]->frameNow = 0;
      }
    }
    if (self->objIterator == self->objSpeed)
      self->objIterator = 0;
  }
}

//
// warps
//

void addWarp(Room* self, int x, int y, int w, int h, enum dataChunks toChunk, int goesTo, int goesX, int goesY)
{
  int i;
  if (self->numberOfWarps+1 > self->maxNumberOfWarps)
  {
    self->maxNumberOfWarps *= 2;
    WarpZone** temp = malloc(self->maxNumberOfWarps*sizeof(WarpZone*));
    for (i = 0; i < self->numberOfWarps; i++)
      temp[i] = self->warps[i];
    free(self->warps);
    self->warps = temp;
    temp = NULL;
  }

  self->warps[self->numberOfWarps] = newWarpZone(x, y, w, h, toChunk, goesTo, goesX, goesY);
  self->numberOfWarps++;
}

void deleteWarp(Room* self, int i)
{
  self->warps[i]->tombStone = 1;
}

//
// event triggers
//

void addTrigger(Room* self, HyperKaos* newTrigger)
{
  int i;
  if (self->numberOfTriggers+1 > self->maxNumberOfTriggers)
  {
    self->maxNumberOfTriggers *= 2;
    HyperKaos** temp = malloc(self->maxNumberOfTriggers*sizeof(HyperKaos*));
    for (i = 0; i < self->numberOfTriggers; i++)
      temp[i] = self->eventTriggers[i];
    free(self->eventTriggers);
    self->eventTriggers = temp;
    temp = NULL;
  }

  self->eventTriggers[self->numberOfTriggers++] = newTrigger;
}

void deleteTrigger(Room* self, int i)
{
  self->eventTriggers[i]->tombStone = 1;
}

void addSigil(Room* self, Synergy* newSigil)
{
  int i;
  if (self->numberOfSigils+1 > self->maxNumberOfSigils)
  {
    self->maxNumberOfSigils *= 2;
    Synergy** temp = malloc(self->maxNumberOfSigils*sizeof(Synergy*));
    for (i = 0; i < self->numberOfSigils; i++)
      temp[i] = self->sigils[i];
    free(self->sigils);
    self->sigils = temp;
    temp = NULL;
  }

  self->sigils[self->numberOfSigils++] = newSigil;
}

void deleteSigil(Room* self, int i)
{
  self->sigils[i]->tombStone = 1;
}

void addPerson(Room* self,  Player* newPlayer)
{
  int i;
  if (self->numberOfPeople+1 > self->maxNumberOfPeople)
  {
    self->maxNumberOfPeople *= 2;
    Player** temp = malloc(self->maxNumberOfPeople*sizeof(Player*));
    for (i = 0; i < self->numberOfPeople; i++)
      temp[i] = self->people[i];
    free(self->people);
    self->people = temp;
    temp = NULL;
  }

  self->people[self->numberOfPeople++] = newPlayer;
}

void deletePerson(Room* self, int i)
{
  self->people[i]->tombStone = 1;
}

void drawPeople(Room* self)
{
  int i;
  for (i = 0; i < self->numberOfPeople; i++)
  {
    if(!(self->people[i]->tombStone))
    drawPlayer(self->people[i]);
  }
}

//
// warp
//

void warpto(Room* destination)
{
  rightHere = destination;
}

