import Tile, { TileType } from "../entities/Tile";
import Graphics from "../assets/Graphics";
import Player from "../entities/Player";
import Professor from "../entities/Professor";
import Julina from "../entities/Julina";
import Item from "../entities/Item";
import slime from "../entities/enemies/slime";

export default class Map {
  public readonly tiles: Array<Array<Tile>>;
  public readonly width: number;
  public readonly height: number;
  public readonly tilemap: Phaser.Tilemaps.Tilemap;
  public readonly wallLayer: Phaser.Tilemaps.StaticTilemapLayer;

  public readonly startingX: number;
  public readonly startingY: number;
  
  constructor(scene: Phaser.Scene) {
    function toHexString(data) {
      var ret = "";
      for (var i = 0; i < 3; ++i) {
        var byte = data[i];
        ret += ('0' + (byte & 0xFF).toString(16)).slice(-2);
      }
      return ret;
    }
    
    var level = scene.textures.get('Level').source[0].image;
    var canvas = document.getElementById('map');
    var context = canvas.getContext('2d');
      
    context.canvas.width  = level.width;
    context.canvas.height = level.height;
    context.drawImage(level, 0, 0);
    
    this.width = level.width;
    this.height = level.height;

    window.enemy1 = [];
    var enemy1pos = [];
    var enemy2pos = [];
    
    var wall = toHexString([0, 0, 0]);
    var empty = toHexString([255, 255, 255]);
    var spawn = toHexString([163, 73, 164]);
    var enemy1 = toHexString([34, 177, 76]);
	var enemy2 = toHexString([64, 0, 0]);
    var door = toHexString([185, 122, 87]);
    var nextLevel = toHexString([255, 242, 0]);
    var lastLevel = toHexString([239, 228, 176]);
	window.nextLevel = nextLevel;
	window.lastLevel = lastLevel;
	var julina = toHexString([237, 28, 36]);
	var veins = toHexString([63, 72, 204]);
	var ateries = toHexString([255, 174, 201]);
	var heart = toHexString([255, 127, 39]);
	var bottleBlood = toHexString([181, 230, 29]);
	var lung = toHexString([0, 162, 232]);
	var professor = toHexString([136, 0, 21]);
	var barrel = toHexString([112, 146, 190]);
	var crate = toHexString([200, 191, 231]);

    this.tiles = [];
      
    for (let y = 0; y < this.height; y++) {
      this.tiles.push([]);
      for (let x = 0; x < this.width; x++) {
        var tilecolor = toHexString(context.getImageData(x, y, 1, 1).data);

        switch (tilecolor) {
          case wall:
            this.tiles[y][x] = new Tile(TileType.Wall, x, y, this);
            break;

          case door:
            this.tiles[y][x] = new Tile(TileType.Door, x, y, this);
            break;

          case enemy1:
            enemy1pos.push([x, y]);
            this.tiles[y][x] = new Tile(TileType.None, x, y, this);
            break;

          case enemy2: // Zombie
            enemy2pos.push([x, y]);
            this.tiles[y][x] = new Tile(TileType.None, x, y, this);
            break;

          case empty:
		  	this.tiles[y][x] = new Tile(TileType.None, x, y, this);
			break;
			
		  case barrel:
		  case crate:
			this.tiles[y][x] = new Tile(TileType.None, x, y, this);
			break;
			
		  case bottleBlood:
			this.tiles[y][x] = new Tile(TileType.None, x, y, this);
			this.bottleBloodStartingX = x;
			this.bottleBloodStartingY = y;
			break;
			
		  case veins:
			this.tiles[y][x] = new Tile(TileType.None, x, y, this);
			this.veinsStartingX = x;
			this.veinsStartingY = y;
			break;
			
		  case ateries:
			this.tiles[y][x] = new Tile(TileType.None, x, y, this);
			this.ateriesStartingX = x;
			this.ateriesStartingY = y;
			break;
			
		  case heart:
			this.tiles[y][x] = new Tile(TileType.None, x, y, this);
			this.heartStartingX = x;
			this.heartStartingY = y;
			break;
			
		  case lung:
		  	this.lungStartingX = x;
			this.lungStartingY = y;
			this.tiles[y][x] = new Tile(TileType.None, x, y, this);
			break;
			
		  case julina:
			this.tiles[y][x] = new Tile(TileType.None, x, y, this);
			this.julinaStartingX = x;
			this.julinaStartingY = y;
			break;
			
		  case professor:
			this.tiles[y][x] = new Tile(TileType.None, x, y, this);
			this.profStartingX = x;
			this.profStartingY = y;
			break;
			
          case nextLevel:
			this.tiles[y][x] = new Tile(TileType.Door, x, y, this);
			if (localStorage.direction === 'backward') {
				this.startingX = x;
				this.startingY = y;
			}
			break;
			
		  case lastLevel:
            this.tiles[y][x] = new Tile(TileType.Door, x, y, this);
			if (localStorage.direction === 'forward') {
				this.startingX = x;
				this.startingY = y;
			}
            break;

          case spawn:
            this.tiles[y][x] = new Tile(TileType.None, x, y, this);
			this.spawnStartingX = x;
			this.spawnStartingY = y;
            break;

          default:
            console.error("unknown color at x: " + x + " y: " + y);
            this.tiles[y][x] = new Tile(TileType.None, x, y, this);
        }
		this.tiles[y][x].tilecolor = tilecolor;
      }
    }

    const toReset = [];
    for (let y = 0; y < this.height; y++) {
      for (let x = 0; x < this.width; x++) {
        const tile = this.tiles[y][x];
        if (tile.type === TileType.Wall && tile.isEnclosed()) {
          toReset.push({ y: y, x: x });
        }
      }
    }

    toReset.forEach(d => {
      this.tiles[d.y][d.x] = new Tile(TileType.None, d.x, d.y, this);
    });

    this.tilemap = scene.make.tilemap({
      tileWidth: Graphics.environment.width,
      tileHeight: Graphics.environment.height,
      width: this.width,
      height: this.height
    });

    const dungeonTiles = this.tilemap.addTilesetImage(
      Graphics.environment.name
    );

    const groundLayer = this.tilemap
      .createBlankDynamicLayer("Ground", dungeonTiles, 0, 0)
      .randomize(
        0,
        0,
        this.width,
        this.height,
        Graphics.environment.indices.floor.outer
      );
    const wallLayer = this.tilemap.createBlankDynamicLayer(
      "Wall",
      dungeonTiles,
      0,
      0
    );
	const doorLayerDown = this.tilemap.createBlankDynamicLayer(
      "DoorDown",
      dungeonTiles,
      0,
      0
    );
	const doorLayerUp = this.tilemap.createBlankDynamicLayer(
      "DoorUp",
      dungeonTiles,
      0,
      0
    );
	this.doorLayerDown = doorLayerDown;
	doorLayerDown.depth = 9999999998;
	this.doorLayerUp = doorLayerUp;
	doorLayerUp.depth = 1;

    for (let x = 0; x < this.width; x++) {
      for (let y = 0; y < this.height; y++) {
		const tile = this.tiles[y][x];
        if (tile.type === TileType.Wall) {
			wallLayer.putTileAt(tile.wallIndex(), x, y);
        } else if (tile.type === TileType.Door) {
			if (tile.tilecolor === lastLevel) {
				wallLayer.putTileAt(0x7e, x, y);
			} else if (tile.tilecolor === nextLevel) {
				wallLayer.putTileAt(0x7d, x, y);
				tile.collides = false;
			} else {
				var index = tile.doorIndex();
				wallLayer.putTileAt(index, x, y);
			}
		}
      }
    }
      
    wallLayer.setCollisionBetween(0, 256);
    this.tilemap.convertLayerToStatic(groundLayer);
    this.wallLayer = wallLayer;
	wallLayer.depth = 1;
    
	if (!this.startingX) this.startingX = this.spawnStartingX;
	if (!this.startingY) this.startingY = this.spawnStartingY;
	
	if (this.tiles[this.startingY][this.startingX].type !== TileType.None) {
		if (this.tiles[this.startingY-1][this.startingX].type === TileType.None) {
			this.startingY -= 1;
		}
		else if (this.tiles[this.startingY+1][this.startingX].type === TileType.None) {
			this.startingY += 1;
		}
		else if (this.tiles[this.startingY][this.startingX-1].type === TileType.None) {
			this.startingX -= 1;
		}
		else if (this.tiles[this.startingY][this.startingX-1].type === TileType.None) {
			this.startingX += 1;
		}
	}
	
    var player = new Player(
      this.tilemap.tileToWorldX(this.startingX) + 8,
      this.tilemap.tileToWorldY(this.startingY),
      scene
    );
	
	if (this.profStartingX && this.profStartingY) {
		var prof = new Professor(
			this.tilemap.tileToWorldX(this.profStartingX)+4,
			this.tilemap.tileToWorldX(this.profStartingY)-2,
			scene,
			player
		);
		window.professor = prof;
	}
	
	if (this.julinaStartingX && this.julinaStartingY) {
		var julina = new Julina(
			this.tilemap.tileToWorldX(this.julinaStartingX)+12,
			this.tilemap.tileToWorldX(this.julinaStartingY)-2,
			scene,
			player
		);
		window.julina = julina;
	}
	
	if (this.heartStartingX && this.heartStartingY && window.inventory.indexOf('Heart') === -1) {
		var heart = new Item(
			this.tilemap.tileToWorldX(this.heartStartingX)+8,
			this.tilemap.tileToWorldX(this.heartStartingY)+8,
			scene,
			player,
			'heart',
			'Heart'
		);
		window.heart = heart;
	}
	
	if (this.lungStartingX && this.lungStartingY && window.inventory.indexOf('Lung') === -1) {
		var heart = new Item(
			this.tilemap.tileToWorldX(this.lungStartingX)+8,
			this.tilemap.tileToWorldX(this.lungStartingY)+8,
			scene,
			player,
			'lung',
			'Lung'
		);
		window.lung = lung;
	}
	
	if (this.bottleBloodStartingX && this.bottleBloodStartingY) {
		var bottleBlood = new Item(
			this.tilemap.tileToWorldX(this.bottleBloodStartingX)+8,
			this.tilemap.tileToWorldX(this.bottleBloodStartingY)+8,
			scene,
			player,
			'bloodShrine',
			'bloodShrine'
		);
		window.bottleBlood = bottleBlood;
	}
	
	if (this.ateriesStartingX && this.ateriesStartingY && window.inventory.indexOf('Arteries') === -1) {
		var ateries = new Item(
			this.tilemap.tileToWorldX(this.ateriesStartingX)+8,
			this.tilemap.tileToWorldX(this.ateriesStartingY)+8,
			scene,
			player,
			'ateries',
			'Arteries'
		);
		window.ateries = ateries;
	}
	
	if (this.veinsStartingX && this.veinsStartingY && window.inventory.indexOf('Veins') === -1) {
		var veins = new Item(
			this.tilemap.tileToWorldX(this.veinsStartingX)+8,
			this.tilemap.tileToWorldX(this.veinsStartingY)+8,
			scene,
			player,
			'veins',
			'Veins'
		);
		window.veins = veins;
	}
      
    window.player = player;
    
	window.slimeId = 1;
    for (var i = 0; i < enemy1pos.length; ++i) {
      var s = new slime(
        this.tilemap.tileToWorldX(enemy1pos[i][0])+8,
        this.tilemap.tileToWorldY(enemy1pos[i][1])+8,
        scene,
        player
      );
	  var prefix = "" + window.level + "_" + s.slimeId + "_";
	  if (localStorage[prefix  + "hp"]) {
		  s.hp = +localStorage[prefix  + "hp"];
		  s.sprite.x = +localStorage[prefix  + "x"];
		  s.sprite.y = +localStorage[prefix  + "y"];
		  if (s.hp <= 0) {
			  s.alive = false;
			  s.sprite.anims.play(Graphics.slime.animations.alreadyDeath.name);
			  s.body.setVelocity(0);
			  s.body.immovable = true;
			  s.sprite.depth = 2;
			  s.body.checkCollision.none = true;
		  }
	  }
      window.enemy1.push(s);
    }
	
    for (var i = 0; i < enemy2pos.length; ++i) {
      var s = new slime(
        this.tilemap.tileToWorldX(enemy2pos[i][0])+8,
        this.tilemap.tileToWorldY(enemy2pos[i][1])+8,
        scene,
        player
      );
	  var prefix = "" + window.level + "_" + s.slimeId + "_";
	  if (localStorage[prefix  + "hp"]) {
		  s.hp = +localStorage[prefix  + "hp"];
		  s.sprite.x = +localStorage[prefix  + "x"];
		  s.sprite.y = +localStorage[prefix  + "y"];
		  if (s.hp <= 0) {
			  s.alive = false;
			  s.sprite.anims.play(Graphics.slime.animations.alreadyDeath.name);
			  s.body.setVelocity(0);
			  s.body.immovable = true;
			  s.sprite.depth = 2;
			  s.body.checkCollision.none = true;
		  }
	  }
      window.enemy1.push(s);
    }
	
	if (localStorage['openDoors' + window.level]) {
		var openDoors = JSON.parse(localStorage['openDoors' + window.level]);
		for (var i = 0; i < openDoors.length; ++i) {
			var cur = openDoors[i];
			var wall = this.wallLayer.getTileAt(cur[0], cur[1]);
			var tile = this.tiles[wall.y][wall.x];
			window.openDoor(tile, wall, this);
		}
	}
  }

  tileAt(x: number, y: number): Tile | null {
    if (y < 0 || y >= this.height || x < 0 || x >= this.width) {
      return null;
    }
    return this.tiles[y][x];
  }
}
