#include <iostream.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <math.h>
//#include <chartographer.h>

// these are the divisors in the auto-allocation equations if
// 	blotches and tiles per blotch are on auto mode
#define AUTO_TILES_MODIFIER 20
#define AUTO_BLOTCHES_MODIFIER 150


/*
====================================
Blotch Maker Pro 2.4 - class mode!
====================================

If you want to dink around with blotchmaker, you can change the vars in the
User Vars section below and also change the sample blotchmaking perameters in the
Main() function. The sample vars template is a way to pass a whole great glob of info to
BlotchMaker proper. Unfortunately, BlotchMaker is only a little test bed and a way for me to learn C++.
It does not have any command line perams and the only way to change the perams is to edit this file and recompile.


*/







// NEXT:
// find a way to include template presets on calling BlotchMaker.
//	right now we have only custom
//	a new public chartographer function? --> GetTemplate
// split to headers and code to make a module

// TO DO:
// -------------------------------------------------------
// edge attraction for running tile placement
// random number of tiles per blotch?
// preset user config variables into templates:
//	Curliqueuos (snakes)
//	Pipe Grid
//	Mountain Ranges
//	Swiss Cheese
//	Mold Colonies
// 	Pepper Shot
//	Tunnels (clear-mode)
// 	Custom (user messes with all variables)
// edge turning option (instead of terminating blotch)
// starting corner sizes mapped out
// cut simple path to each homebase
// waypoints and path cutting to each point.
// bailout of FULL blotch_mode if number of attempts is > tile_count * some_number
//	(or get rid of FULL) - could cause infinite loop on maxed out maps
// blotch path thickness
// edge deterioration on path thickness (not all cells padding the core cell are popped in)
//	kinda like blank shooting for each pad tile.
// Box-shaped blotching?
// -------------------------------------------------------





// single tile position structure
struct pos {
	short int x;
	short int y;
	};

// obstacle type - for use with maptiles
enum obstacle {empty=0, block, water, pit, barrier};

// map tile structure
struct maptile {
	enum obstacle obst; //obstacle type
	short int elev; // land elevation
	struct pos pos; // X,Y coords (as backup)
	};

//blotch modes for blotch tile placement
enum blotch_modes {full=0, chaotic=1, x_or=2, clear=3} ;
	// full - places all tiles regardless
	// chaotic - counts all attempts to place tiles as tiles actually placed.
	// x_or - places blocks if tile is empty, otherwise empties it.
	// clear - empties a tile if it contains an obstacle.


// a little data module to fit inside the blotch template
struct blotch_type_perams {
	short int tiles_per_blotch; // zero for AUTO
	float mode_chances[4]; // Mode Persuasion (in parts): Full, Chaotic, X_OR, Clear
	float turn_chances[4]; // Turn Persuasion (in parts): Forward, Left, Right, Back
	short int blank_shot_chance; // Blank Shot (percentage chance of shooting a blank per tile)
	short int path_thickness; // 1 to 10 --- 0 = regular line. 1 = one tile on all sides of core tile. etc.
	short int edge_det; // percent chance of edges of path to "shoot a blank"
	short int edge_grav; // value of the edge attraction. usually 1-5. zero for none;
	enum obstacle blotch_type; // just to call it something later when placing tiles, determining which kind of obstacle to place
	};

// AKA a "blotch template"
struct map_user_vars_package {
	int max_blotches; // max number of blotches per blotch_type
	float type_chances[5]; // empty, block, water, pit, barrier
	// one set of vars for each mode
	struct blotch_type_perams empty;
	struct blotch_type_perams block;
	struct blotch_type_perams water;
	struct blotch_type_perams pit;
	struct blotch_type_perams barrier;
	};


//FUNCTION PROTOTYPES
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
// Non-linear functions:
void ConvertToGoalMarkers (float list[], short int num_list_items);
inline int RoundValue(const float &param);
inline float AbsValue(float num);
inline int AbsValue(int num);
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""










