//-----------------------------------------------------------------------------
// DuFace's Wire Gates
// -------------------
//
// Adds vector and entity based gates. Common to all gate sections are the
// following:
//
//   * Multiplexer/Demultiplexer
//   * Latch/D-latch
//   * Equal/Inequal/Less-than/Greater-than (where appropriate)
//
//
// Vector Gates
// ------------
//
//   * Add -- removed
//   * Subtract -- removed
//   * Negate -- removed
//   * Multiply/Divide by constant -- removed
//   * Dot/Cross Product -- removed
//   * Yaw/Pitch -- removed
//   * Yaw/Pitch (Radian) -- removed
//   * Magnitude -- removed
//   * Conversion To/From -- removed
//   * Normalise -- removed
//   * Identity -- removed
//   * Random (really needed?) -- removed
//   * Component Derivative -- removed
//   * Component Integral -- removed
//
//
// Entity Gates
// ------------
//
//   * Activator
//   * Owner -- removed
//   * Identity
//   * Name -- removed
//   * Position -- removed
//   * Colour -- removed
//   * EntId -- removed
//   * Class -- removed
//   * Parent
//   * Null
//   * Velocity -- removed
//   * Forward/Right/Up vectors -- removed
//   * Max Health/Health
//   * Model -- removed
//   * Skin/Skin Count
//   * IsPlayer -- removed
//   * IsConstrained
//   * IsInWorld -- removed
//   * IsNPC -- removed
//   * IsOnFire -- removed
//   * IsOnGround -- removed
//   * IsPlayerHolding -- removed
//   * IsVehicle -- removed
//   * IsWeapon -- removed
//   * Can See
//
//
// Player Gates
// ------------
//
// Still strictly entity gates, but will output nil or zero (whichever is more
// appropriate) if the input entity is not a player.
//
//   * Alive
//   * Armour
//   * Chat Print
//   * Crouching
//   * Death/Kill Count
//   * Aim Vector -- removed
//   * InVehicle -- removed
//   * SteamID -- removed
//   * Team ID/Name
//   * Seconds Connected -- removed
//   * IsLockedOnto
//
//
// Thats all I can think of for the time being. If you can think of anything
// you'd like added then either PM me or start a thread on the Wiremod forums.
// You could leave a message on the FP forums if you wanted but I probably wont
// read it. I tend to lurk moar at Wiremod.com ;-)
//-----------------------------------------------------------------------------

AddCSLuaFile ("autorun/dufacewiregates.lua")


//-----------------------------------------------------------------------------
// Encapsulate to prevent up-fuckery.
//-----------------------------------------------------------------------------

local function AddWireGates_DuFace ()

//-----------------------------------------------------------------------------
// Controlling convars
//-----------------------------------------------------------------------------

	// Behaviour of chatprint gate is to be controlled to prevent exploitation
	// by mingebags:
	//   0 - Gate completely disabled (still spawns, just won't do anything)
	//   1 - Can only act on owning player (entity input ignored)
	//   2 - Full operation allowed
	//CreateConVar ("sv_wiregates_chatprint_act", "1", FCVAR_ARCHIVE)


//-----------------------------------------------------------------------------
// Entity Gates
//-----------------------------------------------------------------------------

// Activator (TODO... maybe)
// Identity
	GateActions["entity_ident"] = {
		group = "Entity",
		name = "Identity",
		inputs = { "A" },
		inputtypes = { "ENTITY" },
		outputtypes = { "ENTITY" },
		output = function (gate, A)
			if (A and IsEntity (A) and A:IsValid ()) then return A end
			return nil
		end,
		label = function (Out, A)
			local strEnt = "(none)"
			if (IsEntity (Out)) then strEnt = Out:GetName () end
			return string.format ("%s = %s", A, strEnt)
		end
	}
