//=============================================================================
// MapGarbage.
//=============================================================================
class PathsLinker expands BrushBuilder;

var() bool bLogNewNav, bLogTweakPoint, bGenerateDecoUC, bLogActors, bComputeDistance,
bCheckDuplicates, bFixDuplicates, bLogBrushBuildLag, bTestReach,
bCountReachSpecs;
var() int NumBrush;
//var() XC_Linker XL;
var() bool bDisconnectN1toN2;
var() bool bConnectPathsFor;
var() NavigationPoint N1, N2;
var() int RFlags;
var() float CRadius;
var() float CHeight;
var() bool bModifyReachSpec;
var() int NSpec;

var LevelInfo MyMap;


/*
const R_WALK       = 0x00000001; //walking required
const R_FLY        = 0x00000002; //flying required 
const R_SWIM       = 0x00000004; //swimming required
const R_JUMP       = 0x00000008; //jumping required
const R_DOOR       = 0x00000010;
const R_SPECIAL    = 0x00000020;
const R_PLAYERONLY = 0x00000040;

struct ReachSpec
{
	var() int Distance; 
	var() Actor Start;
	var() Actor End;
	var() int CollisionRadius; 
    var() int CollisionHeight; 
	var() int ReachFlags;
	var() byte bPruned;
};
var() ReachSpec DummyReachSpec;
*/

final function FindLevel()
{
	SetPropertyText("MyMap","MyLevel.LevelInfo0");
	if( MyMap==None )
		SetPropertyText("MyMap","MyLevel.LevelInfo1");
	if( MyMap==None )
		SetPropertyText("MyMap","MyLevel.LevelInfo2");
	if( MyMap==None )
		Warn("Couldn't find levelinfo!");
}

event bool Build()
{
	FindLevel();
	if( MyMap == None )
		GoTo NoMap;
	if (bLogNewNav)
		LogSpawnSelected();
	if (bLogTweakPoint)
		LogVectorialEtc();
	if (bGenerateDecoUC)
		GenerateNonStaticDeco();
	if (bLogActors)
		LogWrappedNames();
	if (bComputeDistance)
		ComputeTheseTwo();
	if (bCheckDuplicates)
		FindCrappedActors();
	if (bFixDuplicates)
		DestroyDupes();
	if (bLogBrushBuildLag)
	{
		if ( NumBrush == 0 )
			return BadParameters("NumBrush parameter should not be 0.");
		FindLaggerBuild();
	}
	if (bTestReach)
		TestTheseTwo();

	if ( bDisconnectN1toN2 )
	{
		RemovePath();
		bDisconnectN1toN2 = False;
	}

	if ( bCountReachSpecs )
		CountReachSpecs();

	if ( bConnectPathsFor )
		GeneratePath(N1,N2);

	if ( bModifyReachSpec )
		ModifySpec();
	GoTo Ending;
NoMap:
	log ("No Map Environment for Operating...",'PathsLinker');
		return BadParameters("Did not find a clean Level...");
Ending:
}

final function ModifySpec()
{
	if ( N1 == None || NSpec == 0 )
	{
		log ("Please select 1 Navigationpoint as N1 and reachSpec index.");
		return;
	}
	log("Reachspec"@NSpec@"will have CollisionRadius ="@CRadius$", CollisionHeight ="@CHeight$", Reach Flags ="@RFlags$".",'PathModifier');
		N1.EditReach(NSpec,,,,CRadius,CHeight,RFlags,False);
}

final function GeneratePath(NavigationPoint N1, NavigationPoint N2)
{
	local int i, j;
	local int RIdx;

	if ( N1 == None || N2 == None || RFlags == 0 || CRadius == 0 || CHeight == 0 )
	{
		log ("Cannot create a null reachSpec, please specify all path parameters.",'SpecGenerator');
		return;
	}
	for ( i = 0; i < 16; i++ )
	{
		if ( N1.Paths[i] > -1 )
			continue;
		else
		{
			N1.Paths[i] = N1.GenReachSpec(N1,N2,VSize(N1.Location-N2.Location),CRadius,CHeight,RFlags,False);
			RIdx = N1.Paths[i];
			log ("Created a reachSpec"@RIdx@"with distance ="@VSize(N1.Location-N2.Location),'ReachSpecCreated');
			log ("               Start ="@N1.Name,'ReachSpecCreated');
			log ("                 End ="@N2.Name,'ReachSpecCreated');
			log ("     CollisionRadius ="@CRadius,'ReachSpecCreated');
			log ("     CollisionHeight ="@CHeight,'ReachSpecCreated');
			log ("Reach Flags required ="@RFlags,'ReachSpecCreated');
			log ("Next upstreamPaths[x] available in"@N2.Name@"will also have this reachSpec added.",'ReachSpecCreated' );
			break;
		}
	}
	for ( j = 0; j < 16; j++ )
	{
		if ( N2.upstreamPaths[j] > -1 )
			continue;
		else
		{
			N2.upstreamPaths[j] = RIdx;
			break;
		}
	}
}