// USER VARIABLES
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
const bool verbose = 0;
// these need to be defined before making a chartographer class
const short int rows_in_map = 40;
const short int cols_in_map = 60;
const enum obstacle map_initializer = empty;
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""









/* MAP INTERFACE (notes) */

class chartographer {
	public:
		chartographer(short int cols, short int rows);
		void InitializeMap(enum obstacle initializer); // you can reinitialize it here, although it's initalized to EMPTY by default constructor
		void BlotchMaker(struct map_user_vars_package the_user_vars); // start making blotches!
		// on above ^ : the "template", whether set or custom, must be passed in from outside.
		// 	In the future, we may want a simplified interface, in which case we would just
		// 	ask for a template name and not give CUSTOM as an option
		struct maptile map[cols_in_map][rows_in_map]; // the map
		short int map_cols;
		short int map_rows;
	private:
		// variables
		struct pos prev_tile;
		struct pos this_tile;
		struct pos next_tile;
		int tile_count; // tiles per blotch, running total
		short int blotch_count; // blotches running total
		short int turn_types[4]; // possibly add 45deg turns in the future
		short int num_turn_types; // simple number to pass to functions
		short int num_blotch_modes; // simple number to pass to functions
		short int num_blotch_types; // simple number to pass to functions
		enum blotch_modes blotch_mode; // current blotch mode in operation
		struct blotch_type_perams *blotch_type; // ptr - current blotch type in operation
		struct map_user_vars_package vars_template; // the template containing the user variables. determine which one to use later
		// Functions: Starting from the top:
		void MakeBlotch ();
			bool DoNextTile ();
				int AdjustForEdgeAttraction (char axis, short int coord);
				int ExtractVector (struct pos prev_tile, struct pos next_tile);
				int GetTurnAngle ();
				bool GetNextTileCoords (short int vector, struct pos &this_tile, struct pos &next_tile);
				bool ApplyTile (struct pos next_tile);
		int chartographer::IsValid (struct pos tile);
	};


/////// MAIN START ///////////////////////////////////////////////////////////////////////////////////////////////////////
int main () {


//Seed the random numbers:
srand(time(NULL));

// make a sample template
/* !!!!!!   USE THIS TO SET VARIABLES !!!!!!
number of blotches to make,
{type chances in lottery balls} ---> empty, block, water, pit, barrier,
foreach type after that:
{tiles per blotch,
{mode persuasion in lottery balls},
	---> Full: all tiles placed - can cause infinite loops if map is maxed out- carefull
	---> Chaotic: not all tiles may be placed (overwrite mode)
	---> X_OR (yes if no, no if yes)
	---> Clear (clears whatever was there - good for cutting paths)
{turn persuasion in lottery balls},
	---> forward
	---> left
	---> right
	---> backward
percentage chance to shoot a blank ( 100 < ),
path thickness (unused),
edge deterioration (unused),
edge attraction for first tile placed (usually about 1-100. more = stronger gravity),
obstacle type (a self reporting variable - don't worry about it)
}

also check the user vars further up the file
*/

map_user_vars_package sample = {
	100,
	{0,10,0,0,0}, // type chances:  empty, block, water, pit, barrier
	// one set of vars for each type
	// EMPTY
	{  1, {0,1,0,0}, {1,1,0,0}, 10, 0, 0, 8, obstacle(0)  },
	// BLOCK
	{  10, {0,1,0,0}, {2,4,2,0}, 0, 0, 0, 0, obstacle(1)  },
	// WATER
	{  50, {0,1,0,0}, {1,1,0,0}, 5, 0, 0, 0, obstacle(2)  },
	// PIT
	{  50, {0,1,0,0}, {1,1,0,0}, 10, 0, 0, 8, obstacle(3)  },
	// BARRIER
	{  50, {0,1,0,0}, {1,1,0,0}, 10, 0, 0, 8, obstacle(4)  }
	};

// creat a chartographer object and start making blotches on it
class chartographer chartographer(cols_in_map, rows_in_map);
chartographer.BlotchMaker(sample);

//print the results
for (int counter = 0; counter < chartographer.map_cols + 2; counter++) {cout << '*';}
cout << '\n';
for (int counter = 0; counter < chartographer.map_rows; counter++ ) {
	cout << '*';
	for (int counterB = 0; counterB < chartographer.map_cols; counterB++ ) {
		if ( chartographer.map[counterB][counter].obst == empty ) {cout << ' ';}
		else if ( chartographer.map[counterB][counter].obst == block ) {cout << 'X';}
		else if ( chartographer.map[counterB][counter].obst == water ) {cout << '~';}
		else if ( chartographer.map[counterB][counter].obst == barrier ) {cout << '+';}
		else if ( chartographer.map[counterB][counter].obst == pit ) {cout << 'u';}
		}
	cout << "*\n";
	}
for (int counter = 0; counter < chartographer.map_cols + 2; counter++) {cout << '*';}
cout << '\n';


return(0);
}
// END OF MAIN //////////////////////////////////////////////////////////////////////////////////////////////////////////////







