#include <stdio.h>
#include "swis.h"
#include <kernel.h>
#include <time.h>
#include "Sound.h"
#include "Graphics.h"

/********************************************\
Slightly hacked up 'Mission2' as a map editor
\********************************************/

extern int tick;
extern int lasttick;
extern int screen;

#define TILESX 10
#define TILESY 10
#define SMARTTILES 200
#define AREAS 20

// SWI Registers
_kernel_swi_regs inreg;
_kernel_swi_regs outreg;

// Sprite buffer
unsigned char *buffer;
unsigned char *fontbuffer;
unsigned char *tilebuffer;

int tick = 0;
int lasttick = 0;
extern int screen;
int clipboard = 0;
int clipboard_tileid = 0;
extern void screen_nobuffer();
unsigned int editsmart = 0;
int selectedsmart = -1;
// map[0] is the 'master', [1] and [2] represents what is currently
// believed to be displayed in the corresponding (+1) screen buffer
// and we run a compare to see if re-drawing is needed. 0xFF is used
// for a re-draw being required (so it's set on the tile under the player
unsigned char map[3][TILESX][TILESY];
unsigned char fullmap[10000];

char tilenamebuffer[4];
char textbuffer[63];

struct EntityLocation_s {
  short signed int X,Y;
};

int readmodkey = 0;

struct TilePlayer_s {
  struct EntityLocation_s location;
  struct EntityLocation_s hitbox_bl;
  struct EntityLocation_s hitbox_tr;
  struct EntityLocation_s mapoffset;
  struct EntityLocation_s localtile;
  int rawtile;
};

struct TilePlayer_s TilePlayer;

struct SmartTile_s {
  int Tile;
  int ActionTarget;
  unsigned char Action;
  unsigned char ActionValue;
};

struct SmartTile_s SmartTiles[SMARTTILES];

struct Area_s {
  unsigned char name[16];
};

struct Area_s Areas[AREAS];

#define ACTION_SIZE 16

char actionnames[][ACTION_SIZE] =
{ "TileSet",
  "AreaChange"
};

void game2_loadsmarttiles(char* filename)
{
  int length;

  // Attempt to get file info
  inreg.r[0] = 5;
  inreg.r[1] = (int) filename;
  _kernel_swi(OS_File,&inreg,&outreg);

  // Length will be in R4 if it exists
  length = outreg.r[4];

  if(length > sizeof(SmartTiles))
  {
    screen_nobuffer();
    while (1)
    printf("Smarttiles exceeds %d bytes (%d bytes) object type is %d\n",sizeof(SmartTiles),length,outreg.r[0]);
    //exit(0);
  }

  // Attempt to get file info
  inreg.r[0] = 16;
  inreg.r[1] = (int) filename;
  inreg.r[2] = (int) SmartTiles;
  inreg.r[3] = 0;

  _kernel_swi(OS_File,&inreg,&outreg);
 
}

void game2_loadareanames(char* filename)
{
  int length;

  // Attempt to get file info
  inreg.r[0] = 5;
  inreg.r[1] = (int) filename;
  _kernel_swi(OS_File,&inreg,&outreg);

  // Length will be in R4 if it exists
  length = outreg.r[4];

  if(length > sizeof(Areas))
  {
    screen_nobuffer();
    while (1)
    printf("Areas exceeds %d bytes (%d bytes) object type is %d\n",sizeof(Areas),length,outreg.r[0]);
    //exit(0);
  }

  // Attempt to get file info
  inreg.r[0] = 16;
  inreg.r[1] = (int) filename;
  inreg.r[2] = (int) Areas;
  inreg.r[3] = 0;

  _kernel_swi(OS_File,&inreg,&outreg);
 
}

void game2_savesmarttiles(char* filename)
{
  // Attempt to get file info
  inreg.r[0] = 10;
  inreg.r[1] = (int) filename;
  inreg.r[2] = 0xffd;
  inreg.r[4] = (int) SmartTiles;
  inreg.r[5] = (int) SmartTiles + (sizeof(SmartTiles));

  _kernel_swi(OS_File,&inreg,&outreg);
}