final function CountReachSpecs()
{
	local int I, J;
//	local Actor Start, End;
	local NavigationPoint N;

	foreach MyMap.AllActors(class 'NavigationPoint', N)
	{
		if ( N.Paths[0] > -1 )
			I++;
		if ( N.Paths[1] > -1 )
			I++;
		if ( N.Paths[2] > -1 )
			I++;
		if ( N.Paths[3] > -1 )
			I++;
		if ( N.Paths[4] > -1 )
			I++;
		if ( N.Paths[5] > -1 )
			I++;
		if ( N.Paths[6] > -1 )
			I++;
		if ( N.Paths[7] > -1 )
			I++;
		if ( N.Paths[8] > -1 )
			I++;
		if ( N.Paths[9] > -1 )
			I++;
		if ( N.Paths[10] > -1 )
			I++;
		if ( N.Paths[11] > -1 )
			I++;
		if ( N.Paths[12] > -1 )
			I++;
		if ( N.Paths[13] > -1 )
			I++;
		if ( N.Paths[14] > -1 )
			I++;
		if ( N.Paths[15] > -1 )
			I++;
		if ( N.PrunedPaths[0] > -1 )
			J++;
		if ( N.PrunedPaths[1] > -1 )
			J++;
		if ( N.PrunedPaths[2] > -1 )
			J++;
		if ( N.PrunedPaths[3] > -1 )
			J++;
		if ( N.PrunedPaths[4] > -1 )
			J++;
		if ( N.PrunedPaths[5] > -1 )
			J++;
		if ( N.PrunedPaths[6] > -1 )
			J++;
		if ( N.PrunedPaths[7] > -1 )
			J++;
		if ( N.PrunedPaths[8] > -1 )
			J++;
		if ( N.PrunedPaths[9] > -1 )
			J++;
		if ( N.PrunedPaths[10] > -1 )
			J++;
		if ( N.PrunedPaths[11] > -1 )
			J++;
		if ( N.PrunedPaths[12] > -1 )
			J++;
		if ( N.PrunedPaths[13] > -1 )
			J++;
		if ( N.PrunedPaths[14] > -1 )
			J++;
		if ( N.PrunedPaths[15] > -1 )
			J++;
	}
	log ("This map has"@I@"ReachSpecs shown as direct paths in Navigation Network.",'NumReachSpecs');
	log ("This map has"@J@"ReachSpecs as shortcuts aka PrunedPaths in Navigation Network.",'NumReachSpecs');
	log ("After calculation we have"@I+J@"ReachSpecs.",'NumReachSpecs');
	if ( (I + J) <= 0 )
	{
		log ("Map doesn't include paths?",'NumReachSpecs');
		Goto DoneReach;
	}
	if ( (I + J) > 3000 )
		log ("Technically this map is overloaded and this is not that healthy for A.I.",'NumReachSpecs');
	else
		log ("It looks like map has a normal reachspecs charge for UE1. Greetings !",'NumReachSpecs');
DoneReach:
}

