package commands.general;

import java.util.Random;

import core.Command;
import core.LocStrings;
import dataStructures.KittyChannel;
import dataStructures.KittyGuild;
import dataStructures.KittyRating;
import dataStructures.KittyRole;
import dataStructures.KittyUser;
import dataStructures.Response;
import dataStructures.UserInput;
import utils.OptionParser;

// This command is a port of worldGen command line program by Matthew Cech (https://www.matthewcech.com/)
// Permissions were granted for use and modification in this bot.
public class CommandMap extends Command 
{
	final int MaxWidth = 50;
	final int MaxHeight = 35;
	
	public CommandMap(KittyRole roleLevel, KittyRating contentRating) { super(roleLevel, contentRating); }
	
	@Override
	public String getHelpText() { return String.format(LocStrings.stub("MapInfo"), "" + MaxWidth, "" + MaxHeight); }
	
	@Override
	public void onRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res) 
	{
		// Variables
		int width = 35;
		int height = 25;
		long seed = 0;
		String header = "";
		String body = "";
		Random randGenerator = null;
		
		// Parse out a seed if one was provided
		boolean hasSeed = false;
		if(input.args != null && input.args.length() > 0)
		try
		{
			OptionParser parser = new OptionParser(input.args);
			
			String seedStr = parser.getOption("-s", true);
			if(seedStr != null)
			{
				seed = Long.parseLong(seedStr);
				hasSeed = true;
			}
		
			String widthStr = parser.getOption("-w", true);
			if(widthStr != null)
				width = validateSize(Integer.parseInt(widthStr), MaxWidth);
			
			String heightStr = parser.getOption("-h", true);
			if(heightStr != null)
				height = validateSize(Integer.parseInt(heightStr), MaxHeight);
		}
		catch(NumberFormatException e) 
		{
			res.send(LocStrings.stub("MapInvalid"));
			return;
		} 
		
		// Set up the random generator, creating a seed if needed.
		if(hasSeed)
		{
			randGenerator = new Random(seed);
		}
		else
		{
			seed = new Random().nextLong();
			randGenerator = new Random(seed);
		}
		
		// Response header creation
		header += LocStrings.stub("MapVersion") + "\n";
		header += LocStrings.stub("MapSeed") + ": `" + seed + "`, ";
		header += LocStrings.stub("MapWidth") + "`"+ width +"`, ";
		header += LocStrings.stub("MapHeight") + ": `"+ height + "`\n";
		
		// Response body creation
		body += "```\n";
		body += generateMap(width, height, randGenerator);
		body += "\n```";
		
		// Send back the map
		res.send(header + body);
	}
	
	// Verifies and appropriately caps input as necessary
	int validateSize(int input, int max)
	{
		if(input < 0)
			throw new NumberFormatException();
		
		if(input > max)
			input = max;
		
		return input;
	}
	
	
	// Does the map generation
	String generateMap(int width, int height, Random gen)
	{
		String landscape[][] = new String[width][height];
		int r1 = rand(gen);

		// Variables to determine various feature quantities.
		int LandScarcity = (r1 % 200) + 100; // Higher = more water. 
		int landPasses = 7;                  // Higher = larger land chunks.
		int MountainScarcity = 50;           // Higher = fewer mountains;
		int mountainPasses = r1 % 8 + 2;     // Times to plant mountains.
		int marshPasses = r1 % 3 + 10;       // Times to plant marshes.
		int lavaPasses = 1;                  // Times to plant lava.
		int beachPasses = 1;
		int i, j;
		int sizeX = height;
		int sizeY = width;
		
		// Previously defines
		String LAND = "\u02F6";     // Used to be "
		String WATER = " ";         // Used to be ~ 
		String MOUNTAIN = "\u25B2"; // Used to be ^
		String BEACH = "=";         // Used to be ~
		String MARSH = "\u00A5";    // Used to be ,
		String LAVA = "#";          // Used to be *
		//String SPACE = " ";
		
		////////////////////
		//GENERATION LOOPS//
		////////////////////
		
		// Populate grid with water.
		for(i = 0; i < sizeY; i++)
			for(j = 0; j < sizeX; j++)
				landscape[i][j] = WATER;
			
		// land initial distribution. 
		for(i = 0; i < sizeY; i++)
			for(j = 0; j < sizeX; j++)
			{
				r1 = rand(gen) % LandScarcity;
				if(r1 == 0)
					landscape[i][j] = LAND;
			}
	
		// Land expansion, standard land. 
		while(landPasses-- > 0)
			for(i = 0; i < sizeY; i++)
				for(j = 0; j < sizeX; j++)
					if(landscape[i][j] == LAND)
					{
						if((rand(gen) % 3) == 0)
							expandLand(sizeX, sizeY, landscape, i, j, LAND);
					}
	
		//Beach creation based on land
		while(beachPasses-- > 0)
			for(i = 0; i < sizeY; i++)
				for(j = 0; j < sizeX; j++)
					if(landscape[i][j] == LAND)
					{
						Surround beachQuals = new Surround(new String[]{WATER, WATER, WATER, WATER});
						
						if((rand(gen) % 3) == 0)
							replaceIf(sizeX, sizeY, landscape, beachQuals, i, j, BEACH);
					}
	
		//Mountain creation based on land
		for(i = 0; i < sizeY; i++)
			for(j = 0; j < sizeX; j++)
				if(landscape[i][j] == LAND)
				{
					Surround MountainQuals = new Surround(new String[]{LAND, LAND, LAND, LAND});
					
					if((rand(gen) % MountainScarcity) == 0)
					{
						replaceIf(sizeX, sizeY, landscape, MountainQuals, i, j, MOUNTAIN);
						landscape[i][j] = MOUNTAIN;
					}
				}
	
		//Mountain creation based on Mountain
		while(mountainPasses-- > 0) 
			for(i = 0; i < sizeY; i++)
				for(j = 0; j < sizeX; j++)
					if(landscape[i][j] == LAND)
					{
						Surround MountainQuals = new Surround(new String[]{MOUNTAIN, MOUNTAIN, MOUNTAIN, MOUNTAIN});
						
						int scalar = 0;
						scalar = replaceIf(sizeX, sizeY, landscape, MountainQuals, i, j, MOUNTAIN);
						if(scalar > 0)
							if((rand(gen) % (30 / scalar)) == 0)
								landscape[i][j] = MOUNTAIN;
						
					}
	
		//Marsh creation based on land
		while(marshPasses-- > 0) 
			for(i = 0; i < sizeY; i++)
				for(j = 0; j < sizeX; j++)
					if( landscape[i][j] == LAND)
					{
						Surround ForestQuals = new Surround();
						ForestQuals.left = BEACH;
						ForestQuals.right = BEACH;
						ForestQuals.up = BEACH;
						ForestQuals.down = BEACH;
						
						if((rand(gen) % 80) == 0)
						{
							replaceIf(sizeX, sizeY, landscape, ForestQuals, i, j, MARSH);
							landscape[i][j] = MARSH;
						}
					}  
	
		//Lava creation based on land
		while(lavaPasses-- > 0)
			for(i = 0; i < sizeY; i++)
				for(j = 0; j < sizeX; j++)
					if( landscape[i][j] == MOUNTAIN)
					{
						Surround Mountains = new Surround(new String[]{MOUNTAIN, MOUNTAIN, MOUNTAIN, MOUNTAIN});
						
						if((rand(gen) % 5) == 0)
							if(replaceIf(sizeX, sizeY, landscape, Mountains, i, j, MOUNTAIN) == 4)
								landscape[i][j] = LAVA;
					}
	
		////////////
		//CLEAN UP//
		////////////
		
		//Remove lone islands, with the exception of things on the edge.
		for(i = 0; i < sizeY; i++)
			for(j = 0; j < sizeX; j++)
				{
					Surround water = new Surround(new String[]{WATER, WATER, WATER, WATER});
					if(replaceIf(sizeX, sizeY, landscape, water, i, j, WATER) == 4)
						landscape[i][j] = WATER;
				}
		
		
		
	
		// Build the map
		String output = "";
		for(int y = 0; y < height; ++y)
		{
			if(y != 0)
				output += '\n';
			
			for(int x = 0; x < width; ++x)
				output += landscape[x][y];
		}
		
		// Return the map
		return output;
	}
	
	// Standardized application of rand for the purposes of this generaton
	int rand(Random rand)
	{
		return rand.nextInt();
	}
	
	// Ported from C struct
	class Surround 
	{ 
		public String left;
		public String right; 
		public String up;
		public String down;
		
		public Surround()
		{
			left = null;
			right = null;
			up = null;
			down = null;
		}
		
		public Surround(String[] args)
		{
			if(args.length > 0)
				left = args[0];
			
			if(args.length > 1)
				right = args[1];
			
			if(args.length > 2)
				up = args[2];
			
			if(args.length > 3)
				down = args[3];
		}
	}

	// Expands the current land block
	static void expandLand(int x, int y, String landscape[][], int i, int j, String toAdd)
	{
		// Four checks, one for each block in the 4 cardinal directions.
		if(i - 1 > -1)            
			landscape[i - 1][j] = toAdd;
		
		if(i + 1 < y)
			landscape[i + 1][j] = toAdd;
		
		if(j - 1 > -1)
			landscape[i][j - 1] = toAdd; 
		
		if(j + 1 < x)
			landscape[i][j + 1] = toAdd;  
	}

	// Replaces surrounding stuff
	int replaceIf(int x, int y, String[][] landscape, Surround spaces, int i, int j, String toAdd)
	{
		// Running total of tiles changed.
		int tilesChanged = 0;
		
		// Check square left of me.
		if(spaces.left != null)
			if(j - 1 > -1)
				if(landscape[i][j - 1] == spaces.left)
				{
					landscape[i][j - 1] = toAdd;
					tilesChanged++;
				}
		
		// Check square right of me.
		if(spaces.right != null)
			if(j + 1 < x)
				if(landscape[i][j + 1] == spaces.right)
				{
					landscape[i][j + 1] = toAdd;
					tilesChanged++;
				}
		
		// Check square above me.
		if(spaces.up != null)
			if(i - 1 > -1)
				if(landscape[i - 1][j] == spaces.up)
				{
					landscape[i - 1][j] = toAdd;
					tilesChanged++;
				}
		
		// Check square below me.
		if(spaces.down != null)
			if(i + 1 < y)
				if(landscape[i + 1][j] == spaces.down)
				{
					landscape[i + 1][j] = toAdd;
					tilesChanged++;
				}
		
		return tilesChanged;
	}
}
