#include "map.h"
#include "globals.h"
#include <iostream>
#include <fstream>
#include "mystr.h"
#include "camera.h"
#include "mymath.h"
#include "person.h"




MapStoryboard actual_map_storyboard[12];


int MapPowers[4];


using namespace std;

std::deque<SpawnPoint *> spawn_points;
std::deque<LightPoint *> light_points;

Map *actual_map=NULL;


void MapStoryboard::CopyFromEditor(EditorStoryboard *p) {
  for (int i=0;i!=8;i+=1) strcpy(text[i], p->paragraph[i].c_str());
  cam_x = p->cam_x;
  cam_y = p->cam_y;
}


void MapStoryboard::Clear() {
  cam_x=cam_y=0;
  for (int i=0;i!=8;i+=1) strcpy(text[i], "");
}


void SpawnPeople() {
  std::deque<SpawnPoint *>::iterator it;
  for (it=spawn_points.begin(); it!=spawn_points.end(); it+=1) {
    SpawnPerson((*it));
  }
}




void Map::Start(int tw, int th, CacheBitmap *t) {
  w = tw;
  h = th;

  real_w = w * SCREEN_TILE_W;
  real_h = h * SCREEN_TILE_H;

  Camera_Boundary_X = real_w-768*(GlobalAspectRatio);
  Camera_Boundary_Y = real_h-768;

  if (w <= 0) {AL_MESSAGE("Map Width is below 0!"); return;}
  if (h <= 0) {AL_MESSAGE("Map Height is below 0!"); return;}
  for (int l=0; l!=3; l+=1) {
    tiles[l] = (short int **) malloc(sizeof(short int *) * w);
    for (int x=0; x!=w; x+=1) {
      tiles[l][x] = (short int *) malloc(sizeof(short int) * h);
      for (int y=0; y!=h; y+=1) {
        tiles[l][x][y] = -1;
      }
    }
  }
  spawn_points.clear();
  light_points.clear();

  for (int i=0;i!=12; i+=1) actual_map_storyboard[i].Clear();

  SetTileset(t);
}


void Map::Resize(float tw, float th) {
  for (int l=0; l!=3; l+=1) {
    for (int x=0; x!=w; x+=1) {
      free(tiles[l][x]);
    }
    free(tiles[l]);
  }

  w = tw;
  h = th;

  real_w = w * SCREEN_TILE_W;
  real_h = h * SCREEN_TILE_H;

  Camera_Boundary_X = real_w-768*(GlobalAspectRatio);
  Camera_Boundary_Y = real_h-768;

  if (w <= 0) {AL_MESSAGE("Map Width is below 0!"); return;}
  if (h <= 0) {AL_MESSAGE("Map Height is below 0!"); return;}
  for (int l=0; l!=3; l+=1) {
    tiles[l] = (short int **) malloc(sizeof(short int *) * w);
    for (int x=0; x!=w; x+=1) {
      tiles[l][x] = (short int *) malloc(sizeof(short int) * h);
      for (int y=0; y!=h; y+=1) {
        tiles[l][x][y] = -1;
      }
    }
  }
}


void Map::Clear() {
  for (int l=0; l!=3; l+=1) {
    for (int x=0; x!=w; x+=1) {
      for (int y=0; y!=h; y+=1) {
        tiles[l][x][y] = -1;
      }
    }
  }

  std::deque<SpawnPoint *>::iterator it;
  for (it=spawn_points.begin(); it!=spawn_points.end(); it+=1) {
    delete (*it);
  }
  spawn_points.clear();

  std::deque<LightPoint *>::iterator itl;
  for (itl=light_points.begin(); itl!=light_points.end(); itl+=1) {
    delete (*itl);
  }
  light_points.clear();
}