final function RemovePath()
{
	local Actor Start, End;
//	local NavigationPoint N;
	local int distance, reachFlags, i, j, RIdx, RIdx2;
	local bool bFoundPath, bFoundUpStream;

/*
	Finally I got into what was NEEDED for years but no one has bothered with doing it
	This is just removing shit failures by un-referencing evil path as TranslocDest does in games
out of translocator - simple as a pie and very useful.
*/

	if ( N1 == None || N2 == None )
	{
		log ("You need to specify both N1 and N2 Navigation Points having evil path that has to be removed...",'Disconnector');
		log ("You can get them one by one by selecting node1, puting bool bGet1stN1 to True",'LazyHint');
		log ("And then the same with node2 not both in the same time - then using this command",'LazyHint');
		log ("If GUI interface of builder doesn't show anything it needs some clicks on values, for triggering a refresh.",'LazyHint');
		log ("bGet1stN1 will complete selected Node as N1 when is True and button BUILD pushed,",'LazyHint');
		log ("bGet2ndN2 will complete selected Node as N2 when is True and button BUILD pushed.",'LazyHint');
		log ("After completion of Names N1 and N2, value bDisconnectN1toN2 must be True and push BUILD button.",'LazyHint');
		log ("If everything is selected correctly Editor.log will have printed the action performed.",'LazyHint');
		return;
	}
	for ( i=0; i<16; i++ )
	{
		if ( N1.Paths[i] != -1 )
			N1.DescribeSpec( N1.Paths[i], Start, End, reachFlags, distance );
		if ( Start == N1 && End == N2 )
		{
			log ("Found Connection from"@N1.Name@"to"@N2.Name@"with reachFlags ="@reachFlags$", removing ReachSpec reference...",'Disconnector');
			RIdx = N1.Paths[i];
			N1.Paths[i] = -1;
			bFoundPath = True;
			log ("Compacting paths list in"@N1.Name@"if possible...",'Disconnector');
			Start = None;
			End = None;
		}
		if ( bFoundPath )
		{
			if ( i < 15 )
				if ( N1.Paths[i+1] != -1 )
				{
					N1.Paths[i] = N1.Paths[i+1];
					N1.Paths[i+1] = -1;
				}
		}
	}
	Start = None;
	End = None;
	if ( bFoundPath )
	{
		log ("Searching UpstreamPaths reachSpec reference into"@N2.Name$"...",'Disconnector');
		for ( j=0; j<16; j++ )
		{
			if ( N2.UpstreamPaths[j] != -1 )
				N2.DescribeSpec( N2.UpstreamPaths[j], Start, End, reachFlags, distance );
			if ( Start == N1 && End == N2 )
			{
				log ("Found Connection from"@N1.Name@"to"@N2.Name@"with reachFlags ="@reachFlags$", removing ReachSpec reference...",'Disconnector');
				RIdx2 = N2.UpstreamPaths[j];
				N2.UpstreamPaths[j] = -1;
				bFoundUpStream = True;
				log ("Compacting UpstreamPaths list in"@N2.Name@"if possible...",'Disconnector');
				Start = None;
				End = None;
			}
			if ( bFoundUpStream )
			{
				if ( j < 15 )
					if ( N2.UpstreamPaths[j+1] != -1 )
					{
						N2.UpstreamPaths[j] = N2.UpstreamPaths[j+1];
						N2.UpstreamPaths[j+1] = -1;
					}
			}
		}
		if ( !bFoundUpStream )
		{
			log ("There is no Upstream from"@N1.Name@"to"@N2.Name@"described in"@N2.Name$".",'Disconnector');
		}
		if ( RIdx > -1 )
			N1.EditReach(RIdx,,,0,0,0,0,False);
		if ( RIdx2 != RIDx && RIdx2 > -1 )
			N2.EditReach(RIdx2,,,0,0,0,0,False);
	}
	else
		log ("Path from"@N1.Name@"to"@N2.Name@"looks already disconnected or it has been hacked before.",'Disconnector');
	if ( bFoundPath )
		log("Disconnected path going from"@N1.Name@"to"@N2.Name,'Disconnector');
	log("______________________________________________________________________________________________",'Disconnector');
	N1 = None;
	N2 = None;
}

final function TestTheseTwo()
{
	local Actor A, A1, A2;
	local GunlocScout S;

	foreach MyMap.AllActors(class'Actor',A)
	{
		if ( A.bSelected && A1 == None )
		{
			A1 = A;
			continue;
		}
		if ( A.bSelected && A2 == None )
		{
			A2 = A;
			break;
		}
	}
	if ( A1 != None && A2 != None )
	{
		S = MyMap.Spawn(class'GunlocScout',,,A1.Location);
		if (S == None) Goto UnabletoOperate;
		else
		{
			if (S.CheckReachability( A1,A2 ))
			{
				Log ("Route is OK from"@A1.Name@"to"@A2.Name@"having distance"@VSize(A1.Location-A2.Location)@"UU.");
				S.SetLocation(A2.Location);
				if (S.CheckReachability( A2,A1 ))
				{
					Log ("Route is OK from"@A2.Name@"to"@A1.Name@"having distance"@VSize(A2.Location-A1.Location)@"UU.");
				}
				else
				{
					Log ("Route from "$A2.Name$" to "$A1.Name$" doesn't seem reachable.");
					S.bCanFly = True;
					if (S.CheckReachability( A2,A1 ))
						Log("Flight is required for navigating from"@A2.Name@"to"@A1.Name);
				}
			}
			else
			{
				Log ("Route from "$A1.Name$" to "$A2.Name$" doesn't seem reachable.");
				S.bCanFly = True;
				if (S.CheckReachability( A1,A2 ))
					Log("Flight is required for navigating from"@A1.Name@"to"@A2.Name);
				S.bCanFly = False;
				S.SetLocation(A2.Location);
				if (S.CheckReachability( A2,A1 ))
				{
					Log ("Route is OK from"@A2.Name@"to"@A1.Name@"having distance"@VSize(A2.Location-A1.Location)@"UU.");
				}
				else
				{
					S.bCanFly = True;
					if (S.CheckReachability( A2,A1 ))
						Log("Flight is required for navigating from"@A2.Name@"to"@A1.Name);
					else
						Log ("Route from "$A2.Name$" to "$A1.Name$" doesn't seem reachable.");
				}
			}
			S.Destroy();
		}
		Goto DoneHere;
	}
UnabletoOperate:
	log ("Bad Selection... I'm sorry about that...");
DoneHere:
}

