this.GoalType = "BUILD";
this.Version = 1;
this.DefaultPriority = 0.9;
this.CreateOnLoad = false;
this.RolePriorityBonus = 0.1;

//////////////////////////////////////////////////////////////////////////

this.Schema = Schema();
this.Schema.Stance = Validate.Enum("stand","crouch","prone","random","jump").Default("crouch");
this.Schema.IgnoreTargets = Validate.IntRange(0,1).Default(0);
this.Schema.Wait = Validate.FloatRange(-1.0, 100.0).Default(-1.0);

//////////////////////////////////////////////////////////////////////////

this.InitNewGoal = function()
{
	this.TraceInfo = {};
	this.TraceEntity = null;
	this.CreateOnLoad = true; // user created goals need this
	this.Facing = GetLocalFacing();
	this.Offsets = {};

	this.Schema.Check(this);
};

//////////////////////////////////////////////////////////////////////////

this.UpgradeVersion = function(Props)
{
	// version 0 is string,string key/values from the old waypoint format
	if(Props.Version==0)
	{
		// Version 1 Schema, upgrade from waypoint format
		this.Schema.Check(Props);
		Props.Version = 1;
		Props.Mobile = ToBool(Props.Mobile,false);
		Props.IgnoreTargets = ToBool(Props.IgnoreTargets,false);
	}
	else if(Props.Version == this.Version)
	{
		this.Schema.Check(Props);

		// copy all the fields
		this.Stance = Props.Stance;
		this.Mobile = Props.Mobile;
		this.IgnoreTargets = Props.IgnoreTargets;
		this.Vehicle = Props.Vehicle;
		this.Wait = Props.Wait;
		if(IsInt(this.Wait)) { this.Wait = ToFloat(this.Wait); }

		if(this.Mobile == true)
		{
			this.DynamicPosition = true;
			this.DynamicOrientation = true;
		}

		if (Props.Offsets)
		{
			this.Offsets = Props.Offsets;
		}
		else
		{
			this.Offsets = table();
		}

		this.TraceInfo = Props.TraceInfo;
		this.IgnoreEntity = Props.IgnoreEntity;
		this.Facing = Props.Facing;

		if(!this.Schema.Check(this))
		{
			this.DisableGoal(true);
		}
	}
};

//////////////////////////////////////////////////////////////////////////

this.Render = function()
{
	if (this.TraceEntity && EntityIsValid(this.TraceEntity))
	{
		tracePos = GetEntPosition(this.TraceEntity);
		goalPos = this.GetPosition();
		bbEnt = this.TraceEntity;
	}

	if ( this.Vehicle )
	{
		vehGoal = GetGoal(this.Vehicle);
		if ( vehGoal )
		{
			vehEnt = vehGoal.GetEntity();
		}
	}

	while(this.RenderGoal == true)
	{
		goalName = this.GetName();
		if(this.ExtraDebugText==null)
		{
			this.ExtraDebugText = "";
			if (this.TraceEntity)
			{
				this.ExtraDebugText += format("TraceEnt: %d\n",this.TraceEntity);
			}

			this.ExtraDebugText += format("Stance: %s\n",this.Stance);

			if(this.Mobile)
			{
				this.ExtraDebugText += "Mobile\n";
			}
			else
			{
				this.ExtraDebugText += "Static \n";
			}

			if(this.IgnoreTargets)
			{
				this.ExtraDebugText += "IgnoreTargets \n";
			}

			if (this.IgnoreEntity)
			{
				this.ExtraDebugText += "IgnoreEntity\n";
			}

			if (this.Vehicle) {
				this.ExtraDebugText += format("Vehicle: %s\n",this.Vehicle);
			}

			if (this.Wait >= 0) {
				this.ExtraDebugText += "Wait: " + this.Wait + "\n";
			}
		}

		// Render the built-in debug info.
		this.RenderDefault();

		if (tracePos && goalPos)
		{
			DrawArrow(goalPos,tracePos,COLOR.YELLOW,2);
		}
		else if (this.TraceEntity && EntityIsValid(this.TraceEntity))
		{
			tracePos = GetEntPosition(this.TraceEntity);
			goalPos = this.GetPosition();
			bbEnt = this.TraceEntity;
		}

		if (bbEnt)
		{
			DrawEntityOBB( bbEnt, 20, COLOR.GREEN );
		}

		if ( this.Facing && this.IgnoreEntity)
		{
			goalBasePos = this.GetPosition();
			goalPos = goalBasePos + Vec3(0,0,64);
			DrawLine(goalPos,goalBasePos,COLOR.YELLOW,2);
			DrawArrow(goalPos,goalPos+this.Facing*32,COLOR.BLUE,2);
		}

		if ( this.Vehicle && this.Offsets )
		{
			if ( !vehGoal || !vehEnt )
			{
				vehGoal = GetGoal(this.Vehicle);
				if ( vehGoal )
				{
					vehEnt = vehGoal.GetEntity();
				}
			}

			if ( vehEnt && EntityIsValid(vehEnt) )
			{
				foreach ( id and offset in this.Offsets )
				{
					// if it's excluded, draw it red
					if ( this.ExcludeIndexes && typeId(Util.GetTableKey(this.ExcludeIndexes, id)) != 0 ) {
						clr = COLOR.RED;
					}
					else if ( this.StartIndex == id && typeId(this.StartIndex) != 0 ) {
						clr = COLOR.GREEN;
					}
					else {
						clr = COLOR.BLUE;
					}

					buildPos = GetEntityWorldSpace( vehEnt, offset );
					//buildPos = GroundPoint(buildPos);
					DrawLine(buildPos,buildPos+Vector3(0,0,64),clr,2);
					DrawText3d(buildPos+Vector3(0,0,64),goalName + " offset " + ToString(id),COLOR.WHITE,2,textDrawRadius);
				}
			}
		}

		sleep(2);
	}
};