// Parent
	GateActions["entity_parent"] = {
		group = "Entity",
		name = "Parent",
		inputs = { "A" },
		inputtypes = { "ENTITY" },
		outputtypes = { "ENTITY" },
		output = function (gate, A)
			if (A and IsEntity (A) and A:IsValid ()) then return A:GetParent () end
			return NULL
		end,
		label = function (Out, A)
			local strParent = "(none)"
			if (Out != NULL and IsEntity (Out)) then strParent = Out:GetName () end
			return string.format ("A: %s  Parent  %s", A, strParent)
		end
	}
// Null (not actually worth the effort)
// Max Health/Health
	GateActions["entity_health"] = {
		group = "Entity",
		name = "Health",
		inputs = { "A" },
		inputtypes = { "ENTITY" },
		outputs = { "Health", "MaxHealth" },
		timed = true,
		output = function (gate, A)
			if (A and IsEntity (A) and A:IsValid ()) then 
				return A:Health (), A:GetMaxHealth ()
			end
			return 0, 0
		end,
		label = function (Out, A)
			return string.format ("A: %s  Health: %d  Max. Health: %d",
				A, Out.Health, Out.MaxHealth)
		end
	}
// Skin/Skin Count
	GateActions["entity_skin"] = {
		group = "Entity",
		name = "Skin Info",
		inputs = { "A" },
		inputtypes = { "ENTITY" },
		outputs = { "Skin", "SkinCount" },
		output = function (gate, A)
			if (A and IsEntity (A) and A:IsValid ()) then 
				return A:GetSkin (), A:SkinCount ()
			end
			return 0, 0
		end,
		label = function (Out, A)
			return string.format ("A: %s  Skin ID: %d  Count: %d",
				A, Out.Skin, Out.SkinCount)
		end
	}
// IsConstrained
	GateActions["entity_isconstrained"] = {
		group = "Entity",
		name = "Is Constrained",
		inputs = { "A" },
		inputtypes = { "ENTITY" },
		timed = true,
		output = function (gate, A)
			if (A and IsEntity (A) and A:IsValid () and A:IsConstrained ()) then
				return 1
			end
			return 0
		end,
		label = function (Out, A)
			local strNot = "no"
			if (Out == 1) then strNot = "yes" end
			return string.format ("A: %s  Is Constrained: %s", A, strNot)
		end
	}
// Can See
	GateActions["entity_cansee"] = {
		group = "Entity",
		name = "Can See",
		inputs = { "A", "B" },
		inputtypes = { "ENTITY", "ENTITY" },
		timed = true,
		output = function (gate, A, B)
			if (A and IsEntity (A) and A:IsValid () and B and IsEntity (B) and B:IsValid ()) then
				if (A:Visible (B)) then
					return 1
				end
			end
			return 0
		end,
		label = function (Out, A, B)
			local strNot = "not"
			if (Out == 1) then strNot = "" end
			return string.format ("Entity(%s) can%s see Entity(%s)", A, strNot, B)
		end
	}


//-----------------------------------------------------------------------------
// Player Gates
//-----------------------------------------------------------------------------

// Alive
	GateActions["player_isalive"] = {
		group = "Player",
		name = "Is Alive",
		inputs = { "A" },
		inputtypes = { "ENTITY" },
		timed = true,
		output = function (gate, A)
			if (A and IsEntity (A) and A:IsValid () and A:IsPlayer () and A:Alive ()) then
				return 1
			end
			return 0
		end,
		label = function (Out, A)
			local strNot = "no"
			if (Out == 1) then strNot = "yes" end
			return string.format ("A: %s  Is Alive: %s", A, strNot)
		end
	}
// Armour
	GateActions["player_armour"] = {
		group = "Player",
		name = "Armour",
		inputs = { "A" },
		inputtypes = { "ENTITY" },
		timed = true,
		output = function (gate, A)
			if (A and IsEntity (A) and A:IsValid () and A:IsPlayer ()) then 
				return A:Armor ()
			end
			return 0
		end,
		label = function (Out, A)
			return string.format ("A: %s  Armour: %d", A, Out)
		end
	}