/*
final function RemovePath(NavigationPoint NPointOut)
{
//	local class<XC_Engine_Actor> X;
	local XC_Engine_Actor XCG;

	if ( NPOintOut == None )
		return;

	// class<InternetInfo>( DynamicLoadObject("XC_IpServerFix.XC_UdpServerQuery",class'Class') );

//	X = class<XC_Engine_Actor>(DynamicLoadObject("XC_Engine.XC_Engine_Actor",class'Class'));

	Assert( XCG != None);
	XCG.CleanupNavSpecs(NPointOut);
}
*/

final function FindLaggerBuild()
{
	local Brush B;
	local INT i;

	foreach MyMap.AllActors(class 'Brush',B)
	{
		i++;
		if (i == NumBrush)
		{
			log ("Suspect Brush pausing build process ="@B.Name);
			break;
		}
	}
}

final function FindCrappedActors()
{
	local Actor A, A1;
	local Int i, j;

	foreach MyMap.AllActors(class'Actor',A)
	{
		foreach MyMap.AllActors(class'Actor',A1,A.Tag)
		{
			if ( string(A.Name) == string(A1.Name) )
			{
				i++;
			}
		}
		if ( i > 1 )
		{
			j++;
			log (A.Name@"was found"@i@"times.");
		}
		i = 0;
	}
	if ( j > 0 )
		log ("Found"@j@"duplicated Actors.");
	else
		log ("This Level doesn't seems to have duplicated actors at this check.");
}

final function DestroyDupes()
{
	local Actor A, A1;
	local int j, i;

	foreach MyMap.AllActors(class'Actor',A)
	{
		if ( A.Tag == 'Duplicated' )
		{
			A.Destroy();
			j++;
			Continue;
		}
		else
		{
			foreach MyMap.AllActors(class'Actor',A1,A.Tag)
			{
				if ( string(A.Name) == string(A1.Name) )
				{
					i++;
					if ( i > 1 )
					{
						A1.Tag = 'Duplicated';
						i = 0;
					}
				}
			}
			i = 0;
		}
	}
	A = None;
	foreach MyMap.AllActors(class'Actor',A,'Duplicated')
		A.Tag = A.default.Tag;
	if ( j > 0 )
		log ("Removed"@j@"duplicated Actors. Do the check again...");
	else
		log ("There is nothing duplicated for removal.");
}

final function ComputeTheseTwo()
{
	local Actor A, A1, A2;

	foreach MyMap.AllActors(class'Actor',A)
	{
		if ( A.bSelected && A1 == None )
		{
			A1 = A;
			continue;
		}
		if ( A.bSelected && A2 == None )
		{
			A2 = A;
			break;
		}
	}
	if ( A1 != None && A2 != None )
	{
		log ("Distance between"@A1.Name@"and"@A2.Name@"="@VSize(A1.Location-A2.Location)@"UU.");
		A1 = None; A2 = None;
	}
	else
		log("You must have 2 selected actors...");
}

final function LogWrappedNames()
{
	local int I;
	local Actor A;

	foreach MyMap.AllActors(class'Actor',A)
	{
		I++;
		log (A.Name@"is class"@A.Class@"and number"@I@".",'OrderOfActors');
	}
	log ( "Logged "$I$" actors..." );
}

