--- **AI** -- Build large airborne formations of aircraft.
-- 
-- **Features:**
--
--   * Build in-air formations consisting of more than 40 aircraft as one group.
--   * Build different formation types.
--   * Assign a group leader that will guide the large formation path.
-- 
-- ===
-- 
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/FOR%20-%20Formation)
-- 
-- ===
-- 
-- ### [YouTube Playlist](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl0bFIJ9jIdYM22uaWmIN4oz)
-- 
-- ===
-- 
-- ### Author: **FlightControl**
-- ### Contributions: 
-- 
-- ===
--   
-- @module AI.AI_Formation
-- @image AI_Large_Formations.JPG

--- AI_FORMATION class
-- @type AI_FORMATION
-- @extends Core.Fsm#FSM_SET
-- @field Wrapper.Unit#UNIT FollowUnit
-- @field Core.Set#SET_GROUP FollowGroupSet
-- @field #string FollowName
-- @field #AI_FORMATION.MODE FollowMode The mode the escort is in.
-- @field Scheduler#SCHEDULER FollowScheduler The instance of the SCHEDULER class.
-- @field #number FollowDistance The current follow distance.
-- @field #boolean ReportTargets If true, nearby targets are reported.
-- @Field DCSTypes#AI.Option.Air.val.ROE OptionROE Which ROE is set to the FollowGroup.
-- @field DCSTypes#AI.Option.Air.val.REACTION_ON_THREAT OptionReactionOnThreat Which REACTION_ON_THREAT is set to the FollowGroup.
-- @field #number dtFollow Time step between position updates.


