#include <stdio.h>

#include "config.h"

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

#include "enum.h"
#include "Engine.h"
#include "Player.h"
#include "Room.h"
#include "WorldData.h"

typedef struct TTF_Font TTF_Font;
typedef struct timer Timer;
typedef struct textBox TextBox;
typedef struct kaos Kaos;
typedef struct scene Scene;
typedef struct hyperKaos HyperKaos;
#include "extern.h"

Player* newPlayer(char* filename, int a, int b)
{
  SDL_Rect originClip;
  originClip.x = 32;
  originClip.y = 0;
  originClip.h = 16;
  originClip.w = 16;
  Uint32 rmask, gmask, bmask, amask;

  #if SDL_BYTEORDER == SDL_BIG_ENDIAN
    rmask = 0xff000000;
    gmask = 0x00ff0000;
    bmask = 0x0000ff00;
    amask = 0x000000ff;
  #else
    rmask = 0x000000ff;
    gmask = 0x0000ff00;
    bmask = 0x00ff0000;
    amask = 0xff000000;
  #endif

  Player* self = malloc(sizeof(Player));

  self->counter = 0;
  self->spriteSheet = NULL;
  self->spriteSheet = loadImage(filename);
  self->sprite = SDL_CreateRGBSurface(0,16,16,32,rmask,gmask,bmask,amask);
  applySurface(0, 0, self->spriteSheet, self->sprite, &originClip);
  self->point.x = a;
  self->point.y = b;
  self->bearing.x = 0;
  self->bearing.y = 0;
  self->boundBox.x = self->point.x - 8;
  self->boundBox.y = self->point.y - 8;
  self->boundBox.h = 16;
  self->boundBox.w = 16;
  self->frontFaceBox.x = self->boundBox.x;
  self->frontFaceBox.y = self->boundBox.y + 16;
  self->frontFaceBox.w = 16;
  self->frontFaceBox.h = 16;

  return self;
}

void killPlayer(Player* self)
{

  SDL_FreeSurface(self->sprite);
  SDL_FreeSurface(self->spriteSheet);
  free(self);
}

void movePlayer(Player* self, Room* hereNow)
{
  self->point.x += self->bearing.x;
  self->boundBox.x = self->point.x - 8;

 // obstacle collision

  if (checkCollision(rightHere, self, rightHere->obstacle))
  {
    self->point.x -= self->bearing.x;
    self->boundBox.x = self->point.x - 8;
  }

  self->point.y += self->bearing.y;
  self->boundBox.y = self->point.y - 8;


  if (checkCollision(rightHere, self, rightHere->obstacle))
  {
    self->point.y -= self->bearing.y;
    self->boundBox.y = self->point.y - 8;
  }

  // event trigger collision

  if (self->bearing.y > 0)
  {
    self->frontFaceBox.x = self->boundBox.x;
    self->frontFaceBox.y = self->boundBox.y + 16;
  }

  if (self->bearing.y < 0)
  {
    self->frontFaceBox.x = self->boundBox.x;
    self->frontFaceBox.y = self->boundBox.y - 16;
  }

  if (self->bearing.x > 0)
  {
    self->frontFaceBox.x = self->boundBox.x + 16;
    self->frontFaceBox.y = self->boundBox.y;
  }
  if (self->bearing.x < 0)
  {
    self->frontFaceBox.x = self->boundBox.x - 16;
    self->frontFaceBox.y = self->boundBox.y;
  }

  int kt = 0;
  int kf = 0;
  if (checkKCollision(rightHere, &(self->boundBox), rightHere->eventTriggers, &kf, &kt))
  {
    if (!kt)
    {
      kaosFlag = kf;
    }
    else if (actionbutton)
    {
      kaosFlag = kf;
    }
  }
  if (checkKCollision(rightHere, &(self->frontFaceBox), rightHere->eventTriggers, &kf, &kt))
  {
    if (kt)
    {
      if (actionbutton)
        kaosFlag = kf;
    }
  }

  // warp collision

  int outgoing = 0;
  if (!captive)
  {
  if (checkWCollision(rightHere, &(self->boundBox), rightHere->warps, &outgoing))
  {
    nextChunk = ((rightHere->warps) + outgoing)->chunk;

    if (nextChunk != thisChunk)
    {
      bufferData(((hereNow->warps) + outgoing)->chunk);
      warpto(mapBuffer[((rightHere->warps) + outgoing)->destination]);
    }
    else warpto(mapData[((rightHere->warps) + outgoing)->destination]);
    self->point.x = ((hereNow->warps) + outgoing)->x;
    self->point.y = ((hereNow->warps) + outgoing)->y;
  }
  }
  //stay onscreen

  if (self->point.x < 1) self->point.x = 1;
  if (self->point.x > 320 - 1) self->point.x = 320 - 1;
  if (self->point.y < 1) self->point.y = 1;
  if (self->point.y > 180) self->point.y = 180 - 1;
}