int game2_getnewsmarttile()
{
  int i;
  for(i = 0; i < SMARTTILES; i++)
  {
    if(SmartTiles[i].Tile == -1)
    {
      SmartTiles[i].Tile = TilePlayer.rawtile;
      SmartTiles[i].Action = 0;
      SmartTiles[i].ActionValue = 0;
      return i;
    }
  }
  
    screen_nobuffer();
    printf("Couldn't allocate new spart tile.");
    exit(0);
  return -1;
}

void game2_loadmap(char* filename)
{
  int length;

  // Attempt to get file info
  inreg.r[0] = 5;
  inreg.r[1] = (int) filename;
  _kernel_swi(OS_File,&inreg,&outreg);

  // Length will be in R4 if it exists
  length = outreg.r[4];

  if(length > sizeof(fullmap))
  {
    screen_nobuffer();
    printf("Map exceeds %d bytes (%d bytes) object type is %d",sizeof(fullmap),length,outreg.r[0]);
    exit(0);
  }
  
  // Attempt to get file info
  inreg.r[0] = 16;
  inreg.r[1] = (int) filename;
  inreg.r[2] = (int) fullmap;
  inreg.r[3] = 0;

  _kernel_swi(OS_File,&inreg,&outreg);
}

void game2_savemap(char* filename)
{
  // Attempt to get file info
  inreg.r[0] = 10;
  inreg.r[1] = (int) filename;
  inreg.r[2] = 0xffd;
  inreg.r[4] = (int) fullmap;
  inreg.r[5] = (int) fullmap + sizeof(fullmap);

  _kernel_swi(OS_File,&inreg,&outreg);
}

void game2_fillmap(int xoffset, int yoffset)
{
  memcpy(map[0][0],fullmap+yoffset+(xoffset*100),10);
  memcpy(map[0][1],fullmap+yoffset+(xoffset*100)+100,10);
  memcpy(map[0][2],fullmap+yoffset+(xoffset*100)+200,10);
  memcpy(map[0][3],fullmap+yoffset+(xoffset*100)+300,10);
  memcpy(map[0][4],fullmap+yoffset+(xoffset*100)+400,10);
  memcpy(map[0][5],fullmap+yoffset+(xoffset*100)+500,10);
  memcpy(map[0][6],fullmap+yoffset+(xoffset*100)+600,10);
  memcpy(map[0][7],fullmap+yoffset+(xoffset*100)+700,10);
  memcpy(map[0][8],fullmap+yoffset+(xoffset*100)+800,10);
  memcpy(map[0][9],fullmap+yoffset+(xoffset*100)+900,10);
}

void game2_setup()
{
  screen_flipbuffer();
  screen_clear();
  screen_flipbuffer();
  screen_clear();
  TilePlayer.location.X = 50;
  TilePlayer.location.Y = 50;
  TilePlayer.hitbox_bl.X = 0;
  TilePlayer.hitbox_bl.Y = 0;
  TilePlayer.hitbox_tr.X = 60;
  TilePlayer.hitbox_tr.Y = 50;
  memset(map[0],0xFF,100);
  memset(map[1],0xFF,100);
  memset(map[2],0xFF,100);

  game2_loadmap("m2_map");
  game2_loadsmarttiles("m2_smart");
  game2_loadareanames("m2_areas");
  game2_fillmap(TilePlayer.mapoffset.X,TilePlayer.mapoffset.Y);
}