final function LogSpawnSelected()
{
	local Actor A;
	local bool bRotated;

	foreach MyMap.AllActors(class'Actor',A)
	{
		if (A.bSelected)
		{
			if ( A.Rotation.Pitch != 0 )
			{
				log ("NewRot.Pitch="$A.Rotation.Pitch$";",'UcCopy');
				bRotated = True;
			}
			if ( A.Rotation.Roll != 0 )
			{
				log ("NewRot.Roll="$A.Rotation.Roll$";",'UcCopy');
				bRotated = True;
			}
			if ( A.Rotation.Yaw != 0 )
			{
				log ("NewRot.Yaw="$A.Rotation.Yaw$";",'UcCopy');
				bRotated = True;
			}
			if ( bRotated && NavigationPoint(A) == None )
				log ( "Spawn(class'"$A.Tag$"',,,vect("$int(A.Location.X)$","$int(A.Location.Y)$","$int(A.Location.Z)$"),NewRot);",'UcCopy' );
			else
				log ( "Spawn(class'"$A.Tag$"',,,vect("$int(A.Location.X)$","$int(A.Location.Y)$","$int(A.Location.Z)$"));",'UcCopy' );
			bRotated = False;
		}
	}
}

final function LogVectorialEtc()
{
	local Actor A;
	local string str, sto;
	local int I;

	I=0;
	foreach MyMap.AllActors(class'Actor',A)
	{
		I++;
		if (A.bSelected)
		{
			if (A.IsA('NavigationPoint'))
			{
				log ( "foreach NavigationActors(class'"$GetClassName(A)$"',"$Left(GetClassName(A),2)$",30,vect("$int(A.Location.X)$","$int(A.Location.Y)$","$int(A.Location.Z)$"))",'UcCopy');
				log ("{",'UcCopy');
				log ("	if (string("$Left(GetClassName(A),2)$".Name) ~= "$CHR(34)$A.Name$CHR(34)$")",'UcCopy');
				log ("	{",'UcCopy');
				log ("		",'UcCopy');
				log ("		break;",'UcCopy');
				log ("	}",'UcCopy');
				log ("}",'UcCopy');
			}
			else
			{
				log ("	if (string(A.Name) ~= "$CHR(34)$A.Name$CHR(34)$") // Position "$I,'UcCopy');
				log ("	{",'UcCopy');
				log ("		",'UcCopy');
				log ("		continue;",'UcCopy');
				log ("	}",'UcCopy');
			}
			str = "";
			sto = "";
		}
	}
	I = 0;
}

final function GenerateNonStaticDeco()
{
	local Actor A;
//	local string str, sto;
//	local int I;

	foreach MyMap.AllActors(class'Actor',A)
	{
		if (A.bSelected)
		{
			if ( A.IsA('Decoration') )
			{
				log ("class E_"$GetClassName(A)$" expands "$GetClassName(A)$";",'UcCopy');
				log (" ",'UcCopy');
				log ("defaultproperties",'UcCopy');
				log ("{",'UcCopy');
				log ("	bStatic=False",'UcCopy');
				log ("	bNoDelete=False",'UcCopy');
				log ("	bMovable=True",'UcCopy');
				log ("	bGameRelevant=True",'UcCopy');
				log ("	bCollideWorld=False",'UcCopy');
				log ("	bCollideActors=False",'UcCopy');
				log ("	bCollideWhenPlacing=False",'UcCopy');
				log ("	ScaleGlow=240",'UcCopy');
				log ("	RemoteRole=ROLE_None",'UcCopy');
				log ("}",'UcCopy');
			}
		}
	}
}

function String GetClassName(Actor AnActor)
{

	Local string FullName, ClassName, Temp;
	Local int pos, I, J;

	Temp="None";
	if (AnActor == None) return Temp;
	FullName="";
	pos=-1;
	FullName=string(AnActor.Class);
	ClassName=string(AnActor.Class);
//	log (FullName);
	pos = InStr(FullName, ".");
//	log (pos);
	if ( InStr(FullName, ".") > 0 )
	{
		FullName=Left(FullName, InStr(FullName, "."));
		I = Len(FullName);
		J = Len(string(AnActor.Class));
		FullName = Right(ClassName,J-(I+1));
	}
//	log ("GetClassName::"@FullName@"for"@AnActor.Name);
	return FullName;
/*
	if (anActor == None)
		return string(None);
	return string(anActor.Class.Outer);
*/
//	return string(anActor.Class.Outer);
}

defaultproperties
{
	BitmapFilename="PathsLinker"
	ToolTip="Paths Linker"
}