//////////////////////////////////////////////////////////////////////////////////////////////////////////
chartographer::chartographer(short int cols, short int rows) {
// constructor

// initialize variables:
tile_count = 0;
blotch_count = 0;
map_cols = cols;
map_rows = rows;
turn_types[0] = 0;
turn_types[1] = 270;
turn_types[2] = 90;
turn_types[3] = 180;
num_turn_types = 4;
num_blotch_modes = 4;
num_blotch_types = 5;

// fill in the map --> automatically starts with EMPTY
for (int counter = 0; counter < map_rows; counter++ ) {
	for (int counterB = 0; counterB < map_cols; counterB++ ) {
		map[counterB][counter].obst = empty;
		}
	}

}
//////////////////////////////////////////////////////////////////////////////////////////////////////////




////// DONE ////////////////////////////////////////////////////////////////////////////////////////////////////
void chartographer::InitializeMap(enum obstacle initializer) {
// fill in the map
for (int counter = 0; counter < map_rows; counter++ ) {
	for (int counterB = 0; counterB < map_cols; counterB++ ) {
		map[counterB][counter].obst = initializer;
		}
	}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////



//////////////////////////////////////////////////////////////////////////////////////////////////////////
void chartographer::BlotchMaker(struct map_user_vars_package the_user_vars) {

vars_template = the_user_vars; // puts this in more permanant area as a member var.

// "chances" lists -> send to converter functions (for each blotch_type in the vars template)
ConvertToGoalMarkers( vars_template.type_chances, num_blotch_types );

ConvertToGoalMarkers( vars_template.block.mode_chances, num_blotch_modes );
ConvertToGoalMarkers( vars_template.block.turn_chances, num_turn_types );

ConvertToGoalMarkers( vars_template.empty.mode_chances, num_blotch_modes );
ConvertToGoalMarkers( vars_template.empty.turn_chances, num_turn_types );

ConvertToGoalMarkers( vars_template.barrier.mode_chances, num_blotch_modes );
ConvertToGoalMarkers( vars_template.barrier.turn_chances, num_turn_types );

ConvertToGoalMarkers( vars_template.water.mode_chances, num_blotch_modes );
ConvertToGoalMarkers( vars_template.water.turn_chances, num_turn_types );

ConvertToGoalMarkers( vars_template.pit.mode_chances, num_blotch_modes );
ConvertToGoalMarkers( vars_template.pit.turn_chances, num_turn_types );

// dynamically adjust number of tiles & blotches if they are on auto (zero) mode;
if (vars_template.max_blotches == 0) {
	vars_template.max_blotches = int( (map_cols * map_rows) / AUTO_BLOTCHES_MODIFIER );
	}
if (vars_template.empty.tiles_per_blotch == 0) {
	vars_template.empty.tiles_per_blotch = int( (map_cols * map_rows) / AUTO_TILES_MODIFIER );
	}
if (vars_template.water.tiles_per_blotch == 0) {
	vars_template.water.tiles_per_blotch = int( (map_cols * map_rows) / AUTO_TILES_MODIFIER );
	}
if (vars_template.block.tiles_per_blotch == 0) {
	vars_template.block.tiles_per_blotch = int( (map_cols * map_rows) / AUTO_TILES_MODIFIER );
	}
if (vars_template.pit.tiles_per_blotch == 0) {
	vars_template.pit.tiles_per_blotch = int( (map_cols * map_rows) / AUTO_TILES_MODIFIER );
	}
if (vars_template.barrier.tiles_per_blotch == 0) {
	vars_template.barrier.tiles_per_blotch = int( (map_cols * map_rows) / AUTO_TILES_MODIFIER );
	}


// start making blotches
while(blotch_count < vars_template.max_blotches) {

	// roll for blotch TYPE
	float blotch_type_roll = rand() % 100;
	for (short int counter = 0; counter < num_blotch_types; counter++) {
		// set the blotch_type pointer to the right type stats in the template
		if (  blotch_type_roll <= vars_template.type_chances[counter]  ) {
			switch (counter) {
				case 0: blotch_type = &vars_template.empty; break;
				case 1: blotch_type = &vars_template.block; break;
				case 2: blotch_type = &vars_template.water; break;
				case 3: blotch_type = &vars_template.pit; break;
				case 4: blotch_type = &vars_template.barrier; break;
				}
			break;
			}
		}

	float blotch_mode_roll = rand() % 100;
	for (short int counter = 0; counter < num_blotch_modes; counter++) {
		if (  blotch_mode_roll <= blotch_type->mode_chances[counter]  ) {
			blotch_mode = blotch_modes(counter); //typecasted into blotch_mode type
			break;
			}
		}

	MakeBlotch();
	blotch_count++;
	}

}
///////////////////////////////////////////////////////////////////////////////////////////////////////////



//////////////////////////////////////////////////////////////////////////////////////////////////////////
void chartographer::MakeBlotch () {
// Starts making a blotch.

// pick a place to start, if needed
if (tile_count == 0) {
	struct pos first_tile;
	first_tile.x = int(  rand() % map_cols  );
	first_tile.y = int(  rand() % map_rows  );
	first_tile.x = AdjustForEdgeAttraction('x', int(rand()%map_cols));
	first_tile.y = AdjustForEdgeAttraction('y', int(rand()%map_rows));
	ApplyTile(first_tile);
	if (verbose) {cout << "STARTING AT: " << first_tile.x << ',' << first_tile.y << '\n';}
	// update stats
	prev_tile.x = first_tile.x;
	prev_tile.y = first_tile.y;
	tile_count++;
	}

if (verbose) {cout << "Selected: " <<  blotch_mode << " - [0=Full, 1=Chaotic, 2=X-OR]\n";}

// start placing tiles
while(tile_count < blotch_type->tiles_per_blotch) {
	DoNextTile();
	}

tile_count = 0;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////



//////////////////////////////////////////////////////////////////////////////////////////////////////////
bool chartographer::DoNextTile () {
// Places a tile in a series inside of a blotch
// Returns: 0 if good, 1 if terminated.

// do a random second tile, if needed, to establish a vector
if (tile_count == 1) {
	bool gotcha = 0;
	while (gotcha == 0) {
		short int roll = int(rand()%4);
		switch (roll) {
			case 0:
				this_tile.x = prev_tile.x +1;
				this_tile.y = prev_tile.y;
				break;
			case 1:
				this_tile.x = prev_tile.x -1;
				this_tile.y = prev_tile.y;
				break;
			case 2:
				this_tile.x = prev_tile.x;
				this_tile.y = prev_tile.y +1;
				break;
			case 3:
				this_tile.x = prev_tile.x;
				this_tile.y = prev_tile.y -1;
				break;
			}
		// if it checks out, go with it.
		if (  IsValid(this_tile)  ) {
			ApplyTile(this_tile);
			// update stats
			tile_count++;
			gotcha = 1;
			}
		}
	} // end random second tile

// get a vector
short int vector = ExtractVector(prev_tile, next_tile);
if (verbose) {cout << "From: " << prev_tile.x << ',' << prev_tile.y << "To: " << this_tile.x << ',' << this_tile.y << '\n';}
if (verbose) {cout << "Vector: " << vector << '\n';}

// do a roll to get a turn angle
short int turn_angle = GetTurnAngle();

// find the absolute turn angle to calculate the next tiles position
short int final_vector = (vector + turn_angle) % 360;
if (verbose) {cout << "Final Vector: " << final_vector << '\n';}

// calculate the next tiles position
bool failed = GetNextTileCoords(final_vector, this_tile, next_tile);
// termination signal if bad
if (failed) { return(1); }

if (verbose) {cout << "From: " << this_tile.x << ',' << this_tile.y << "To: " << next_tile.x << ',' << next_tile.y << "\n\n";}

// apply the blotch mode and tile to map
bool do_not_update = ApplyTile(next_tile);

// update stats
if ( do_not_update == false ) {tile_count++;}
prev_tile.x = this_tile.x;
prev_tile.y = this_tile.y;
this_tile.x = next_tile.x;
this_tile.y = next_tile.y;

return 0;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////




//////////////////////////////////////////////////////////////////////////////////////////////////////////
bool chartographer::ApplyTile (struct pos next_tile) {
// Applies the tile to the map in a variety of ways
// Takes: tile to place
// returns1 if do_not_update signal, 0 if not
//	do_not_update will attempt a tile placement "do-over".

// shoot a blank?
if ( rand()%100 < blotch_type->blank_shot_chance ) {return 1;}

else {
	switch(blotch_mode) {
		case full:
			if (map[next_tile.x][next_tile.y].obst == blotch_type->blotch_type) { return 1;}
			else {map[next_tile.x][next_tile.y].obst = blotch_type->blotch_type;}
			break;
		case chaotic:
			map[next_tile.x][next_tile.y].obst = blotch_type->blotch_type;
   			break;
		case x_or:
			if (map[next_tile.x][next_tile.y].obst == blotch_type->blotch_type) { map[next_tile.x][next_tile.y].obst = empty; }
			else {map[next_tile.x][next_tile.y].obst = blotch_type->blotch_type;}
   			break;
		case clear:
			map[next_tile.x][next_tile.y].obst = empty;
   			break;
		default: // defaults to full
			if (map[next_tile.x][next_tile.y].obst == blotch_type->blotch_type) { return 1;}
			else {map[next_tile.x][next_tile.y].obst = blotch_type->blotch_type;}
		}
 	}

//cout << "tile applied at " << next_tile.x << ',' << next_tile.y << "which is a -->  " << blotch_mode << '\n';

return(0);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////



//////////////////////////////////////////////////////////////////////////////////////////////////////////
int chartographer::IsValid (struct pos tile) {
// Returns 1 if a valid map coord is passed, zero if not.
// takes: a tile pos. struct (tile x,y coords)
if (  (tile.x < 0) || (tile.x > (map_cols - 1))  )  {return 0;}
else if (  (tile.y < 0) || (tile.y > (map_rows - 1))  )  {return 0;}
return 1;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////



/////////////////////////////////////////////////////////////////////////////////////////////////////////
int chartographer::ExtractVector (struct pos prev_tile, struct pos next_tile) {
// Gets a vector based on the current tile and the last tile placed.
// Takes: the previous tile position, the current tile position
//             0 (F)
//                |
// 270 (L) ---+--- 90 (R)
//                |
//           180 (B)
short int delta_x = prev_tile.x - this_tile.x;
short int delta_y = prev_tile.y - this_tile.y;
if (delta_x == -1) { return 90;}
else if (delta_x == 1) { return 270;}
else if (delta_y == -1) { return 0;}
else if (delta_y == 1) { return 180;}
return(0);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////



/////////////////////////////////////////////////////////////////////////////////////////////////////////
int chartographer::GetTurnAngle () {
// Gets a randomly generated turn, based on turn persuasions.
//
float roll = rand()%100;
for (short int x = 0; x < num_turn_types; x++) {
	if (  roll <= blotch_type->turn_chances[x]  ) {
		if (verbose) {cout << "Turn angle: " << turn_types[x] << '\n';}
		return (turn_types[x]);
		}
	}
	return (0);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////



/////////////////////////////////////////////////////////////////////////////////////////////////////////
bool chartographer::GetNextTileCoords (short int vector, struct pos &this_tile, struct pos &next_tile) {
// gets the coordinates of a tile using a final vector (abs turn angle) and the previous tile
// modifies "next_tile" instead of returning it.
// takes:
//	the vector (turn angle),
//	ref to the current tile
//	ref to the next_tile
// returns: 0 if good, 1 if failed and terminate blotch
switch (vector) {
	case 0:
		next_tile.x = this_tile.x;
		next_tile.y = this_tile.y + 1;
		break;
	case 90:
		next_tile.x = this_tile.x + 1;
		next_tile.y = this_tile.y;
		break;
	case 180:
		next_tile.x = this_tile.x;
		next_tile.y = this_tile.y - 1;
		break;
	case 270:
		next_tile.x = this_tile.x - 1;
		next_tile.y = this_tile.y;
		break;
	}
// check for validity
if (  IsValid(next_tile)  ) {return 0;}
// otherwise [pick a new direction? or] TERMINATE BLOTCH
else {
	tile_count = blotch_type->tiles_per_blotch; // max out counter
 	return(1); // termination signal
	}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////



//////////////////////////////////////////////////////////////////////////////////////////////////////////
void ConvertToGoalMarkers (float list[], short int num_list_items) {
// converts lists of random-chance parts into a list of % goal markers for if statement decisions
// takes:
//	a list ref
//	number of items in list
// returns: nothing (just transforms list)

short int counter = 0;
float max_chances = 0;
float running_total = 0;

//get total
for (counter = 0; counter < num_list_items; counter++) {
	max_chances += list[counter];
	}
//convert to percentages
for (counter = 0; counter < num_list_items; counter++) {
	list[counter] = ( list[counter] / max_chances ) * 100;
	}
//convert to goal markers
for (counter = 0; counter < num_list_items; counter++) {
	float was = list[counter];
	list[counter] += running_total;
	running_total += was;
	}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////



//////////////////////////////////////////////////////////////////////////////////////////////////////////
int chartographer::AdjustForEdgeAttraction (char axis, short int coord) {
// Adjusts the first tile placed by the gravity of the edge in that particular blotch type
// Takes:
//	the axis: (char) X or Y
//	the current position on that axis (say, "32"...)
// Returns: the coord of where you ought to place the tile.

//find the center
float center;
if (axis == 'X' || axis == 'x') {center = (map_cols-1) / 2.0;} else {center = (map_rows-1) / 2.0;} // minus one because this is for the array index which starts at zero!!!

//find the distance from the edge
float edge_dist = center - AbsValue(coord - center);
if (edge_dist == 0) {edge_dist = 1;} // fix for possible divide by zero

//apply the formula
float attr = (blotch_type->edge_grav * center * (1 / edge_dist ) ) - blotch_type->edge_grav;

//get the new tile on the axis to move to
short int ans;
if (coord < center)
	ans = coord - RoundValue(attr);
	else
	ans = coord + RoundValue(attr);

// check for out-of-bounds
if (ans < 0) {ans = 0;}
if (ans > (center*2)) {ans = int(center*2);}

return(ans);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////





inline int RoundValue(const float &param) {
	if((param - floor(param)) < 0.5f)
		return int(floor(param));
	else
		return int(ceil(param));
	}


inline float AbsValue(float num){
	if (num >= 0) return(num);
	else return(  num - num - num  );
	}
inline int AbsValue(int num){
	if (num >= 0) return(num);
	else return(  num - num - num  );
	}