void Map::Save(std::string nm) {
  CopyToFile();
  ofstream myfile;
  myfile.open (("Maps/" + nm + ".map").c_str(), ios::out | ios::binary);

  char intnm[128];
  strcpy(intnm,internal_name.c_str());
  myfile.write(reinterpret_cast<char *>(intnm), sizeof(char)*128);

  myfile.write(reinterpret_cast<char *>(&mf),sizeof(MapFile));

  for (int l=0; l!=3; l+=1) {
    for (int x=0; x!=w; x+=1) {
      myfile.write(reinterpret_cast<char *>(tiles[l][x]), sizeof(short int) * h);
    }
  }

  int th = ((int)tileset->Height() / REAL_TILE_H) * TILESET_WIDTH;
  myfile.write(reinterpret_cast<char *>(collision_map), sizeof(short int) * th);

  int sz;
  sz = spawn_points.size();

  myfile.write(reinterpret_cast<char *>(&sz), sizeof(int));

  for (int i=0; i!=sz; i+=1) {
    myfile.write(reinterpret_cast<char *>(spawn_points[i]), sizeof(SpawnPoint));
  }


  sz = light_points.size();
  myfile.write(reinterpret_cast<char *>(&sz), sizeof(int));
  for (int i=0; i!=sz; i+=1) {
    myfile.write(reinterpret_cast<char *>(light_points[i]), sizeof(LightPoint));
  }

  myfile.write(reinterpret_cast<char *>(MapPowers), sizeof(int)*4);

  sz=StoryboardSize;
  myfile.write(reinterpret_cast<char *>(&sz), sizeof(int));
  myfile.write(reinterpret_cast<char *>(actual_map_storyboard), sizeof(MapStoryboard)*12);

  int b;
  b=weather;
  myfile.write(reinterpret_cast<char *>(&b), sizeof(int));

  myfile.close();
}

void Map::Load(std::string nm) {
  ifstream myfile;
  myfile.open (("Maps/" + nm + ".map").c_str(), ios::in | ios::binary);

  char intnm[128];
  myfile.read(reinterpret_cast<char *>(intnm), sizeof(char)*128);
  internal_name = ToString(intnm);

  myfile.read(reinterpret_cast<char *>(&mf),sizeof(MapFile));

  name = nm;

  Start(mf.w, mf.h, load_cache_bitmap(ToString(mf.tileset)));

  back[0] = load_cache_bitmap(mf.back_1);
  back[1] = load_cache_bitmap(mf.back_2);
  back[2] = load_cache_bitmap(mf.back_3);

  for (int l=0; l!=3; l+=1) {
    for (int x=0; x!=w; x+=1) {
      myfile.read(reinterpret_cast<char *>(tiles[l][x]), sizeof(short int) * h);
    }
  }

  int th = ((int)tileset->Height() / REAL_TILE_H) * TILESET_WIDTH;
  myfile.read(reinterpret_cast<char *>(collision_map), sizeof(short int) * th);

  int sz;
  sz=0;

  myfile.read(reinterpret_cast<char *>(&sz), sizeof(int));

  for (int i=0; i!=sz; i+=1) {
    SpawnPoint *sp;
    sp = new SpawnPoint(0, 0, 0);
    myfile.read(reinterpret_cast<char *>(sp), sizeof(SpawnPoint));
    spawn_points.push_back(sp);
  }


  sz = light_points.size();
  myfile.read(reinterpret_cast<char *>(&sz), sizeof(int));

  for (int i=0; i!=sz; i+=1) {
    LightPoint *sp;
    sp = new LightPoint(0, 0, 0);
    myfile.read(reinterpret_cast<char *>(sp), sizeof(LightPoint));
    light_points.push_back(sp);
  }

  myfile.read(reinterpret_cast<char *>(MapPowers), sizeof(int)*4);

  myfile.read(reinterpret_cast<char *>(&StoryboardSize), sizeof(int));
  myfile.read(reinterpret_cast<char *>(actual_map_storyboard), sizeof(MapStoryboard)*12);

  int b;
  myfile.read(reinterpret_cast<char *>(&b), sizeof(int));
  weather=b;

  myfile.close();
}


