/*
	players position is the intersect of an x and y to create 4 quadrants

	quadrant A is the space in negative X and positive Y
	quadrant B is the space in positive X and positive Y
	quadrant C is the space in negative X and negative Y
	quadrant D is the space in positive X and negative Y

	 A | B
	---|---
	 C | D

	commands:
	wpa_showaxis - draws a debug line along x and y axis ( as far as current dist limits )
	wpa_showx - draws a debug line along x axis ( as far as current x axis dist limit )
	wpa_showy - draws a debug line along y axis ( as far as current y axis dist limit )
	wpa_autofill - fills waypoints in all four quadrants
	wpa_filla - fills quadrant A
	wpa_fillb - fills quadrant B
	wpa_fillc - fills quadrant C
	wpa_filld - fills quadrant D
	wpa_filltop - fills quadrants A and B
	wpa_fillbottom - fills quadrants C and D
	wpa_fillleft - fills quadrants A and C
	wpa_fillright - fills quadrants B and D
	wpa_setx - set distance limit for x axis ( no param shows current )
	wpa_sety - set distance limit for y axis ( no param shows current )
	wpa_setceil - set distance limit for positive z axis ( no param shows current )
	wpa_setfloor - set distance limit for negative z axis ( no param shows current )
	wpa_clearlimits - clear all distance limits for x, y, and z axis
	wpa_undo - delete all waypoints in the last array added
	wpa_setspread - sets the distance between waypoints to be added
	wpa_setconndist - sets the limit for connecting waypoints
	wpa_setconnectall - true / false, default false. determines whether or not to connect just the last array 
*/
if ( WPA == null )
{
	global WPA = table();
	
	WPA.EndpointX = null;
	WPA.EndpointY = null;
	WPA.SkipFirst = false;
	WPA.StartX = null;
	WPA.StartY = null;
	WPA.StartZ = null;
	WPA.DistanceX = -1;
	WPA.DistanceY = -1;
	WPA.LimitX = false;
	WPA.LimitY = false;
	WPA.Ceiling = -1;
	WPA.Floor = -1;
	WPA.Spread = 250; // default dist between waypoints
	WPA.ConnHeight = 150; // default height allowed for connecting waypoints
	WPA.ConnDistance = 400; // default distance allowed for connecting waypoints
	WPA.ConnectAll = false; // by default connections will be made to the last array only

	// store the last added array 
	// used to undo ( possibly move in future )
	WPA.LastArray = {};

	// returns table of players position info
	WPA.GetPositionInfo = function()
	{
		t = {};
		t.playerEnt = GetLocalEntity();
		t.playerId = GetGameIdFromEntity(GetLocalEntity());
		t.playerPos = GetEntPosition( t.playerEnt );
		t.eyePos = GetEntEyePosition( t.playerEnt );
		t.endPos = t.eyePos + GetEntFacing( t.playerEnt ) * 50000;

		return t;
	};

	WPA.Connect = function()
	{
		if ( !WPA.ConnectAll ) {
			WPA.ConnectLastArray();
		} else {
			s = "waypoint_autobuild 0 0 " + WPA.ConnHeight + " " + WPA.ConnDistance;
			ExecCommand( s );
		}
	};

	WPA.ConnectLastArray = function()
	{
		if ( tableCount(WPA.LastArray) < 2 ) {
			print( "WPA.ConnectLastArray: not enough waypoints" );
			return;
		}

		foreach ( id in WPA.LastArray )
		{
			source = {}; 
			Wp.GetWaypointByGUID(id, source);

			foreach ( n in WPA.LastArray )
			{
				if ( id == n ) {
					continue;
				}

				dest = {}; 
				Wp.GetWaypointByGUID(n, dest);

				if ( DistanceBetween(source.position, dest.position) > WPA.ConnDistance ) {
					continue;
				}

				height = Abs((source.position.z - dest.position.z));

				if ( WPA.ConnHeight > 0 && height > WPA.ConnHeight ) {
					continue;
				}

				offset = Vector3(0,0,64); // top to bottom for better results
				tr = TraceLine( source.position + offset, dest.position, null, TRACE.ALL, 0, false );

				if ( tr.fraction < 1 ) {
					continue;
				}

				tr = TraceLine( dest.position + offset, source.position, null, TRACE.ALL, 0, false );

				if ( tr.fraction < 1 ) {
					continue;
				}

				Wp.Connect(id, n);
			}
		}
	};

	WPA.CheckX = function(wpPos)
	{
		if ( WPA.DistanceX > -1 && wpPos.x >= WPA.StartX && ((wpPos.x - WPA.StartX) >  WPA.DistanceX) ){
			return false;
		}
		
		if ( WPA.DistanceX > -1 && wpPos.x <= WPA.StartX && ((WPA.StartX - wpPos.x) >  WPA.DistanceX) ){
			return false;
		}

		return true;
	};

	WPA.CheckY = function(wpPos)
	{
		if ( WPA.DistanceY > -1 && wpPos.y >= WPA.StartY && ((wpPos.y - WPA.StartY) > WPA.DistanceY) ){
			return false;
		}

		if ( WPA.DistanceY > -1 && wpPos.y <= WPA.StartY && ((WPA.StartY - wpPos.y) >  WPA.DistanceY) ){
			return false;
		}

		return true;
	};

	WPA.CheckZ = function(wpPos)
	{
		if ( WPA.Ceiling > -1 && wpPos.z >= WPA.StartZ && ((wpPos.z - WPA.StartZ) >  WPA.Ceiling) ){
			return false;
		}

		if ( WPA.Floor > -1 && wpPos.z <= WPA.StartZ && ((WPA.StartZ - wpPos.z) >  WPA.Floor) ){
			return false;
		}

		return true;
	};

	WPA.FillA = function(clearLast)
	{
		if ( clearLast ){
			WPA.LastArray = {};
		}

		pi = WPA.GetPositionInfo();

		trX = TraceLine( pi.eyePos, pi.eyePos + Vector3(-1, 0, 0) * 50000, 0, TRACE.SHOT, pi.playerId, false );
		trY = TraceLine( pi.eyePos, pi.eyePos + Vector3(0, 1, 0) * 50000, 0, TRACE.SHOT, pi.playerId, false );

		if ( !WPA.LimitX ){
			WPA.DistanceX = DistanceBetween( pi.eyePos, trX.end );
		}

		if ( !WPA.LimitY ){
			WPA.DistanceY = DistanceBetween( pi.eyePos, trY.end );
		}

		// do some calcs
		numX = WPA.DistanceX / WPA.Spread;
		numY = WPA.DistanceY / WPA.Spread;

		// add em
		WPA.AddWaypointArray( numX, WPA.Spread, numY, WPA.Spread, -1, 1 );	
	};

	WPA.FillB = function(clearLast)
	{
		if ( clearLast ){
			WPA.LastArray = {};
		}

		pi = WPA.GetPositionInfo();

		trX = TraceLine( pi.eyePos, pi.eyePos + Vector3(1, 0, 0) * 50000, 0, TRACE.SHOT, pi.playerId, false );
		trY = TraceLine( pi.eyePos, pi.eyePos + Vector3(0, 1, 0) * 50000, 0, TRACE.SHOT, pi.playerId, false );	

		if ( !WPA.LimitX ){
			WPA.DistanceX = DistanceBetween( pi.eyePos, trX.end );
		}

		if ( !WPA.LimitY ){
			WPA.DistanceY = DistanceBetween( pi.eyePos, trY.end );
		}

		// do some calcs
		numX = WPA.DistanceX / WPA.Spread;
		numY = WPA.DistanceY / WPA.Spread;

		// add em
		WPA.AddWaypointArray( numX, WPA.Spread, numY, WPA.Spread, 1, 1 );	
	};

	WPA.FillC = function(clearLast)
	{
		if ( clearLast ){
			WPA.LastArray = {};
		}

		pi = WPA.GetPositionInfo();

		trX = TraceLine( pi.eyePos, pi.eyePos + Vector3(-1, 0, 0) * 50000, 0, TRACE.SHOT, pi.playerId, false );
		trY = TraceLine( pi.eyePos, pi.eyePos + Vector3(0, -1, 0) * 50000, 0, TRACE.SHOT, pi.playerId, false );

		if ( !WPA.LimitX ){
			WPA.DistanceX = DistanceBetween( pi.eyePos, trX.end );
		}

		if ( !WPA.LimitY ){
			WPA.DistanceY = DistanceBetween( pi.eyePos, trY.end );
		}

		numX = WPA.DistanceX / WPA.Spread;
		numY = WPA.DistanceY / WPA.Spread;

		WPA.AddWaypointArray( numX, WPA.Spread, numY, WPA.Spread, -1, -1 );
	};

	WPA.FillD = function(clearLast)
	{
		if ( clearLast ){
			WPA.LastArray = {};
		}

		pi = WPA.GetPositionInfo();

		trX = TraceLine( pi.eyePos, pi.eyePos + Vector3(1, 0, 0) * 50000, 0, TRACE.SHOT, pi.playerId, false );
		trY = TraceLine( pi.eyePos, pi.eyePos + Vector3(0, -1, 0) * 50000, 0, TRACE.SHOT, pi.playerId, false );

		if ( !WPA.LimitX ){
			WPA.DistanceX = DistanceBetween( pi.eyePos, trX.end );
		}

		if ( !WPA.LimitY ){
			WPA.DistanceY = DistanceBetween( pi.eyePos, trY.end );
		}

		numX = WPA.DistanceX / WPA.Spread;
		numY = WPA.DistanceY / WPA.Spread;

		WPA.AddWaypointArray( numX, WPA.Spread, numY, WPA.Spread, 1, -1 );
	};

	// Jaskots original code extended a bit 
	WPA.AddWaypointArray = function( numx, distx, numy, disty, x, y )
	{
		print("Util.AddWaypointArray: numx:", numx, "distx:", distx, "numy:", numy, "disty:", disty);
		pi = WPA.GetPositionInfo();
		WPA.StartX = pi.playerPos.x;
		WPA.StartY = pi.playerPos.y;
		WPA.StartZ = pi.playerPos.z;
		
		for ( j = 0 ; j < numy ; j = j + 1 )
		{
			//skip if y invert
			if ( j == 0 && y < 1 && WPA.SkipFirst )
				{ continue; } 	

			for ( i = 0 ; i < numx ; i = i + 1)
			{
				//skip if x invert
				if ( i == 0 && x < 1 && WPA.SkipFirst )
					{ continue; }

				startPos = Vector3( pi.playerPos.x + ( i * distx * x ), 
					pi.playerPos.y + ( j * disty * y), pi.playerPos.z );
				//~ print("Util.AddWaypointArray: startPos:", startPos);
				wpPos = Util.FindGround( startPos );
				//~ print("Util.AddWaypointArray: wpPos:", wpPos);

				if ( wpPos && WPA.CheckX(wpPos) && WPA.CheckY(wpPos) && WPA.CheckZ(wpPos) )
				{
					waypoint = Wp.AddWaypoint( wpPos, Vector3( 0, 0, 0 ) );
					
					// add to a table for undo 
					WPA.LastArray[ waypoint ] = waypoint;
				}
			}
		}
	};

	// COMMANDS
	Commands["wpa_undo"] = 
	{
		Func = function()
		{
			foreach( id in WPA.LastArray ){
				Wp.DeleteWaypoint( id );
			}
		},

		Help = 
		{
			"Removes the last array of waypoints added",
		},
	};

	Commands["wpa_setx"] = 
	{
		Func = function(_params)
		{
			if ( _params[0] ){
				WPA.DistanceX = _params[0];
				WPA.LimitX = true;
				print( "WPA.DistanceX set to ", WPA.DistanceX );
			} else {
				print( "WPA.DistanceX = ", WPA.DistanceX );
			}
		},

		Help = 
		{
			"Set or show current distance limit for X",
		},
	};

	Commands["wpa_sety"] = 
	{
		Func = function(_params)
		{
			if ( _params[0] ){
				WPA.DistanceY = _params[0];
				WPA.LimitY = true;
				print( "WPA.DistanceY set to ", WPA.DistanceY );
			} else {
				print( "WPA.DistanceY = ", WPA.DistanceY );
			}
		},

		Help = 
		{
			"Set or show current distance limit for Y axis",
		},
	};

	Commands["wpa_setfloor"] = 
	{
		Func = function(_params)
		{
			if ( _params[0] ){
				WPA.Floor = _params[0];
				print( "WPA.Floor set to ", WPA.Floor );
			} else {
				print( "WPA.Floor = ", WPA.Floor );
			}
		},

		Help = 
		{
			"Set or show the limit below start position",
		},
	};

	Commands["wpa_setceil"] = 
	{
		Func = function(_params)
		{
			if ( _params[0] ){
				WPA.Ceiling = _params[0];
				print( "WPA.Ceiling set to ", WPA.Ceiling );
			} else {
				print( "WPA.Ceiling = ", WPA.Ceiling );
			}
		},

		Help = 
		{
			"Set or show the limit above start position",
		},
	};

	Commands["wpa_clearlimits"] = 
	{
		Func = function(_params)
		{
			WPA.DistanceX = -1;
			WPA.LimitX = false;
			WPA.DistanceY = -1;
			WPA.LimitY = false;
			WPA.Floor = -1;
			WPA.Ceiling = -1;
		},

		Help = 
		{
			"Clear x y and z distance limits",
		},
	};

	Commands["wpa_showx"] = 
	{
		Func = function()
		{
			pi = WPA.GetPositionInfo();
			d = 50000;
			
			if ( WPA.DistanceX >= 0 ){
				d = WPA.DistanceX;
			}

			DrawDebugLine( Vector3(pi.eyePos[0], pi.eyePos[1], pi.eyePos[2] - 32), pi.eyePos + Vector3(-1, 0, 0) * d, COLOR.GREEN, 10.0 );
			DrawDebugLine( Vector3(pi.eyePos[0], pi.eyePos[1], pi.eyePos[2] - 32), pi.eyePos + Vector3(1, 0, 0) * d, COLOR.GREEN, 10.0 );
		},

		Help = 
		{
			"Draw X alignment",
		},
	};

	Commands["wpa_showy"] = 
	{
		Func = function()
		{
			pi = WPA.GetPositionInfo();
			d = 50000;
			
			if ( WPA.DistanceY >= 0 ){
				d = WPA.DistanceY;
			}

			DrawDebugLine( Vector3(pi.eyePos[0], pi.eyePos[1], pi.eyePos[2] - 32), pi.eyePos + Vector3(0, -1, 0) * d, COLOR.BLUE, 10.0 );
			DrawDebugLine( Vector3(pi.eyePos[0], pi.eyePos[1], pi.eyePos[2] - 32), pi.eyePos + Vector3(0, 1, 0) * d, COLOR.BLUE, 10.0 );
		},

		Help = 
		{
			"Draw Y alignment",
		},
	};

	Commands["wpa_showaxis"] = 
	{
		Func = function()
		{
			pi = WPA.GetPositionInfo();
			x = 50000;
			y = 50000;

			if ( WPA.DistanceX >= 0 ){
				x = WPA.DistanceX;
			}

			if ( WPA.DistanceY >= 0 ){
				y = WPA.DistanceY;
			}

			DrawDebugLine( Vector3(pi.eyePos[0], pi.eyePos[1], pi.eyePos[2] - 32), pi.eyePos + Vector3(-1, 0, 0) * x, COLOR.RED, 30.0 );
			DrawDebugLine( Vector3(pi.eyePos[0], pi.eyePos[1], pi.eyePos[2] - 32), pi.eyePos + Vector3(1, 0, 0) * x, COLOR.GREEN, 30.0 );
			DrawDebugLine( Vector3(pi.eyePos[0], pi.eyePos[1], pi.eyePos[2] - 32), pi.eyePos + Vector3(0, -1, 0) * y, COLOR.RED, 30.0 );
			DrawDebugLine( Vector3(pi.eyePos[0], pi.eyePos[1], pi.eyePos[2] - 32), pi.eyePos + Vector3(0, 1, 0) * y, COLOR.BLUE, 30.0 );

			print( "Green is X, Blue is Y, Red is is inverted");
		},

		Help = 
		{
			"Draw X and Y alignment",
		},
	};

	Commands["wpa_filla"] = 
	{
		Func = function()
		{
			WPA.SkipFirst = false;
			WPA.FillA(true);
			WPA.Connect();
		},

		Help = 
		{
			"fills quadrant A ( negative x, positive y )",
		},
	};

	Commands["wpa_fillb"] = 
	{
		Func = function()
		{
			WPA.SkipFirst = false;
			WPA.FillB(true);
			WPA.Connect();
		},

		Help = 
		{
			"fills quadrant B ( positive x, positive y )",
		},
	};

	Commands["wpa_fillc"] = 
	{
		Func = function()
		{
			WPA.SkipFirst = false;
			WPA.FillC(true);
			WPA.Connect();
		},

		Help = 
		{
			"fills quadrant C ( negative x, negative y )",
		},
	};

	Commands["wpa_filld"] = 
	{
		Func = function()
		{
			WPA.SkipFirst = false;
			WPA.FillD(true);
			WPA.Connect();
		},

		Help = 
		{
			"fills quadrant D ( positive x, negative y )",
		},
	};

	Commands["wpa_filltop"] = 
	{
		Func = function()
		{
			WPA.LastArray = {};
			WPA.SkipFirst = true;
			WPA.FillA(false);
			WPA.FillB(false);
			WPA.Connect();
		},

		Help = 
		{
			"fills quadrants A and B",
		},
	};

	Commands["wpa_fillbottom"] = 
	{
		Func = function()
		{
			WPA.LastArray = {};
			WPA.SkipFirst = true;
			WPA.FillC(false);
			WPA.FillD(false);
			WPA.Connect();
		},

		Help = 
		{
			"fills quadrants C and D",
		},
	};

	Commands["wpa_fillleft"] = 
	{
		Func = function()
		{
			WPA.LastArray = {};
			WPA.SkipFirst = true;
			WPA.FillA(false);
			WPA.FillC(false);
			WPA.Connect();
		},

		Help = 
		{
			"fills quadrants A and C",
		},
	};

	Commands["wpa_fillright"] = 
	{
		Func = function()
		{
			WPA.LastArray = {};
			WPA.SkipFirst = true;
			WPA.FillB(false);
			WPA.FillD(false);
			WPA.Connect();
		},

		Help = 
		{
			"fills quadrants B and D",
		},
	};

	Commands["wpa_autofill"] = 
	{
		Func = function()
		{
			WPA.LastArray = {};
			WPA.SkipFirst = true;
			WPA.FillA(false);
			WPA.FillB(false);
			WPA.FillC(false);
			WPA.FillD(false);
			WPA.Connect();
		},

		Help = 
		{
			"WPA Autofill, fills all 4 quadrants",
		},
	};

	Commands["wpa_setspread"] =
	{
		Func = function(_params)
		{
			if ( !_params[0] || _params[0] < 1 ) {
				print("WPA.Spread is ", WPA.Spread);
				return;
			}

			WPA.Spread = _params[0];
			print("WPA.Spread set to ", WPA.Spread);
		},

		Help =
		{
			"WPA Set Spread, sets the distance between waypoints",
		},
	};

	Commands["wpa_setconndist"] =
	{
		Func = function(_params)
		{
			if ( !_params[0] || _params[0] < 1 ) {
				print("WPA.ConnDistance is ", WPA.ConnDistance);
				return;
			}

			WPA.ConnDistance = _params[0];
			print("WPA.ConnDistance set to ", WPA.ConnDistance);
		},

		Help =
		{
			"WPA Set Connection Distance, sets the max distance for connecting waypoints",
		},
	};

	Commands["wpa_setconnectall"] =
	{
		Func = function(_params)
		{
			if ( typeId(_params[0]) == 0 ) {
				print("WPA.ConnectAll is ", WPA.ConnectAll);
				return;
			}

			WPA.ConnectAll = _params[0];
			print("WPA.ConnectAll set to ", WPA.ConnectAll);
		},

		Help =
		{
			"WPA Set Connect All, sets whether or not to connect all waypoints or just the last array",
		},
	};
}