void game2_tick_input()
{
  int x,y;
  int oldselectedsmart;
  int i,tmptileid;

  oldselectedsmart = selectedsmart;
  if(editsmart == 1)
  {
    // Right arrow
    if(input_readkey(121))
    {
       if(tick > readmodkey)
      {
        SmartTiles[selectedsmart].Action++;
        if(SmartTiles[selectedsmart].Action > 1)
          SmartTiles[selectedsmart].Action = 1;
        readmodkey = tick + 20;
      }
    }

    // Left arrow
    if(input_readkey(25))
    {
      if(tick > readmodkey)
      {
        SmartTiles[selectedsmart].Action--;
        // Looks weird, but unsigned so -1 is 255
        if(SmartTiles[selectedsmart].Action > 1)
          SmartTiles[selectedsmart].Action = 0;
        readmodkey = tick + 20;
      }
    }

    // Up arrow
    if(input_readkey(57))
    {
      if(tick > readmodkey)
      {
        selectedsmart--;
        while(SmartTiles[selectedsmart].Tile != SmartTiles[oldselectedsmart].Tile)
        {
          if(selectedsmart > 0)
            selectedsmart--;
          else
            selectedsmart = oldselectedsmart;
        }
        readmodkey = tick + 20;
      }
    }

    // Down arrow
    if(input_readkey(41))
    {
      if(tick > readmodkey)
      {
        selectedsmart++;
        while(SmartTiles[selectedsmart].Tile != SmartTiles[oldselectedsmart].Tile)
        {
          if(selectedsmart < SMARTTILES)
            selectedsmart++;
          else
            selectedsmart = oldselectedsmart;
        }
        readmodkey = tick + 20;
      }
    }

    // > - next tile
    if(input_readkey(103))
    {
      if(tick > readmodkey)
      {
        if(SmartTiles[selectedsmart].Action == 0)
          SmartTiles[selectedsmart].ActionValue++;
        if(SmartTiles[selectedsmart].Action == 1)
          SmartTiles[selectedsmart].ActionValue++;
        readmodkey = tick + 10;
      }
    }

    // < - previous tile
    if(input_readkey(102))
    {
      if(tick > readmodkey)
      {
        if(SmartTiles[selectedsmart].Action == 0)
          SmartTiles[selectedsmart].ActionValue--;
        if(SmartTiles[selectedsmart].Action == 1)
          SmartTiles[selectedsmart].ActionValue--;
        readmodkey = tick + 10;
      }
    }

    // D - Delete SmartTile
    if(input_readkey(50))
    {
      if(tick > readmodkey)
      {
        SmartTiles[selectedsmart].Tile = -1;
        readmodkey = tick + 50;
      }
    }
    

    // C - Create smart tile
    if(input_readkey(82))
    {
      if(tick > readmodkey)
      {
        game2_getnewsmarttile();
        readmodkey = tick + 10;
      }
    }

    // P - Set Target
    if(input_readkey(55))
    {
      if(tick > readmodkey)
      {
         if(SmartTiles[selectedsmart].Action == 0)
           SmartTiles[selectedsmart].ActionTarget = clipboard_tileid;
        readmodkey = tick + 10;
      }
    }
  }else{
    // Right arrow
    if(input_readkey(121))
    {
       if(tick > readmodkey)
      {
        TilePlayer.location.X += 100;
        if(TilePlayer.location.X > (950))
        {
          TilePlayer.location.X = 50;
          TilePlayer.mapoffset.X += 10;
        }
        readmodkey = tick + 10;
      }
    }

    // Left arrow
    if(input_readkey(25))
    {
      if(tick > readmodkey)
      {
        TilePlayer.location.X -= 100;
        if(TilePlayer.location.X < (10))
        {
          if(TilePlayer.mapoffset.X > 0)
          {
            TilePlayer.location.X = 950;
            TilePlayer.mapoffset.X -= 10;
          }else{
            TilePlayer.location.X += 100;
          }
        }
        readmodkey = tick + 10;
      }
    }

    // Up arrow
    if(input_readkey(57))
    {
      if(tick > readmodkey)
      {
        TilePlayer.location.Y += 100;
        if(TilePlayer.location.Y > (950))
        {
          TilePlayer.location.Y = 50;
          TilePlayer.mapoffset.Y += 10;
        }
        readmodkey = tick + 10;
      }
    }

    // Down arrow
    if(input_readkey(41))
    {
      if(tick > readmodkey)
      {
        TilePlayer.location.Y -= 100;
        if(TilePlayer.location.Y < (10))
        {
          if(TilePlayer.mapoffset.Y > 0)
          {
            TilePlayer.location.Y = 950;
            TilePlayer.mapoffset.Y -= 10;
          }else{
            TilePlayer.location.Y += 100;
          }
        }
        readmodkey = tick + 10;
      }
    }

    // > - next tile
    if(input_readkey(103))
    {
      if(tick > readmodkey)
      {
        fullmap[TilePlayer.rawtile] = fullmap[TilePlayer.rawtile] + 1;
        readmodkey = tick + 10;
      }
    }

    // < - previous tile
    if(input_readkey(102))
    {
      if(tick > readmodkey)
      {
        fullmap[TilePlayer.rawtile] = fullmap[TilePlayer.rawtile] - 1;
        readmodkey = tick + 10;
      }
    }

    // C - Copy tile
    if(input_readkey(82))
    {
      if(tick > readmodkey)
      {
        clipboard = fullmap[TilePlayer.rawtile];
        clipboard_tileid = TilePlayer.rawtile;
        readmodkey = tick + 10;
      }
    }

    // P - Paste tile
    if(input_readkey(55))
    {
      if(tick > readmodkey)
      {
        fullmap[TilePlayer.rawtile] = clipboard;

        for(i = 0; i < SMARTTILES; i++)
        {
          if(SmartTiles[i].Tile == clipboard_tileid)
          {
            tmptileid = game2_getnewsmarttile();

            if(tmptileid > -1)
            {
              SmartTiles[tmptileid].ActionTarget = SmartTiles[i].ActionTarget;
              SmartTiles[tmptileid].Action = SmartTiles[i].Action;
              SmartTiles[tmptileid].ActionValue = SmartTiles[i].ActionValue;
            }
          }
        }
        readmodkey = tick + 10;
      }
    }
  }

  // S - Save
  if(input_readkey(81))
  {
    game2_savemap("m2_map");
    game2_savesmarttiles("m2_smart");
  }

  // H - Toggle 'hard' on a tile
  if(input_readkey(84))
  {
    if(tick > readmodkey)
    {
      fullmap[TilePlayer.rawtile] = fullmap[TilePlayer.rawtile] ^ 128;
      readmodkey = tick + 10;
    }
  }

  // T - Toggle Smart Tile (trigger) editor
  if(input_readkey(35))
  {
    if(tick > readmodkey)
    {
      editsmart = !editsmart;
      readmodkey = tick + 50;
    }
  }
}