std::string GetRealMapName(std::string filename) {
  ifstream myfile;
  myfile.open (("Maps/" + filename + ".map").c_str(), ios::in | ios::binary);
  char intnm[128];
  myfile.read(reinterpret_cast<char *>(intnm), sizeof(char)*128);
  return ToString(intnm);
}


void Map::DrawLayer(int l) {
  int sx, sy, fx, fy;
  sx = ((int)Camera_X/SCREEN_TILE_W)-1;
  sy = ((int)Camera_Y/SCREEN_TILE_H)-1;
  fx = (sx + ((768*(GlobalAspectRatio)) / SCREEN_TILE_W))+3;
  fy = (sy + (768 / SCREEN_TILE_H))+3;

  if (sx < 0) sx = 0;
  if (sy < 0) sy = 0;
  if (fx > w) fx = w;
  if (fy > h) fy = h;

  //game_font->Print("SX: "+ToString(sx) + "SY: "+ ToString(sy) + "FX: "+ToString(fx) + "FY: "+ ToString(fy), 20, 40 + 20*l);

  for (int x=sx; x!=fx; x+=1) {
    for (int y=sy; y!=fy; y+=1) {
      short int t;
      t=tiles[l][x][y];
      if (t != -1) {
        int cx, cy;
        cx = (t % TILESET_WIDTH) * REAL_TILE_W;
        cy = (t / TILESET_WIDTH) * REAL_TILE_H;
        float dx, dy;
        dx = x*SCREEN_TILE_W-Camera_X;
        dy = y*SCREEN_TILE_H-Camera_Y;
        al_draw_scaled_bitmap(tileset->get_bit(), cx, cy, REAL_TILE_W, REAL_TILE_W, RSW(dx), RSH(dy), RSW(SCREEN_TILE_W), RSH(SCREEN_TILE_H), 0);
      }
    }
  }
}

void Map::SetTileset(CacheBitmap *t) {
  int th;
  tileset = t;
  th = ((int)tileset->Height() / REAL_TILE_H) * TILESET_WIDTH;
  collision_map = (short int *) malloc(sizeof(short int) * th);
  for (int x=0; x!=th; x+=1) {
    collision_map[x] = 0;
  }
}

bool Map::IsFree(int x, int y) {
  int tx, ty;
  tx = x / SCREEN_TILE_W;
  ty = y / SCREEN_TILE_H;
  if (tx < 1) return false;
  if (ty < 1) return false;
  if (tx >= w-1) return false;
  if (ty >= h-1) return false;
  for (int l=0; l!=3; l+=1) {
    int t;
    t = tiles[l][tx][ty];
    if (t != -1) {
      if (collision_map[t] >= 1) {
        return false;
      }
    }
  }
  return true;
}

bool Map::IsFreeAbove(int x, int y) {
  int tx, ty;
  tx = x / SCREEN_TILE_W;
  ty = y / SCREEN_TILE_H;
  if (tx < 0) return false;
  if (ty < 0) return false;
  if (tx >= w) return false;
  if (ty >= h) return false;
  for (int l=0; l!=3; l+=1) {
    int t;
    t = tiles[l][tx][ty];
    if (t != -1) {
      if (collision_map[t] == 1) {
        return false;
      }
    }
  }
  return true;
}

