package com.jmrapp.terralegion.game.io;

import java.io.IOException;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import com.jmrapp.terralegion.game.item.inventory.Inventory;
import com.jmrapp.terralegion.game.utils.JSONConverter;
import com.jmrapp.terralegion.game.world.block.BlockType;
import com.jmrapp.terralegion.game.world.chunk.Chunk;
import com.jmrapp.terralegion.game.world.chunk.ChunkManager;
import com.jmrapp.terralegion.game.world.World;
import com.jmrapp.terralegion.game.world.entity.impl.Player;

import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;

public class WorldIO {

	public static void saveWorld(World world) {
		try {
			String basePath = "worlds/" + world.getWorldName();
			Gdx.files.external(basePath).mkdirs();
			FileHandle handle = Gdx.files.external(basePath + "/world.save");
			handle.file().createNewFile();
			
			String totalToken = world.getSeed() + "\n" + 
								world.getChunkManager().CHUNKS_X + " " + world.getChunkManager().CHUNKS_Y + "\n";
			handle.writeString(totalToken, false);
			
			Chunk[][] chunks = world.getChunkManager().getChunks(); //Get the chunks and go through all
			for (int y = 0; y < world.getChunkManager().CHUNKS_Y; y++) {
				for (int x = 0; x < world.getChunkManager().CHUNKS_X; x++) {
					if (chunks[x][y].isGenerated()) { //If it has been generated then save it
						saveChunk(world.getWorldName(), chunks[x][y]);
					}
				}
			}

			// save player
			savePlayer(world.getPlayer());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public static LoadedWorldInfo loadWorld(World world) {
		String basePath = "worlds/" + world.getWorldName();
		FileHandle baseDir = Gdx.files.external(basePath);
		if (baseDir.exists()) {
			FileHandle worldDef = Gdx.files.external(basePath + "/world.save");
			String[] lines = worldDef.readString().split("\n");
			
			//Load seed
			long seed = Long.parseLong(lines[0]);
			
			//Load Chunk Dimensions
			String[] dim = lines[1].split(" ");
			int worldChunkSizeWidth = Integer.parseInt(dim[0]);
			int worldChunkSizeHeight = Integer.parseInt(dim[1]);

			//Load player
			Player player = loadPlayer();

			LoadedWorldInfo worldInfo = new LoadedWorldInfo(player, seed, worldChunkSizeWidth, worldChunkSizeHeight);
		
			//Go through directory and find all chunks. Load them.
			for (FileHandle handle : baseDir.list()) {
				if (handle.name().startsWith("chunk")) {
					String[] chunkName = handle.name().split("chunk");
					String[] chunkPos = chunkName[1].replace(".save", "").split("_");
					int x = Integer.parseInt(chunkPos[0]);
					int y = Integer.parseInt(chunkPos[1]);
					Chunk chunk = loadChunk(world, handle, x, y);
					world.getChunkManager().setChunk(chunk, x, y);
				}
			}
			return worldInfo;
		}
		return null;
	}
	
	public static void saveChunk(String worldFileName, Chunk chunk) {
		try {
			String basePath = "worlds/" + worldFileName;
			Gdx.files.external(basePath).mkdirs();
			FileHandle handle = Gdx.files.external(basePath + "/chunk" + chunk.getStartX() + "_" + chunk.getStartY() + ".save");
			
			handle.file().createNewFile();
			
			String totalToken = Chunk.CHUNK_SIZE + " " + Chunk.CHUNK_SIZE + "\n"; //First line is the width and the height of the world
			
			for (int y = 0; y < Chunk.CHUNK_SIZE; y++) {
				BlockType lastType = null;
				int sameCount = 0;
				for (int x = 0; x < Chunk.CHUNK_SIZE; x++) {
					BlockType type = chunk.getBlock(x, y);
					
					if (type == null) //In case it is null
						type = BlockType.AIR;
					
					if (lastType == null) //if it is the first instance because only the first run will be null
						lastType = type;
					
					if (type == lastType) { //We're still counting the same blocktype
						sameCount++;
					} else if (x != 0){ //We found a new blocktype. Save in format <#>-<typeId>
						totalToken += lastType.getId() + "-" + sameCount + " ";
						sameCount = 1;
						lastType = type;
					}
				}
				if (sameCount != 0) { //If there are values not saved and still counting
					totalToken += lastType.getId() + "-" + sameCount + "\n";
				} else {
					totalToken += "\n";
				}
			}
			totalToken += "\n"; //separate wall and blocks
			for (int y = 0; y < Chunk.CHUNK_SIZE; y++) {
				BlockType lastType = null;
				int sameCount = 0;
				for (int x = 0; x < Chunk.CHUNK_SIZE; x++) {
					BlockType type = chunk.getWall(x, y);
					
					if (type == null) //In case it is null
						type = BlockType.AIR;
					
					if (lastType == null) //if it is the first instance because only the first run will be null
						lastType = type;
					
					if (type == lastType) { //We're still counting the same blocktype
						sameCount++;
					} else if (x != 0){ //We found a new blocktype. Save in format <#>-<typeId>
						totalToken += lastType.getId() + "-" + sameCount + " ";
						sameCount = 1;
						lastType = type;
					}
				}
				if (sameCount != 0) { //If there are values not saved and still counting
					totalToken += lastType.getId() + "-" + sameCount + "\n";
				} else {
					totalToken += "\n";
				}
			}
			
			handle.writeString(totalToken, false);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public static Chunk loadChunk(World world, FileHandle handle, int x, int y) {
		if (handle.exists()) {
			Chunk chunk = new Chunk(world.getChunkManager(), x, y, y == ChunkManager.CHUNKS_Y - 1); //Create a new chunk
			
			String[] rows = handle.readString().split("\n"); //Get all rows
			
			String[] dimensions = rows[0].split(" "); //Get the dimensions from the first line
			int width = Integer.parseInt(dimensions[0]); //get the width
			int height = Integer.parseInt(dimensions[1]); //get the height
			
			for (int i = 0; i < height; i++) { //Go through each row
				String[] rowData = rows[i + 1].split(" "); //Get each column information. + 1 so that we skip the dimension row
				int xOffset = 0; //The offset from the last data count
				for (int j = 0; j < rowData.length; j++) { //Go through each column entry
					String[] data = rowData[j].split("-");
					int id = Integer.parseInt(data[0]);
					int count = Integer.parseInt(data[1]);
					for (int k = 0; k < count; k++) {
						BlockType type = BlockType.getBlockType(id);
						chunk.setBlock(type, xOffset + k, i, false); //Set the block and call any place events (this will automatically set light values for example)
					}
					xOffset += count;
				}
			}
			
			for (int i = 0; i < height; i++) { //Go through each row
				String[] rowData = rows[i + 2 + height].split(" "); //Get each column information. + 2 + height so that we skip the dimensions row, the space row, and the block rows
				int xOffset = 0; //The offset from the last data count
				for (int j = 0; j < rowData.length; j++) { //Go through each column entry
					String[] data = rowData[j].split("-");
					int id = Integer.parseInt(data[0]);
					int count = Integer.parseInt(data[1]);
					for (int k = 0; k < count; k++) {
						BlockType type = BlockType.getBlockType(id);
						chunk.setWall(type, xOffset + k, i, false); //Set the block and call any place events (this will automatically set light values for example)
					}
					xOffset += count;
				}
			}
			chunk.setGenerated(true);
			return chunk;
		}
		return null; //Chunk file doesn't exist
	}
	
	public static boolean chunkFileExists(String worldFileName, int x, int y) {
		return Gdx.files.external("worlds/" + worldFileName + "/chunk" + x + "_" + y + ".save").exists();
	}

	public static void savePlayer(Player player) {
		// create the JSONObject for the player information
		JSONObject playerInfo = new JSONObject();

		// create a JSONObject for the x and y position of the player
		JSONObject playerPosition = new JSONObject();
		playerPosition.put("x", player.getX());
		playerPosition.put("y", player.getY());

		// add the position to the JSONObject playerInfo
		playerInfo.put("playerPosition", playerPosition);

		// create a JSONArray for the inventory items of the player
		JSONObject playerInventory = JSONConverter.getJSONFromInventory(player.getInventory());

		// add the playerInventory "list" to the JSONObject playerInfo
		playerInfo.put("playerInventory", playerInventory);

		// write to file
		try {
			FileHandle handle = Gdx.files.external("player.save");
			if (!handle.exists()) {
				handle.file().createNewFile();
			}

			handle.writeString(playerInfo.toJSONString(), false);
		} catch(IOException ex) {
			ex.printStackTrace();
		}
	}

	public static Player loadPlayer() {
		try {
			FileHandle handle = Gdx.files.external("player.save");
			if (!handle.exists()) {
				// not saved player yet
				return null;
			}

			String JSONString = handle.readString();

			JSONObject playerInfo = (JSONObject) new JSONParser().parse(JSONString);

			JSONObject playerPosition = (JSONObject) playerInfo.get("playerPosition");
			Player player = new Player(Float.parseFloat(playerPosition.get("x").toString()), Float.parseFloat(playerPosition.get("y").toString()));

			JSONObject jsonInventory = (JSONObject) playerInfo.get("playerInventory");

			Inventory inventory = JSONConverter.getInventoryFromJSON(jsonInventory);

			player.setInventory(inventory);
			return player;
		} catch(ParseException ex) {
			ex.printStackTrace();
		}
		return null;
	}
	
}