//
// graphics
//

void drawPlayer(Player* self)
{
  applySurface(self->point.x - 8, self->point.y - 8, self->sprite, screen, NULL);
  walkAnim(self);
}


void changeSprite(Player* self, SDL_Rect* clip)
{
  SDL_Rect zeroOffset;
  zeroOffset.x = 0;
  zeroOffset.y = 0;
  SDL_FillRect(self->sprite, NULL, 0x000000);
  SDL_BlitSurface(self->spriteSheet, clip, self->sprite, &zeroOffset);
}

void walkAnim(Player* self)
{
  SDL_Rect playerClip;
  playerClip.w = 16;
  playerClip.h = 16;
  playerClip.y = 0;
  if (self->bearing.y > 0)
  {
    if (self->counter == 0)
    {
      playerClip.x = 32;
      changeSprite(self, &playerClip);
    }
    if (self->counter == 4)
    {
      playerClip.x = 48;
      changeSprite(self, &playerClip);
    }
  }
  if (self->bearing.y < 0)
  {
    if (self->counter == 0)
    {
      playerClip.x = 112;
      changeSprite(self, &playerClip);
    }
    if (self->counter == 4)
    {
      playerClip.x = 96;
      changeSprite(self, &playerClip);
    }
  }
  if (self->bearing.x > 0)
  {
    if (self->counter == 0)
    {
      playerClip.x = 64;
      changeSprite(self, &playerClip);
    }
    if (self->counter == 4)
    {
      playerClip.x = 80;
      changeSprite(self, &playerClip);
    }
  }
  if (self->bearing.x < 0)
  {
    if (self->counter == 0)
    {
      playerClip.x = 0;
      changeSprite(self, &playerClip);
    }
    if (self->counter == 4)
    {
      playerClip.x = 16;
      changeSprite(self, &playerClip);
    }
  }
  self->counter++;
  if (self->counter == 8) self->counter = 0;
}

int playerFaces(Player* self, char dir)
{
  switch (dir)
  {
    case 'n':
      if (self->frontFaceBox.y < self->boundBox.y)
        return 1;
      break;
    case 's':
      if (self->frontFaceBox.y > self->boundBox.y)
        return 1;
      break;
    case 'e':
      if (self->frontFaceBox.x > self->boundBox.x)
        return 1;
      break;
    case 'w':
      if (self->frontFaceBox.x < self->boundBox.x)
        return 1;
      break;
  }
  return 0;
}

int playerIsInRect(Player* self, SDL_Rect* rect)
{
  SDL_Rect* pBox = &(self->boundBox);
  int playerX = pBox->x + (pBox->w)/2;
  int playerY = pBox->y + (pBox->h)/2;
  if ((playerX >= rect->x && playerX <= rect->x + rect->w)
    && (playerY >= rect->y && playerY <= rect->y + rect->h))
    return 1;
  else return 0;
}