//////////////////////////////////////////////////////////////////////////

this.SaveToTable = function(SaveTable)
{
	// save the goal specific properties
	if(this.Mobile){ SaveTable.Mobile = this.Mobile; }
	if(this.IgnoreTargets){ SaveTable.IgnoreTargets = this.IgnoreTargets; }
	SaveTable.TraceInfo = this.TraceInfo;
	if (this.Stance!="crouch"){ SaveTable.Stance = this.Stance; }
	SaveTable.Vehicle = this.Vehicle;
	if (tableCount(this.Offsets) > 0) { SaveTable.Offsets = this.Offsets; }
	SaveTable.IgnoreEntity = this.IgnoreEntity;
	if (this.IgnoreEntity) { SaveTable.Facing = this.Facing; }
	if (this.Wait >= 0) { SaveTable.Wait = this.Wait; }
};

//////////////////////////////////////////////////////////////////////////

this.Help = function()
{
	print(this.GoalType,"goal, version",this.Version);
	print("Requires:");
	print("     trace - the traceinfo of the entity to be built,");
	print("             this MUST be an OID entity, use /oidlist to confirm");
	print("  or");
	print("     ignoreentity - goal will be enabled/disabled in map script");
	print("     facing - direction the bot should face");
	print("Optional:");
	print("     stance - stand, crouch, prone, random, jump");
	print("     offset - only use for movable build goals");
	print("     vehicle - this must be set if using offsets");
	print("     wait - delay after goal is completed");
};

//////////////////////////////////////////////////////////////////////////

this.SetProperty = function(property, value)
{
	switch(property)
	{
		case "stance": // stand, crouch, prone, random, jump, default crouch
		{
			if(this.Schema.Stance.CheckPrintErrors(this,value))
			{
				this.Stance = value;
			}
		}
		case "trace":
		{
			if ( value == "clearall" || value == "clear" )
			{
				this.TraceInfo = null;
				this.TraceEntity = null;
			}
			else
			{
				targetinfo = Util.ShowEntityInfo(TRACE.ALL);
				if (tableCount(targetinfo) > 0 && targetinfo.id && GetEntClass(targetinfo.id) == CLASS.GOAL)
				{
					// store the trace positions. this is so we can handle ETPro map scripts
					// since adding ents with the 'create' command makes ent numbers unreliable
					this.TraceInfo = targetinfo;
					this.IgnoreEntity = null;
				}
				else
				{
					print("Invalid or no entity found");
				}
			}
		}
		case "ignoreentity":
		{
			if (value == "0" || value == "false")
			{
				this.IgnoreEntity = null;
			}
			else
			{
				this.TraceInfo = null;
				this.TraceEntity = null;
				this.IgnoreEntity = true;
			}
		}
		case "facing":
		{
			playerFace = GetLocalFacing();
			this.Facing = playerFace;
			if (this.IgnoreEntity)
			{
				print("Facing set to",playerFace);
			}
			else
			{
				print("Facing is not required because IgnoreEntity is false");
			}
		}
		case "vehicle":
		{
			if ( value.Find("MOVER") != -1 && GetGoal(value) )
			{
				this.Vehicle = value;
			}
			else
			{
				print(this.GoalType, ": wrong type or goal not found, expecting mover" );
			}
		}
		case "offset":
		{
			if ( value == "clearall" || value == "clear" )
			{
				tableClear(this.Offsets);
			}
			else
			{
				bGood = false;
				i = ToInt(value);
				if (typeId(i) == 1)
				{
					if(this.Vehicle)
					{
						g = GetGoal(this.Vehicle);
						if (g)
						{
							bGood = true;
							pPos = GetEntPosition(GetLocalEntity());
							offset = GetEntityLocalSpace(g.GetEntity(), pPos);

							// make em add sequentially
							n = tableCount(this.Offsets);
							if ( i < 0 || i > n )
							{
								i = n;
							}

							this.Offsets[ i ] = offset;
							print("Offest",i,"set to",offset);
						}
					}
				}
				else
				{
					print(this.GoalType, ": Invalid offset index, expecting int");
				}

				if (!bGood)
				{
					print(this.GoalType, ": Vehicle must be set first");
				}
			}
		}
		case "wait":
		{
			if ( value=="" )
			{
				this.Wait = -1;
				print("Wait cleared");
			}
			else
			{
				f = ToFloat(value);
				this.Wait = f;
				print("Wait set to",f);
			}
		}
		default:
		{
			print(this.GoalType, ": Invalid property:", property);
		}
	}

	this.ExtraDebugText = null;
};

//////////////////////////////////////////////////////////////////////////

// give it some time to find it since some games spawn ents late
this.Update = function()
{
	while(1)
	{
		sleep(1);
		if ( !this.TraceInfo || tableCount(this.TraceInfo) < 1 )
		{
			block(0);
		}

		if (!this.TraceEntity
				&& (this.IsAvailable(TEAM.AXIS) || this.IsAvailable(TEAM.ALLIES))
				&& this.TraceInfo.eyePos
				&& this.TraceInfo.endPos)
		{
			this.TraceEntity = Util.TraceForEntityInfo(this.TraceInfo.eyePos, this.TraceInfo.endPos, TRACE.ALL);

			if (this.TraceEntity && GetEntClass(this.TraceEntity) != CLASS.GOAL )
			{
				this.TraceEntity = null;
			}
			else
			{
				this.ExtraDebugText = null;
				block(0);
			}
		}
	}
};