--[[// Chat Print
	GateActions["player_printf"] = {
		group = "Player",
		name = "Chat Print",
		inputs = { "Player", "Message" },
		inputtypes = { "ENTITY", "STRING" },
		outputs = {  },
		output = function (gate, ply, msg)
			local act = GetConVarNumber ("sv_wiregates_chatprint_act")
			// Completely disabled
			if (Act == 0) then return end
			// May only work on spawning player
			if (Act == 1 and gate:GetOwner ():IsPlayer ()) then
				gate:GetOwner ():ChatPrint (msg)
				return
			end
			// Unrestricted usage
			if (Act == 2 and ply and ply:IsPlayer () and msg != "") then 
				ply:ChatPrint (msg)
			end
		end,
		label = function (Out, ply, msg)
			local act = GetConVarNumber ("sv_wiregates_chatprint_act")
			// Completely disabled
			if (act == 0) then
				return "Function Disabled!"
			end
			// Function enabled
			local _ply = nil
			if (act == 1 and gate:GetOwner ():IsPlayer ()) then
				_ply = gate:GetOwner ()
			elseif (act == 2 and ply and ply:IsPlayer ()) then 
				_ply = ply
			end
			// Include target in tooltip
			if (_ply) then
				return "Target: ".._ply:GetName ()
			else
				return "No Target"
			end
		end
	}
]]
// Crouching
	GateActions["player_iscrouching"] = {
		group = "Player",
		name = "Is Crouching",
		inputs = { "A" },
		inputtypes = { "ENTITY" },
		timed = true,
		output = function (gate, A)
			if (A and IsEntity (A) and A:IsValid () and A:IsPlayer () and A:Crouching ()) then
				return 1
			end
			return 0
		end,
		label = function (Out, A)
			local strNot = "no"
			if (Out == 1) then strNot = "yes" end
			return string.format ("A: %s  Is Crouching: %s", A, strNot)
		end
	}
// Death/Kill Count
	GateActions["player_mdk"] = {
		group = "Player",
		name = "Death/Kill Count",
		inputs = { "A" },
		inputtypes = { "ENTITY" },
		outputs = { "Deaths", "Kills" },
		timed = true,
		output = function (gate, A)
			if (A and IsEntity (A) and A:IsValid () and A:IsPlayer ()) then 
				return A:Deaths (), A:Frags ()
			end
			return 0, 0
		end,
		label = function (Out, A)
			return string.format ("A: %s  Kills: %d  Deaths: %d", A, Out.Kills, Out.Deaths)
		end
	}
// Team ID/Name
	GateActions["player_team"] = {
		group = "Player",
		name = "Team Details",
		inputs = { "A" },
		inputtypes = { "ENTITY" },
		outputs = { "TeamID", "TeamName" },
		outputtypes = { "NORMAL", "STRING" },
		output = function (gate, A)
			if (A and IsEntity (A) and A:IsValid () and A:IsPlayer ()) then 
				return A:Team (), team.GetName (A:Team ())
			end
			return 0, "(none)"
		end,
		label = function (Out, A)
			return string.format ("A: %s  Team: %s (%d)",
				A, Out.TeamName, Out.TeamID)
		end
	}
// Is Locked Onto by [at least one] Target Finder
	GateActions["player_istarget"] = {
		group = "Player",
		name = "Is Locked Onto",
		inputs = { "A" },
		inputtypes = { "ENTITY" },
		timed = true,
		output = function (gate, A)
			if (IsEntity (A) and A:IsValid () and A:IsPlayer ()) then
				if (A.IsLockedOnto and A:IsLockedOnto ()) then
					return 1
				end
			end
			return 0
		end,
		label = function (Out, A)
			local strNot = "no"
			if (Out == 1) then strNot = "yes" end
			return string.format ("A: %s  In Target: %s", A, strNot)
		end
	}