int game2_tick()
{
  int x;
  int y;
  int i;
  int udt = 0;
  int smarty = 0;
  lasttick = tick;
  tick = clock();

  screen_flipbuffer();

  if(1)
  {
    screen_clear();
    game2_fillmap(TilePlayer.mapoffset.X,TilePlayer.mapoffset.Y);
    for(x = 0; x < TILESX; x++)
    {
      for(y = 0; y < TILESY; y++)
      {
        if(1/*map[0][x][y] ^ map[screen+1][x][y]*/)
        {
          map[screen+1][x][y] = map[0][x][y];
          sprintf(tilenamebuffer,"%i",map[screen+1][x][y] & 127);
          draw_tile(tilenamebuffer,x*100,y*100);
          if(((map[screen+1][x][y] >> 7)  & 0x01))
          {
          draw_sprite("solid",x*100,y*100);
          }
          udt++;
        }
        
        if(game_hitbox_collide(
        (TilePlayer.location.X),(TilePlayer.location.Y),
        1,1,
        x*100,y*100,
        100,100
        ))
        {
          map[1][x][y] = 255;
          map[2][x][y] = 255;
          TilePlayer.localtile.X = x;
          TilePlayer.localtile.Y = y;
          draw_sprite("select1",x*100,y*100);
        }


      }
    }

    TilePlayer.rawtile = (TilePlayer.mapoffset.Y) + TilePlayer.localtile.Y + (TilePlayer.mapoffset.X * 100) + (TilePlayer.localtile.X * 100);

    if(editsmart == 1)
    {
      // If our selected smart tile isn't one we're stood on then reset
      if(SmartTiles[selectedsmart].Tile != TilePlayer.rawtile)
        selectedsmart = -1;

      for(i = 0; i < SMARTTILES; i++)
      {
        if(SmartTiles[i].Tile == TilePlayer.rawtile)
        {
          // If we dont have a selected one, then use the first one we hit
          if(selectedsmart == -1)
            selectedsmart = i;

          draw_sprite("smart",TilePlayer.localtile.X*100,TilePlayer.localtile.Y*100);
          
          smarty++;

          if(selectedsmart == i)
          {
            sprintf(textbuffer,"} %i",SmartTiles[i].ActionTarget);
            draw_spritetext(textbuffer, 150, 1000 - (smarty*120));
          }
          else
          {
            sprintf(textbuffer,"] %i",SmartTiles[i].ActionTarget);
            draw_spritetext(textbuffer, 150, 1000 - (smarty*120));
          }

          sprintf(textbuffer,"%s",actionnames[SmartTiles[i].Action]);
          draw_spritetext(textbuffer, 300, 1000 - (smarty*120));

          if(SmartTiles[i].Action == 0)
          {
            sprintf(tilenamebuffer,"%i",SmartTiles[i].ActionValue);
            draw_tile(tilenamebuffer,600,1000 - (smarty*120) - 25);
          }else if(SmartTiles[i].Action == 1)
          {
            sprintf(textbuffer,"%s",Areas[SmartTiles[i].ActionValue].name);
            draw_spritetext(textbuffer,600,1000 - (smarty*120));
          }
        }
      }

      // If no smart tiles then say
      if(smarty == 0)
      {
        smarty++;
        sprintf(textbuffer,"No Smart Tiles");
        draw_spritetext(textbuffer, 150, 1000 - (smarty*120));
      }
    }else{
      // Just a simple highlight if we've not got the smart edit being shown
      for(i = 0; i < SMARTTILES; i++)
      {
        if(SmartTiles[i].Tile == TilePlayer.rawtile)
          draw_sprite("smart",TilePlayer.localtile.X*100,TilePlayer.localtile.Y*100);
      }
    }

    sprintf(textbuffer,"Tile:%i\nSprite:%i\nSolid: %i",
        TilePlayer.rawtile,
        fullmap[TilePlayer.rawtile],
        ((fullmap[TilePlayer.rawtile] >> 7)  & 0x01)
    );
    draw_spritetext(textbuffer, 1070, 950);

    game2_tick_input();

    x = 1100;
    y = 600;

    for(i = (fullmap[TilePlayer.rawtile] - 3); i <= (fullmap[TilePlayer.rawtile] + 3); i++)
    {
      if((i >=0) && (i <= 255))
      {
        sprintf(tilenamebuffer,"%i",i);
        draw_tile(tilenamebuffer,x,y);
      
        sprintf(textbuffer,"%d",i);
        draw_spritetext(textbuffer, x-60, y+30);
    
        if(i == fullmap[TilePlayer.rawtile])
          draw_sprite("select1",x,y);
      }
      y = y - 100;
    }

    x = 1180;
    y = 750;
  
    sprintf(tilenamebuffer,"%i",clipboard);
    draw_tile(tilenamebuffer,x,y);
    draw_sprite("select1",x,y);
    sprintf(tilenamebuffer,"%i",clipboard_tileid);
    draw_spritetext(tilenamebuffer, x+10, y+30);
    draw_spritetext("Clipboard", x-174, y+30);
    
    
    return 0;
  }else{
/*    screen_flipbuffer();
    screen_clear();
    game2_death();
    return 1;*/
  }
}