--- Build large formations, make AI follow a @{Wrapper.Client#CLIENT} (player) leader or a @{Wrapper.Unit#UNIT} (AI) leader.
--
-- AI_FORMATION makes AI @{GROUP}s fly in formation of various compositions.
-- The AI_FORMATION class models formations in a different manner than the internal DCS formation logic!!!
-- The purpose of the class is to:
-- 
--   * Make formation building a process that can be managed while in flight, rather than a task.
--   * Human players can guide formations, consisting of larget planes.
--   * Build large formations (like a large bomber field).
--   * Form formations that DCS does not support off the shelve.
-- 
-- A few remarks:
-- 
--   * Depending on the type of plane, the change in direction by the leader may result in the formation getting disentangled while in flight and needs to be rebuild.
--   * Formations are vulnerable to collissions, but is depending on the type of plane, the distance between the planes and the speed and angle executed by the leader.
--   * Formations may take a while to build up.
-- 
-- As a result, the AI_FORMATION is not perfect, but is very useful to:
-- 
--   * Model large formations when flying straight line. You can build close formations when doing this.
--   * Make humans guide a large formation, when the planes are wide from each other.
--   
-- ## AI_FORMATION construction
-- 
-- Create a new SPAWN object with the @{#AI_FORMATION.New} method:
--
--   * @{#AI_FORMATION.New}(): Creates a new AI_FORMATION object from a @{Wrapper.Group#GROUP} for a @{Wrapper.Client#CLIENT} or a @{Wrapper.Unit#UNIT}, with an optional briefing text.
--
-- ## Formation methods
-- 
-- The following methods can be used to set or change the formation:
-- 
--  * @{#AI_FORMATION.FormationLine}(): Form a line formation (core formation function).
--  * @{#AI_FORMATION.FormationTrail}(): Form a trail formation.
--  * @{#AI_FORMATION.FormationLeftLine}(): Form a left line formation.
--  * @{#AI_FORMATION.FormationRightLine}(): Form a right line formation.
--  * @{#AI_FORMATION.FormationRightWing}(): Form a right wing formation.
--  * @{#AI_FORMATION.FormationLeftWing}(): Form a left wing formation.
--  * @{#AI_FORMATION.FormationCenterWing}(): Form a center wing formation.
--  * @{#AI_FORMATION.FormationCenterVic}(): Form a Vic formation (same as CenterWing.
--  * @{#AI_FORMATION.FormationCenterBoxed}(): Form a center boxed formation.
--  
-- ## Randomization
-- 
-- Use the method @{AI.AI_Formation#AI_FORMATION.SetFlightRandomization}() to simulate the formation flying errors that pilots make while in formation. Is a range set in meters.
--
-- @usage
-- local FollowGroupSet = SET_GROUP:New():FilterCategories("plane"):FilterCoalitions("blue"):FilterPrefixes("Follow"):FilterStart()
-- FollowGroupSet:Flush()
-- local LeaderUnit = UNIT:FindByName( "Leader" )
-- local LargeFormation = AI_FORMATION:New( LeaderUnit, FollowGroupSet, "Center Wing Formation", "Briefing" )
-- LargeFormation:FormationCenterWing( 500, 50, 0, 250, 250 )
-- LargeFormation:__Start( 1 )
-- 
-- @field #AI_FORMATION 
AI_FORMATION = {
  ClassName = "AI_FORMATION",
  FollowName = nil, -- The Follow Name
  FollowUnit = nil,
  FollowGroupSet = nil,
  FollowMode = 1,
  MODE = {
    FOLLOW = 1,
    MISSION = 2,
  },
  FollowScheduler = nil,
  OptionROE = AI.Option.Air.val.ROE.OPEN_FIRE,
  OptionReactionOnThreat = AI.Option.Air.val.REACTION_ON_THREAT.ALLOW_ABORT_MISSION,
  dtFollow = 0.5,
}

AI_FORMATION.__Enum = {}

--- @type AI_FORMATION.__Enum.Formation
-- @field #number None
-- @field #number Line
-- @field #number Trail
-- @field #number Stack
-- @field #number LeftLine
-- @field #number RightLine
-- @field #number LeftWing
-- @field #number RightWing
-- @field #number Vic
-- @field #number Box
AI_FORMATION.__Enum.Formation = {
  None = 0,
  Mission = 1,
  Line = 2,
  Trail = 3,
  Stack = 4,
  LeftLine = 5,
  RightLine = 6,
  LeftWing = 7,
  RightWing = 8,
  Vic = 9,
  Box = 10,
}

--- @type AI_FORMATION.__Enum.Mode
-- @field #number Mission
-- @field #number Formation
AI_FORMATION.__Enum.Mode = {
  Mission = "M",
  Formation = "F",
  Attack = "A",
  Reconnaissance = "R",
}

--- @type AI_FORMATION.__Enum.ReportType
-- @field #number All
-- @field #number Airborne
-- @field #number GroundRadar
-- @field #number Ground
AI_FORMATION.__Enum.ReportType = {
  Airborne = "*",
  Airborne = "A",
  GroundRadar = "R",
  Ground = "G",
}



--- MENUPARAM type
-- @type MENUPARAM
-- @field #AI_FORMATION ParamSelf
-- @field #number ParamDistance
-- @field #function ParamFunction
-- @field #string ParamMessage

--- AI_FORMATION class constructor for an AI group
-- @param #AI_FORMATION self
-- @param Wrapper.Unit#UNIT FollowUnit The UNIT leading the FolllowGroupSet.
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string FollowName Name of the escort.
-- @param #string FollowBriefing Briefing.
-- @return #AI_FORMATION self
function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefing ) --R2.1
  local self = BASE:Inherit( self, FSM_SET:New( FollowGroupSet ) )
  self:F( { FollowUnit, FollowGroupSet, FollowName } )

  self.FollowUnit = FollowUnit -- Wrapper.Unit#UNIT
  self.FollowGroupSet = FollowGroupSet -- Core.Set#SET_GROUP
  
  self.FollowGroupSet:ForEachGroup(
    function( FollowGroup )
      --self:E("Following")
      FollowGroup:SetState( self, "Mode", self.__Enum.Mode.Formation )
    end
  )
  
  self:SetFlightModeFormation()
  
  self:SetFlightRandomization( 2 )
  
  self:SetStartState( "None" ) 

  self:AddTransition( "*", "Stop", "Stopped" )

  self:AddTransition( {"None", "Stopped"}, "Start", "Following" )

  self:AddTransition( "*", "FormationLine", "*" )
  --- FormationLine Handler OnBefore for AI_FORMATION
  -- @function [parent=#AI_FORMATION] OnBeforeFormationLine
  -- @param #AI_FORMATION self
  -- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
  -- @param #string From
  -- @param #string Event
  -- @param #string To
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
  -- @return #boolean
  
  --- FormationLine Handler OnAfter for AI_FORMATION
  -- @function [parent=#AI_FORMATION] OnAfterFormationLine
  -- @param #AI_FORMATION self
  -- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
  -- @param #string From
  -- @param #string Event
  -- @param #string To
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
  
  --- FormationLine Trigger for AI_FORMATION
  -- @function [parent=#AI_FORMATION] FormationLine
  -- @param #AI_FORMATION self
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
  
  --- FormationLine Asynchronous Trigger for AI_FORMATION
  -- @function [parent=#AI_FORMATION] __FormationLine
  -- @param #AI_FORMATION self
  -- @param #number Delay
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
  
  self:AddTransition( "*", "FormationTrail", "*" )
  --- FormationTrail Handler OnBefore for AI_FORMATION
  -- @function [parent=#AI_FORMATION] OnBeforeFormationTrail
  -- @param #AI_FORMATION self
  -- @param #string From
  -- @param #string Event
  -- @param #string To
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @return #boolean
  
  --- FormationTrail Handler OnAfter for AI_FORMATION
  -- @function [parent=#AI_FORMATION] OnAfterFormationTrail
  -- @param #AI_FORMATION self
  -- @param #string From
  -- @param #string Event
  -- @param #string To
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  
  --- FormationTrail Trigger for AI_FORMATION
  -- @function [parent=#AI_FORMATION] FormationTrail
  -- @param #AI_FORMATION self
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  
  --- FormationTrail Asynchronous Trigger for AI_FORMATION
  -- @function [parent=#AI_FORMATION] __FormationTrail
  -- @param #AI_FORMATION self
  -- @param #number Delay
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.

  self:AddTransition( "*", "FormationStack", "*" )
  --- FormationStack Handler OnBefore for AI_FORMATION
  -- @function [parent=#AI_FORMATION] OnBeforeFormationStack
  -- @param #AI_FORMATION self
  -- @param #string From
  -- @param #string Event
  -- @param #string To
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
  -- @return #boolean
  
  --- FormationStack Handler OnAfter for AI_FORMATION
  -- @function [parent=#AI_FORMATION] OnAfterFormationStack
  -- @param #AI_FORMATION self
  -- @param #string From
  -- @param #string Event
  -- @param #string To
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
  
  --- FormationStack Trigger for AI_FORMATION
  -- @function [parent=#AI_FORMATION] FormationStack
  -- @param #AI_FORMATION self
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
  
  --- FormationStack Asynchronous Trigger for AI_FORMATION
  -- @function [parent=#AI_FORMATION] __FormationStack
  -- @param #AI_FORMATION self
  -- @param #number Delay
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.

  self:AddTransition( "*", "FormationLeftLine", "*" )  
  --- FormationLeftLine Handler OnBefore for AI_FORMATION
  -- @function [parent=#AI_FORMATION] OnBeforeFormationLeftLine
  -- @param #AI_FORMATION self
  -- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
  -- @param #string From
  -- @param #string Event
  -- @param #string To
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
  -- @return #boolean
  
  --- FormationLeftLine Handler OnAfter for AI_FORMATION
  -- @function [parent=#AI_FORMATION] OnAfterFormationLeftLine
  -- @param #AI_FORMATION self
  -- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
  -- @param #string From
  -- @param #string Event
  -- @param #string To
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
  
  --- FormationLeftLine Trigger for AI_FORMATION
  -- @function [parent=#AI_FORMATION] FormationLeftLine
  -- @param #AI_FORMATION self
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
  
  --- FormationLeftLine Asynchronous Trigger for AI_FORMATION
  -- @function [parent=#AI_FORMATION] __FormationLeftLine
  -- @param #AI_FORMATION self
  -- @param #number Delay
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.

  self:AddTransition( "*", "FormationRightLine", "*" )  
  --- FormationRightLine Handler OnBefore for AI_FORMATION
  -- @function [parent=#AI_FORMATION] OnBeforeFormationRightLine
  -- @param #AI_FORMATION self
  -- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
  -- @param #string From
  -- @param #string Event
  -- @param #string To
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
  -- @return #boolean
  
  --- FormationRightLine Handler OnAfter for AI_FORMATION
  -- @function [parent=#AI_FORMATION] OnAfterFormationRightLine
  -- @param #AI_FORMATION self
  -- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
  -- @param #string From
  -- @param #string Event
  -- @param #string To
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
  
  --- FormationRightLine Trigger for AI_FORMATION
  -- @function [parent=#AI_FORMATION] FormationRightLine
  -- @param #AI_FORMATION self
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
  
  --- FormationRightLine Asynchronous Trigger for AI_FORMATION
  -- @function [parent=#AI_FORMATION] __FormationRightLine
  -- @param #AI_FORMATION self
  -- @param #number Delay
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.

  self:AddTransition( "*", "FormationLeftWing", "*" )
  --- FormationLeftWing Handler OnBefore for AI_FORMATION
  -- @function [parent=#AI_FORMATION] OnBeforeFormationLeftWing
  -- @param #AI_FORMATION self
  -- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
  -- @param #string From
  -- @param #string Event
  -- @param #string To
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
  -- @return #boolean
  
  --- FormationLeftWing Handler OnAfter for AI_FORMATION
  -- @function [parent=#AI_FORMATION] OnAfterFormationLeftWing
  -- @param #AI_FORMATION self
  -- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
  -- @param #string From
  -- @param #string Event
  -- @param #string To
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
  
  --- FormationLeftWing Trigger for AI_FORMATION
  -- @function [parent=#AI_FORMATION] FormationLeftWing
  -- @param #AI_FORMATION self
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
  
  --- FormationLeftWing Asynchronous Trigger for AI_FORMATION
  -- @function [parent=#AI_FORMATION] __FormationLeftWing
  -- @param #AI_FORMATION self
  -- @param #number Delay
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
  
  self:AddTransition( "*", "FormationRightWing", "*" )
  --- FormationRightWing Handler OnBefore for AI_FORMATION
  -- @function [parent=#AI_FORMATION] OnBeforeFormationRightWing
  -- @param #AI_FORMATION self
  -- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
  -- @param #string From
  -- @param #string Event
  -- @param #string To
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
  -- @return #boolean
  
  --- FormationRightWing Handler OnAfter for AI_FORMATION
  -- @function [parent=#AI_FORMATION] OnAfterFormationRightWing
  -- @param #AI_FORMATION self
  -- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
  -- @param #string From
  -- @param #string Event
  -- @param #string To
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
  
  --- FormationRightWing Trigger for AI_FORMATION
  -- @function [parent=#AI_FORMATION] FormationRightWing
  -- @param #AI_FORMATION self
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
  
  --- FormationRightWing Asynchronous Trigger for AI_FORMATION
  -- @function [parent=#AI_FORMATION] __FormationRightWing
  -- @param #AI_FORMATION self
  -- @param #number Delay
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
  
  self:AddTransition( "*", "FormationCenterWing", "*" )
  --- FormationCenterWing Handler OnBefore for AI_FORMATION
  -- @function [parent=#AI_FORMATION] OnBeforeFormationCenterWing
  -- @param #AI_FORMATION self
  -- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
  -- @param #string From
  -- @param #string Event
  -- @param #string To
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
  -- @return #boolean
  
  --- FormationCenterWing Handler OnAfter for AI_FORMATION
  -- @function [parent=#AI_FORMATION] OnAfterFormationCenterWing
  -- @param #AI_FORMATION self
  -- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
  -- @param #string From
  -- @param #string Event
  -- @param #string To
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
  
  --- FormationCenterWing Trigger for AI_FORMATION
  -- @function [parent=#AI_FORMATION] FormationCenterWing
  -- @param #AI_FORMATION self
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
  
  --- FormationCenterWing Asynchronous Trigger for AI_FORMATION
  -- @function [parent=#AI_FORMATION] __FormationCenterWing
  -- @param #AI_FORMATION self
  -- @param #number Delay
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.

  self:AddTransition( "*", "FormationVic", "*" )
  --- FormationVic Handler OnBefore for AI_FORMATION
  -- @function [parent=#AI_FORMATION] OnBeforeFormationVic
  -- @param #AI_FORMATION self
  -- @param #string From
  -- @param #string Event
  -- @param #string To
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
  -- @return #boolean
  
  --- FormationVic Handler OnAfter for AI_FORMATION
  -- @function [parent=#AI_FORMATION] OnAfterFormationVic
  -- @param #AI_FORMATION self
  -- @param #string From
  -- @param #string Event
  -- @param #string To
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
  
  --- FormationVic Trigger for AI_FORMATION
  -- @function [parent=#AI_FORMATION] FormationVic
  -- @param #AI_FORMATION self
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
  
  --- FormationVic Asynchronous Trigger for AI_FORMATION
  -- @function [parent=#AI_FORMATION] __FormationVic
  -- @param #AI_FORMATION self
  -- @param #number Delay
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.

  self:AddTransition( "*", "FormationBox", "*" )
  --- FormationBox Handler OnBefore for AI_FORMATION
  -- @function [parent=#AI_FORMATION] OnBeforeFormationBox
  -- @param #AI_FORMATION self
  -- @param #string From
  -- @param #string Event
  -- @param #string To
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
  -- @param #number ZLevels The amount of levels on the Z-axis.
  -- @return #boolean
  
  --- FormationBox Handler OnAfter for AI_FORMATION
  -- @function [parent=#AI_FORMATION] OnAfterFormationBox
  -- @param #AI_FORMATION self
  -- @param #string From
  -- @param #string Event
  -- @param #string To
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
  -- @param #number ZLevels The amount of levels on the Z-axis.
  
  --- FormationBox Trigger for AI_FORMATION
  -- @function [parent=#AI_FORMATION] FormationBox
  -- @param #AI_FORMATION self
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
  -- @param #number ZLevels The amount of levels on the Z-axis.
  
  --- FormationBox Asynchronous Trigger for AI_FORMATION
  -- @function [parent=#AI_FORMATION] __FormationBox
  -- @param #AI_FORMATION self
  -- @param #number Delay
  -- @param #number XStart The start position on the X-axis in meters for the first group.
  -- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
  -- @param #number YStart The start position on the Y-axis in meters for the first group.
  -- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
  -- @param #number ZStart The start position on the Z-axis in meters for the first group.
  -- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
  -- @param #number ZLevels The amount of levels on the Z-axis.
  
 
  self:AddTransition( "*", "Follow", "Following" )

  self:FormationLeftLine( 500, 0, 250, 250 )
  
  self.FollowName = FollowName
  self.FollowBriefing = FollowBriefing


  self.CT1 = 0
  self.GT1 = 0

  self.FollowMode = AI_FORMATION.MODE.MISSION

  return self
end


--- Set time interval between updates of the formation.
-- @param #AI_FORMATION self
-- @param #number dt Time step in seconds between formation updates. Default is every 0.5 seconds.
-- @return #AI_FORMATION
function AI_FORMATION:SetFollowTimeInterval(dt) --R2.1
  self.dtFollow=dt or 0.5
  return self
end

--- This function is for test, it will put on the frequency of the FollowScheduler a red smoke at the direction vector calculated for the escort to fly to.
-- This allows to visualize where the escort is flying to.
-- @param #AI_FORMATION self
-- @param #boolean SmokeDirection If true, then the direction vector will be smoked.
-- @return #AI_FORMATION
function AI_FORMATION:TestSmokeDirectionVector( SmokeDirection ) --R2.1
  self.SmokeDirectionVector = ( SmokeDirection == true ) and true or false
  return self
end

--- FormationLine Handler OnAfter for AI_FORMATION
-- @param #AI_FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
-- @return #AI_FORMATION
function AI_FORMATION:onafterFormationLine( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, Formation ) --R2.1
  self:F( { FollowGroupSet, From , Event ,To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, Formation } )

  XStart = XStart or self.XStart
  XSpace = XSpace or self.XSpace
  YStart = YStart or self.YStart
  YSpace = YSpace or self.YSpace
  ZStart = ZStart or self.ZStart
  ZSpace = ZSpace or self.ZSpace

  FollowGroupSet:Flush( self )
  
  local FollowSet = FollowGroupSet:GetSet()
  
  local i = 1  --FF i=0 caused first unit to have no XSpace! Probably needs further adjustments. This is just a quick work around.
  
  for FollowID, FollowGroup in pairs( FollowSet ) do
  
    local PointVec3 = POINT_VEC3:New()
    PointVec3:SetX( XStart + i * XSpace )
    PointVec3:SetY( YStart + i * YSpace )
    PointVec3:SetZ( ZStart + i * ZSpace )
  
    local Vec3 = PointVec3:GetVec3()
    FollowGroup:SetState( self, "FormationVec3", Vec3 )
    i = i + 1

    FollowGroup:SetState( FollowGroup, "Formation", Formation )
  end
  
  return self

end

--- FormationTrail Handler OnAfter for AI_FORMATION
-- @param #AI_FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @return #AI_FORMATION
function AI_FORMATION:onafterFormationTrail( FollowGroupSet, From , Event , To, XStart, XSpace, YStart ) --R2.1

  self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,0,0, self.__Enum.Formation.Trail )

  return self
end


--- FormationStack Handler OnAfter for AI_FORMATION
-- @param #AI_FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @return #AI_FORMATION
function AI_FORMATION:onafterFormationStack( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace ) --R2.1

  self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,YSpace,0,0, self.__Enum.Formation.Stack )

  return self
end




--- FormationLeftLine Handler OnAfter for AI_FORMATION
-- @param #AI_FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
-- @return #AI_FORMATION
function AI_FORMATION:onafterFormationLeftLine( FollowGroupSet, From , Event , To, XStart, YStart, ZStart, ZSpace ) --R2.1

  self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,0,YStart,0,-ZStart,-ZSpace, self.__Enum.Formation.LeftLine )

  return self
end


--- FormationRightLine Handler OnAfter for AI_FORMATION
-- @param #AI_FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
-- @return #AI_FORMATION
function AI_FORMATION:onafterFormationRightLine( FollowGroupSet, From , Event , To, XStart, YStart, ZStart, ZSpace ) --R2.1

  self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,0,YStart,0,ZStart,ZSpace,self.__Enum.Formation.RightLine)

  return self
end


--- FormationLeftWing Handler OnAfter for AI_FORMATION
-- @param #AI_FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
function AI_FORMATION:onafterFormationLeftWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, ZStart, ZSpace ) --R2.1

  self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,-ZStart,-ZSpace,self.__Enum.Formation.LeftWing)

  return self
end


--- FormationRightWing Handler OnAfter for AI_FORMATION
-- @function [parent=#AI_FORMATION] OnAfterFormationRightWing
-- @param #AI_FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
function AI_FORMATION:onafterFormationRightWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, ZStart, ZSpace ) --R2.1

  self:onafterFormationLine(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,0,ZStart,ZSpace,self.__Enum.Formation.RightWing)

  return self
end


--- FormationCenterWing Handler OnAfter for AI_FORMATION
-- @param #AI_FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
function AI_FORMATION:onafterFormationCenterWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace ) --R2.1

  local FollowSet = FollowGroupSet:GetSet()
  
  local i = 0
  
  for FollowID, FollowGroup in pairs( FollowSet ) do
  
    local PointVec3 = POINT_VEC3:New()
    
    local Side = ( i % 2 == 0 ) and 1 or -1
    local Row = i / 2 + 1
    
    PointVec3:SetX( XStart + Row * XSpace )
    PointVec3:SetY( YStart )
    PointVec3:SetZ( Side * ( ZStart + i * ZSpace ) )
  
    local Vec3 = PointVec3:GetVec3()
    FollowGroup:SetState( self, "FormationVec3", Vec3 )
    i = i + 1
    FollowGroup:SetState( FollowGroup, "Formation", self.__Enum.Formation.Vic )
  end
  
  return self
end


--- FormationVic Handle for AI_FORMATION
-- @param #AI_FORMATION self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
-- @return #AI_FORMATION
function AI_FORMATION:onafterFormationVic( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace ) --R2.1

  self:onafterFormationCenterWing(FollowGroupSet,From,Event,To,XStart,XSpace,YStart,YSpace,ZStart,ZSpace)
  
  return self
end

--- FormationBox Handler OnAfter for AI_FORMATION
-- @param #AI_FORMATION self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #number XStart The start position on the X-axis in meters for the first group.
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
-- @param #number YStart The start position on the Y-axis in meters for the first group.
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
-- @param #number ZLevels The amount of levels on the Z-axis.
-- @return #AI_FORMATION
function AI_FORMATION:onafterFormationBox( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, ZLevels ) --R2.1

  local FollowSet = FollowGroupSet:GetSet()
  
  local i = 0
  
  for FollowID, FollowGroup in pairs( FollowSet ) do
  
    local PointVec3 = POINT_VEC3:New()
    
    local ZIndex = i % ZLevels
    local XIndex = math.floor( i / ZLevels )
    local YIndex = math.floor( i / ZLevels )
    
    PointVec3:SetX( XStart + XIndex * XSpace )
    PointVec3:SetY( YStart + YIndex * YSpace )
    PointVec3:SetZ( -ZStart - (ZSpace * ZLevels / 2 ) + ZSpace * ZIndex )
  
    local Vec3 = PointVec3:GetVec3()
    FollowGroup:SetState( self, "FormationVec3", Vec3 )
    i = i + 1
    FollowGroup:SetState( FollowGroup, "Formation", self.__Enum.Formation.Box )
  end

  return self
end


--- Use the method @{AI.AI_Formation#AI_FORMATION.SetFlightRandomization}() to make the air units in your formation randomize their flight a bit while in formation.
-- @param #AI_FORMATION self
-- @param #number FlightRandomization The formation flying errors that pilots can make while in formation. Is a range set in meters.
-- @return #AI_FORMATION
function AI_FORMATION:SetFlightRandomization( FlightRandomization ) --R2.1

  self.FlightRandomization = FlightRandomization
  
  return self
end


--- Gets your escorts to flight mode.
-- @param #AI_FORMATION self
-- @param Wrapper.Group#GROUP FollowGroup FollowGroup.
-- @return #AI_FORMATION
function AI_FORMATION:GetFlightMode( FollowGroup )

  if FollowGroup then
    FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
    FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Mission )
  end
  
  
  return FollowGroup:GetState( FollowGroup, "Mode" )
end



--- This sets your escorts to fly a mission.
-- @param #AI_FORMATION self
-- @param Wrapper.Group#GROUP FollowGroup FollowGroup.
-- @return #AI_FORMATION
function AI_FORMATION:SetFlightModeMission( FollowGroup )

  if FollowGroup then
    FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
    FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Mission )
  else
    self.FollowGroupSet:ForSomeGroupAlive(
      --- @param Core.Group#GROUP EscortGroup
      function( FollowGroup )
        FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
        FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Mission )
      end
    )
  end
  
  
  return self
end


--- This sets your escorts to execute an attack.
-- @param #AI_FORMATION self
-- @param Wrapper.Group#GROUP FollowGroup FollowGroup.
-- @return #AI_FORMATION
function AI_FORMATION:SetFlightModeAttack( FollowGroup )

  if FollowGroup then
    FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
    FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Attack )
  else
    self.FollowGroupSet:ForSomeGroupAlive(
      --- @param Core.Group#GROUP EscortGroup
      function( FollowGroup )
        FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
        FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Attack )
      end
    )
  end
  
  
  return self
end


--- This sets your escorts to fly in a formation.
-- @param #AI_FORMATION self
-- @param Wrapper.Group#GROUP FollowGroup FollowGroup.
-- @return #AI_FORMATION
function AI_FORMATION:SetFlightModeFormation( FollowGroup )

  if FollowGroup then
    FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
    FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Formation )
  else
    self.FollowGroupSet:ForSomeGroupAlive(
      --- @param Core.Group#GROUP EscortGroup
      function( FollowGroup )
        FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
        FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Formation )
      end
    )
  end
  
  return self
end




--- Stop function. Formation will not be updated any more.
-- @param #AI_FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The following set of groups.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To The to state.
function AI_FORMATION:onafterStop(FollowGroupSet, From, Event, To) --R2.1
  self:E("Stopping formation.")
end

--- Follow event fuction. Check if coming from state "stopped". If so the transition is rejected.
-- @param #AI_FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The following set of groups.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To The to state.
function AI_FORMATION:onbeforeFollow( FollowGroupSet, From, Event, To ) --R2.1
  if From=="Stopped" then
    return false  -- Deny transition.
  end
  return true
end

--- Enter following state.
-- @param #AI_FORMATION self
-- @param Core.Set#SET_GROUP FollowGroupSet The following set of groups.
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To The to state.
function AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1

  if self.FollowUnit:IsAlive() then

    local ClientUnit = self.FollowUnit

    local CT1, CT2, CV1, CV2
    CT1 = ClientUnit:GetState( self, "CT1" )

    local CuVec3=ClientUnit:GetVec3()

    if CT1 == nil or CT1 == 0 then
      ClientUnit:SetState( self, "CV1",  CuVec3)
      ClientUnit:SetState( self, "CT1", timer.getTime() )
    else
      CT1 = ClientUnit:GetState( self, "CT1" )
      CT2 = timer.getTime()
      CV1 = ClientUnit:GetState( self, "CV1" )
      CV2 = CuVec3
      
      ClientUnit:SetState( self, "CT1", CT2 )
      ClientUnit:SetState( self, "CV1", CV2 )
    end
        
    --FollowGroupSet:ForEachGroupAlive( bla, self, ClientUnit, CT1, CV1, CT2, CV2)
    
    for _,_group in pairs(FollowGroupSet:GetSet()) do
      local group=_group --Wrapper.Group#GROUP
      if group and group:IsAlive() then
        self:FollowMe(group, ClientUnit, CT1, CV1, CT2, CV2)
      end
    end
    
    self:__Follow( -self.dtFollow )
  end
  
end


--- Follow me.
-- @param #AI_FORMATION self
-- @param Wrapper.Group#GROUP FollowGroup Follow group.
-- @param Wrapper.Unit#UNIT ClientUnit Client Unit.
-- @param DCS#Time CT1 Time
-- @param DCS#Vec3 CV1 Vec3
-- @param DCS#Time CT2 Time
-- @param DCS#Vec3 CV2 Vec3
function AI_FORMATION:FollowMe(FollowGroup, ClientUnit, CT1, CV1, CT2, CV2)

  if FollowGroup:GetState( FollowGroup, "Mode" ) == self.__Enum.Mode.Formation and not self:Is("Stopped") then
 
    self:T({Mode=FollowGroup:GetState( FollowGroup, "Mode" )})

    FollowGroup:OptionROTEvadeFire()
    FollowGroup:OptionROEReturnFire()

    local GroupUnit = FollowGroup:GetUnit( 1 )
    
    local GuVec3=GroupUnit:GetVec3()
    
    local FollowFormation = FollowGroup:GetState( self, "FormationVec3" )
    
    if FollowFormation then
      local FollowDistance = FollowFormation.x
      
      local GT1 = GroupUnit:GetState( self, "GT1" )
  
      if CT1 == nil or CT1 == 0 or GT1 == nil or GT1 == 0 then
        GroupUnit:SetState( self, "GV1", GuVec3)
        GroupUnit:SetState( self, "GT1", timer.getTime() ) 
      else
        local CD = ( ( CV2.x - CV1.x )^2 + ( CV2.y - CV1.y )^2 + ( CV2.z - CV1.z )^2 ) ^ 0.5
        local CT = CT2 - CT1
  
        local CS = ( 3600 / CT ) * ( CD / 1000 ) / 3.6

        local CDv = { x = CV2.x - CV1.x, y = CV2.y - CV1.y, z = CV2.z - CV1.z }
        local Ca = math.atan2( CDv.x, CDv.z )
  
        local GT1 = GroupUnit:GetState( self, "GT1" )
        local GT2 = timer.getTime()
        
        local GV1 = GroupUnit:GetState( self, "GV1" )        
        local GV2 = GuVec3
        
        --[[
        GV2:AddX( math.random( -Formation.FlightRandomization / 2, Formation.FlightRandomization / 2 ) )
        GV2:AddY( math.random( -Formation.FlightRandomization / 2, Formation.FlightRandomization / 2 ) )
        GV2:AddZ( math.random( -Formation.FlightRandomization / 2, Formation.FlightRandomization / 2 ) )
        ]]
        
        GV2.x=GV2.x+math.random( -self.FlightRandomization / 2, self.FlightRandomization / 2 )
        GV2.y=GV2.y+math.random( -self.FlightRandomization / 2, self.FlightRandomization / 2 )
        GV2.z=GV2.z+math.random( -self.FlightRandomization / 2, self.FlightRandomization / 2 )

        
        GroupUnit:SetState( self, "GT1", GT2 )
        GroupUnit:SetState( self, "GV1", GV2 )
        
  
        local GD = ( ( GV2.x - GV1.x )^2 + ( GV2.y - GV1.y )^2 + ( GV2.z - GV1.z )^2 ) ^ 0.5
        local GT = GT2 - GT1
  

        -- Calculate the distance
        local GDv =  { x = GV2.x - CV1.x, y =  GV2.y - CV1.y, z = GV2.z - CV1.z }
        local Alpha_T = math.atan2( GDv.x, GDv.z ) - math.atan2( CDv.x, CDv.z ) 
        local Alpha_R = ( Alpha_T < 0 ) and Alpha_T + 2 * math.pi or Alpha_T
        local Position = math.cos( Alpha_R )
        local GD = ( ( GDv.x )^2 + ( GDv.z )^2 ) ^ 0.5
        local Distance = GD * Position + - CS * 0.5
  
        -- Calculate the group direction vector
        local GV = { x = GV2.x - CV2.x, y = GV2.y - CV2.y, z = GV2.z - CV2.z  }
  
        -- Calculate GH2, GH2 with the same height as CV2.
        local GH2 = { x = GV2.x, y = CV2.y + FollowFormation.y, z = GV2.z }
  
        -- Calculate the angle of GV to the orthonormal plane
        local alpha = math.atan2( GV.x, GV.z )
  
        local GVx = FollowFormation.z * math.cos( Ca ) + FollowFormation.x * math.sin( Ca )
        local GVz = FollowFormation.x * math.cos( Ca ) - FollowFormation.z * math.sin( Ca )


        -- Now we calculate the intersecting vector between the circle around CV2 with radius FollowDistance and GH2.
        -- From the GeoGebra model: CVI = (x(CV2) + FollowDistance cos(alpha), y(GH2) + FollowDistance sin(alpha), z(CV2))
        local Inclination = ( Distance + FollowFormation.x ) / 10
        if Inclination < -30 then
          Inclination = - 30
        end
        
        local CVI = { 
          x = CV2.x + CS * 10 * math.sin(Ca),
          y = GH2.y + Inclination, -- + FollowFormation.y,
          y = GH2.y,
          z = CV2.z + CS * 10 * math.cos(Ca),
        }
  
        -- Calculate the direction vector DV of the escort group. We use CVI as the base and CV2 as the direction.
        local DV = { x = CV2.x - CVI.x, y = CV2.y - CVI.y, z = CV2.z - CVI.z }
  
        -- We now calculate the unary direction vector DVu, so that we can multiply DVu with the speed, which is expressed in meters / s.
        -- We need to calculate this vector to predict the point the escort group needs to fly to according its speed.
        -- The distance of the destination point should be far enough not to have the aircraft starting to swipe left to right...
        local DVu = { x = DV.x / FollowDistance, y = DV.y, z = DV.z / FollowDistance }
  
        -- Now we can calculate the group destination vector GDV.
        local GDV = { x = CVI.x, y = CVI.y, z = CVI.z }
              
        local ADDx = FollowFormation.x * math.cos(alpha) - FollowFormation.z * math.sin(alpha)
        local ADDz = FollowFormation.z * math.cos(alpha) + FollowFormation.x * math.sin(alpha)
              
        local GDV_Formation = { 
          x = GDV.x - GVx, 
          y = GDV.y, 
          z = GDV.z - GVz
        }
             
        -- Debug smoke. 
        if self.SmokeDirectionVector == true then
          trigger.action.smoke( GDV, trigger.smokeColor.Green )
          trigger.action.smoke( GDV_Formation, trigger.smokeColor.White )
        end
              
              
              
        local Time = 120
              
        local Speed = - ( Distance + FollowFormation.x ) / Time

        if Distance > -10000 then
          Speed = - ( Distance + FollowFormation.x ) / 60
        end
 
        if Distance > -2500 then
          Speed = - ( Distance + FollowFormation.x ) / 20
        end
              
        local GS = Speed + CS

        --self:F( { Distance = Distance, Speed = Speed, CS = CS, GS = GS } )              
  
        -- Now route the escort to the desired point with the desired speed.
        FollowGroup:RouteToVec3( GDV_Formation, GS ) -- DCS models speed in Mps (Miles per second)
        
      end
    end
  end
end