//-----------------------------------------------------------------------------
// Miscellaneous Gates
//-----------------------------------------------------------------------------

// Vector <-> E-Gate Vector
	GateActions["vector_convtoegate"] = {
		group = "Vector",
		name = "Convert To E-Gate Vector",
		inputs = { "A" },
		inputtypes = { "VECTOR" },
		output = function (gate, A)
			if IsVector (A) then
				WireModVectorIndex = WireModVectorIndex % 90 + 1
				WireModVector[WireModVectorIndex] = v
				return WireModVectorIndex + 9
			end
			return 0
		end,
		label = function (Out, A)
			return string.format ("%s => E-Gate Vector", A)
		end
	}
	GateActions["vector_convfromegate"] = {
		group = "Vector",
		name = "Convert From E-Gate Vector",
		inputs = { "A" },
		inputtypes = { "NORMAL" },
		outputtypes = { "VECTOR" },
		output = function (gate, A)
			local vec = WireModVector[A - 9] or nil
			if (vec and IsVector (vec)) then
				return vec
			end
			return Vector (0, 0, 0)
		end,
		label = function (Out, A, B)
			return string.format ("E-Gate Vector => (%d,%d,%d)", Out.x, Out.y, Out.z)
		end
	}
// Components <-> E-Gate Vector
	GateActions["vector_convtoegate_components"] = {
		group = "Vector",
		name = "Convert Components To E-Gate Vector",
		inputs = { "X", "Y", "Z" },
		inputtypes = { "NORMAL", "NORMAL", "NORMAL" },
		output = function (gate, X, Y, Z)
			WireModVectorIndex = WireModVectorIndex % 90 + 1
			WireModVector[WireModVectorIndex] = Vector (X, Y, Z)
			return WireModVectorIndex + 9
		end,
		label = function (Out, A)
			return string.format ("%s => E-Gate Vector", A)
		end
	}
	GateActions["vector_convfromegate_components"] = {
		group = "Vector",
		name = "Convert Components From E-Gate Vector",
		inputs = { "A" },
		inputtypes = { "NORMAL" },
		outputs = { "X", "Y", "Z" },
		output = function (gate, A)
			local vec = WireModVector[A - 9] or nil
			if (vec and IsVector (vec)) then
				return vec.x, vec.y, vec.z
			end
			return 0, 0, 0
		end,
		label = function (Out, A, B)
			return string.format ("E-Gate Vector => X:%d Y:%d Z:%d", Out.X, Out.Y, Out.Z)
		end
	}
// String <-> E-Gate Packet
	GateActions["string_convtoegate"] = {
		group = "String",
		name = "Convert String To E-Gate Packet",
		inputs = { "A" },
		inputtypes = { "STRING" },
		output = function (gate, A)
			if (A and A != "") then
				return WireGateExpressionSendPacket (string.byte (A, 1, string.len (A)))
			end
			return 0
		end,
		label = function (Out, A)
			return string.format ("%s => E-Gate Packet", A)
		end
	}
	GateActions["string_convfromegate"] = {
		group = "String",
		name = "Convert String From E-Gate Packet",
		inputs = { "A" },
		inputtypes = { "NORMAL" },
		outputtypes = { "STRING" },
		output = function (gate, A)
			A = A - 9
			if (WireModPacket[A]) then
				local str = { string.char (unpack (WireModPacket[A])) }
				return string.Implode ("", str)
			end
			return ""
		end,
		label = function (Out, A)
			return string.format ("E-Gate Packet => %s", Out)
		end
	}
	
	// Sort these gates correctly
	for name,gate in pairs(GateActions) do
		if !WireGatesSorted[gate.group] then WireGatesSorted[gate.group] = {} end
		WireGatesSorted[gate.group][name] = gate
	end
end


// Prevent this from loading before default wiregates and/or wiremod
hook.Add("InitPostEntity", "dufacewiregates", AddWireGates_DuFace)