int game_hitbox_collide(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2)
{
    if((x1 + w1) >= x2)
      if(x1 <= (x2 + w2))
        if((y1 + h1) >= y2)
          if(y1 <= (y2 + h2))
            return 1;

  return 0;
}

void exitfunc () {
  screen_flipbuffer();
  screen_clear();
  screen_flipbuffer();
  screen_clear();
  screen_nobuffer();
  free(buffer);
  free(fontbuffer);
  free(tilebuffer);
}

int main(int argc, char *argv[])
{
  int lastoutcome = 1;
  int outcome = 0;
  
  atexit(exitfunc);

  // Set initial display mode
  display_mode(DISPLAY_MODE);
  screen_clear();

  // Load sprite library
  load_sprites("Spr",&buffer);
  load_sprites("Font",&fontbuffer);

  // Clear both buffers or we get gibberish
  screen_flipbuffer();
  screen_clear();
  screen_flipbuffer();
  screen_clear();

  lastoutcome = 1;

  load_sprites("Tiles",&tilebuffer);

  // Mission 2
  while(lastoutcome == 1)
  {
    outcome = 0;
    screen_clear();
    game2_setup();
    while(!outcome)
    {
      outcome = game2_tick();
    }
    lastoutcome = outcome;
  }

  free(buffer);

  return 0;
}