void Map::DrawBack() {
  float mx, my;
  int rw, rh;
  CacheBitmap *bit;

  /*  Draw Last background with a X*.25 Modifier */

  bit=back[2];
  if (bit != NULL) {
  rw = al_get_bitmap_width(bit->get_bit()); rh = 768;
  mx = ((int)-(Camera_X*0.25)%rw)+rw*0.5; my = 384;
  DRAW_TRANSFORMED(bit->get_bit(), RSW(mx), RSH(my), RSW(rw), RSH(rh), 0, 0);

  mx = ((int)-(Camera_X*0.25)%rw)+rw*1.5; my = 384;
  DRAW_TRANSFORMED(bit->get_bit(), RSW(mx), RSH(my), RSW(rw), RSH(rh), 0, 0);
  }


  /*  Draw Middle background with a X*.5 Modifier */

  bit=back[1];
  if (bit != NULL) {
  rw = al_get_bitmap_width(bit->get_bit()); rh = al_get_bitmap_height(bit->get_bit());
  mx = ((int)-(Camera_X*0.5)%al_get_bitmap_width(bit->get_bit()))+al_get_bitmap_width(bit->get_bit())*0.5; my = real_h-al_get_bitmap_height(bit->get_bit())/2-Camera_Y/2-Camera_Boundary_Y/2;
  DRAW_TRANSFORMED(bit->get_bit(), RSW(mx), RSH(my), RSW(al_get_bitmap_width(bit->get_bit())), RSH(al_get_bitmap_height(bit->get_bit())), 0, 0);

  mx = ((int)-(Camera_X*0.5)%al_get_bitmap_width(bit->get_bit()))+al_get_bitmap_width(bit->get_bit())*1.5; my = real_h-al_get_bitmap_height(bit->get_bit())/2-Camera_Y/2-Camera_Boundary_Y/2;
  DRAW_TRANSFORMED(bit->get_bit(), RSW(mx), RSH(my), RSW(al_get_bitmap_width(bit->get_bit())), RSH(al_get_bitmap_height(bit->get_bit())), 0, 0);
  }

  /*  Draw Nearest background with a X*.75 Modifier */

  bit=back[0];
  if (bit != NULL) {
  rw = al_get_bitmap_width(bit->get_bit()); rh = al_get_bitmap_height(bit->get_bit());
  mx = ((int)-(Camera_X*0.75)%al_get_bitmap_width(bit->get_bit()))+al_get_bitmap_width(bit->get_bit())*0.5; my = real_h-rh/2-Camera_Y;
  DRAW_TRANSFORMED(bit->get_bit(), RSW(mx), RSH(my), RSW(al_get_bitmap_width(bit->get_bit())), RSH(al_get_bitmap_height(bit->get_bit())), 0, 0);

  mx = ((int)-(Camera_X*0.75)%al_get_bitmap_width(bit->get_bit()))+al_get_bitmap_width(bit->get_bit())*1.5; my = real_h-rh/2-Camera_Y;
  DRAW_TRANSFORMED(bit->get_bit(), RSW(mx), RSH(my), RSW(al_get_bitmap_width(bit->get_bit())), RSH(al_get_bitmap_height(bit->get_bit())), 0, 0);
  }

}

void Map::DrawFront() {

}


Map::~Map() {
  for (int l=0; l!=3; l+=1) {
    for (int x=0; x!=w; x+=1) {
      free(tiles[l][x]);
    }
    free(tiles[l]);
  }
  free(collision_map);
}



bool LightPoint::InsideScreen() {

}


void LightPoint::Draw() {
  ALLEGRO_BITMAP *bit;
  float w, h;
  bit = lights[type];
  if (type == 0)  w=h=600;
  if (type == 1) w=h=300;
  if (type == 2) w=h=300;
  if (type == 3) w=h=300;
  if (type == 4) w=h=300;
  if (type == 5) w=h=300;
  if (type == 6) w=h=300;
  if (type == 7) w=h=300;
  float rx, ry, rw, rh;
  rx = RSW(x-Camera_X);
  ry = RSH(y-Camera_Y);
  rw = RSW(w);
  rh = RSW(h);
  if ((rx-rw/2 > -rw) && (rx+rw/2 < SCREEN_W+rw) && (ry-rh/2 > -rh) && (ry+rh/2 < SCREEN_H+rh)) {
    DRAW_TRANSFORMED(bit, rx, ry, rw, rh, 0, 0);
  }
}
