--- **Tasking** - Creates and manages player TASK_ZONE_CAPTURE tasks.
-- 
-- The **TASK_CAPTURE_DISPATCHER** allows you to setup various tasks for let human
-- players capture zones in a co-operation effort. 
--  
-- The dispatcher will implement for you mechanisms to create capture zone tasks:
--  
--   * As setup by the mission designer.
--   * Dynamically capture zone tasks.
--   
-- 
--   
-- **Specific features:**
-- 
--   * Creates a task to capture zones and achieve mission goals.
--   * Orchestrate the task flow, so go from Planned to Assigned to Success, Failed or Cancelled.
--   * Co-operation tasking, so a player joins a group of players executing the same task.
-- 
-- 
-- **A complete task menu system to allow players to:**
--   
--   * Join the task, abort the task.
--   * Mark the location of the zones to capture on the map.
--   * Provide details of the zones.
--   * Route to the zones.
--   * Display the task briefing.
--   
--   
-- **A complete mission menu system to allow players to:**
--   
--   * Join a task, abort the task.
--   * Display task reports.
--   * Display mission statistics.
--   * Mark the task locations on the map.
--   * Provide details of the zones.
--   * Display the mission briefing.
--   * Provide status updates as retrieved from the command center.
--   * Automatically assign a random task as part of a mission.
--   * Manually assign a specific task as part of a mission.
--   
--   
--  **A settings system, using the settings menu:**
--  
--   * Tweak the duration of the display of messages.
--   * Switch between metric and imperial measurement system.
--   * Switch between coordinate formats used in messages: BR, BRA, LL DMS, LL DDM, MGRS.
--   * Various other options.
--   
-- ===
-- 
-- ### Author: **FlightControl**
-- 
-- ### Contributions: 
-- 
-- ===
-- 
-- @module Tasking.Task_Zone_Capture_Dispatcher
-- @image MOOSE.JPG

do -- TASK_CAPTURE_DISPATCHER

  --- TASK_CAPTURE_DISPATCHER class.
  -- @type TASK_CAPTURE_DISPATCHER
  -- @extends Tasking.Task_Manager#TASK_MANAGER
  -- @field TASK_CAPTURE_DISPATCHER.ZONE ZONE

  --- @type TASK_CAPTURE_DISPATCHER.CSAR
  -- @field Wrapper.Unit#UNIT PilotUnit
  -- @field Tasking.Task#TASK Task
  

  --- Implements the dynamic dispatching of capture zone tasks.
  -- 
  -- The **TASK_CAPTURE_DISPATCHER** allows you to setup various tasks for let human
  -- players capture zones in a co-operation effort. 
  --  
  -- Let's explore **step by step** how to setup the task capture zone dispatcher.
  -- 
  -- # 1. Setup a mission environment.
  -- 
  -- It is easy, as it works just like any other task setup, so setup a command center and a mission.
  -- 
  -- ## 1.1. Create a command center.
  -- 
  -- First you need to create a command center using the @{Tasking.CommandCenter#COMMANDCENTER.New}() constructor.
  -- The command assumes that you´ve setup a group in the mission editor with the name HQ.
  -- This group will act as the command center object.
  -- It is a good practice to mark this group as invisible and invulnerable.
  -- 
  --     local CommandCenter = COMMANDCENTER
  --        :New( GROUP:FindByName( "HQ" ), "HQ" ) -- Create the CommandCenter.
  --     
  -- ## 1.2. Create a mission.
  -- 
  -- Tasks work in a **mission**, which groups these tasks to achieve a joint **mission goal**. A command center can **govern multiple missions**.
  -- 
  -- Create a new mission, using the @{Tasking.Mission#MISSION.New}() constructor.
  -- 
  --     -- Declare the Mission for the Command Center.
  --     local Mission = MISSION
  --       :New( CommandCenter, 
  --             "Overlord", 
  --             "High", 
  --             "Capture the blue zones.", 
  --             coalition.side.RED 
  --           ) 
  -- 
  -- 
  -- # 2. Dispatch a **capture zone** task.
  -- 
  -- So, now that we have a command center and a mission, we now create the capture zone task.
  -- We create the capture zone task using the @{#TASK_CAPTURE_DISPATCHER.AddCaptureZoneTask}() constructor.
  -- 
  -- ## 2.1. Create the capture zones.
  -- 
  -- Because a capture zone task will not generate the capture zones, you'll need to create them first.
  -- 
  --     
  --     -- We define here a capture zone; of the type ZONE_CAPTURE_COALITION.
  --     -- The zone to be captured has the name Alpha, and was defined in the mission editor as a trigger zone.
  --     CaptureZone = ZONE:New( "Alpha" )
  --     CaptureZoneCoalitionApha = ZONE_CAPTURE_COALITION:New( CaptureZone, coalition.side.RED )
  -- 
  -- ## 2.2. Create a set of player groups.
  --    
  -- What is also needed, is to have a set of @{Core.Group}s defined that contains the clients of the players.
  -- 
  --     -- Allocate the player slots, which must be aircraft (airplanes or helicopters), that can be manned by players.
  --     -- We use the method FilterPrefixes to filter those player groups that have client slots, as defined in the mission editor.
  --     -- In this example, we filter the groups where the name starts with "Blue Player", which captures the blue player slots.
  --     local PlayerGroupSet = SET_GROUP:New():FilterPrefixes( "Blue Player" ):FilterStart()
  -- 
  -- ## 2.3. Setup the capture zone task.
  -- 
  -- First, we need to create a TASK_CAPTURE_DISPATCHER object.
  -- 
  --     TaskCaptureZoneDispatcher = TASK_CAPTURE_DISPATCHER:New( Mission, PilotGroupSet )
  -- 
  -- So, the variable `TaskCaptureZoneDispatcher` will contain the object of class TASK_CAPTURE_DISPATCHER, 
  -- which will allow you to dispatch capture zone tasks:
  --  
  --   * for mission `Mission`, as was defined in section 1.2.
  --   * for the group set `PilotGroupSet`, as was defined in section 2.2.
  -- 
  -- Now that we have `TaskDispatcher` object, we can now **create the TaskCaptureZone**, using the @{#TASK_CAPTURE_DISPATCHER.AddCaptureZoneTask}() method!
  -- 
  --     local TaskCaptureZone = TaskCaptureZoneDispatcher:AddCaptureZoneTask( 
  --       "Capture zone Alpha", 
  --       CaptureZoneCoalitionAlpha, 
  --       "Fly to zone Alpha and eliminate all enemy forces to capture it." )
  -- 
  -- As a result of this code, the `TaskCaptureZone` (returned) variable will contain an object of @{#TASK_CAPTURE_ZONE}!
  -- We pass to the method the title of the task, and the `CaptureZoneCoalitionAlpha`, which is the zone to be captured, as defined in section 2.1!
  -- This returned `TaskCaptureZone` object can now be used to setup additional task configurations, or to control this specific task with special events.
  -- 
  -- And you're done! As you can see, it is a small bit of work, but the reward is great.
  -- And, because all this is done using program interfaces, you can easily build a mission to capture zones yourself!
  -- Based on various events happening within your mission, you can use the above methods to create new capture zones, 
  -- and setup a new capture zone task and assign it to a group of players, while your mission is running!
  -- 
  --     
  -- 
  -- @field #TASK_CAPTURE_DISPATCHER
  TASK_CAPTURE_DISPATCHER = {
    ClassName = "TASK_CAPTURE_DISPATCHER",
    Mission = nil,
    Tasks = {},
    Zones = {},
    ZoneCount = 0,
  }


  
  TASK_CAPTURE_DISPATCHER.AI_A2G_Dispatcher = nil -- AI.AI_A2G_Dispatcher#AI_A2G_DISPATCHER
  
  --- TASK_CAPTURE_DISPATCHER constructor.
  -- @param #TASK_CAPTURE_DISPATCHER self
  -- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done.
  -- @param Core.Set#SET_GROUP SetGroup The set of groups that can join the tasks within the mission.
  -- @return #TASK_CAPTURE_DISPATCHER self
  function TASK_CAPTURE_DISPATCHER:New( Mission, SetGroup )
  
    -- Inherits from DETECTION_MANAGER
    local self = BASE:Inherit( self, TASK_MANAGER:New( SetGroup ) ) -- #TASK_CAPTURE_DISPATCHER
    
    self.Mission = Mission
    self.FlashNewTask = false
    
    self:AddTransition( "Started", "Assign", "Started" )
    self:AddTransition( "Started", "ZoneCaptured", "Started" )
    
    self:__StartTasks( 5 )
    
    return self
  end


  --- Link a task capture dispatcher from the other coalition to understand its plan for defenses.
  -- This is used for the tactical overview, so the players also know the zones attacked by the other coalition!
  -- @param #TASK_CAPTURE_DISPATCHER self
  -- @param #TASK_CAPTURE_DISPATCHER DefenseTaskCaptureDispatcher
  function TASK_CAPTURE_DISPATCHER:SetDefenseTaskCaptureDispatcher( DefenseTaskCaptureDispatcher )
  
    self.DefenseTaskCaptureDispatcher = DefenseTaskCaptureDispatcher
  end


  --- Get the linked task capture dispatcher from the other coalition to understand its plan for defenses.
  -- This is used for the tactical overview, so the players also know the zones attacked by the other coalition!
  -- @param #TASK_CAPTURE_DISPATCHER self
  -- @return #TASK_CAPTURE_DISPATCHER
  function TASK_CAPTURE_DISPATCHER:GetDefenseTaskCaptureDispatcher()
  
    return self.DefenseTaskCaptureDispatcher
  end


  --- Link an AI A2G dispatcher from the other coalition to understand its plan for defenses.
  -- This is used for the tactical overview, so the players also know the zones attacked by the other AI A2G dispatcher!
  -- @param #TASK_CAPTURE_DISPATCHER self
  -- @param AI.AI_A2G_Dispatcher#AI_A2G_DISPATCHER DefenseAIA2GDispatcher
  function TASK_CAPTURE_DISPATCHER:SetDefenseAIA2GDispatcher( DefenseAIA2GDispatcher )
  
    self.DefenseAIA2GDispatcher = DefenseAIA2GDispatcher
  end


  --- Get the linked AI A2G dispatcher from the other coalition to understand its plan for defenses.
  -- This is used for the tactical overview, so the players also know the zones attacked by the AI A2G dispatcher!
  -- @param #TASK_CAPTURE_DISPATCHER self
  -- @return AI.AI_A2G_Dispatcher#AI_A2G_DISPATCHER
  function TASK_CAPTURE_DISPATCHER:GetDefenseAIA2GDispatcher()
  
    return self.DefenseAIA2GDispatcher
  end


  --- Add a capture zone task.
  -- @param #TASK_CAPTURE_DISPATCHER self
  -- @param #string TaskPrefix (optional) The prefix of the capture zone task. 
  -- If no TaskPrefix is given, then "Capture" will be used as the TaskPrefix. 
  -- The TaskPrefix will be appended with a . + a number of 3 digits, if the TaskPrefix already exists in the task collection.
  -- @param Functional.CaptureZoneCoalition#ZONE_CAPTURE_COALITION CaptureZone The zone of the coalition to be captured as the task goal.
  -- @param #string Briefing The briefing of the task to be shown to the player.
  -- @return Tasking.Task_Capture_Zone#TASK_CAPTURE_ZONE
  -- @usage
  -- 
  --  
  function TASK_CAPTURE_DISPATCHER:AddCaptureZoneTask( TaskPrefix, CaptureZone, Briefing )

    local TaskName = TaskPrefix or "Capture"
    if self.Zones[TaskName] then
      self.ZoneCount = self.ZoneCount + 1
      TaskName = string.format( "%s.%03d", TaskName, self.ZoneCount )
    end
    
    self.Zones[TaskName] = {} 
    self.Zones[TaskName].CaptureZone = CaptureZone
    self.Zones[TaskName].Briefing = Briefing
    self.Zones[TaskName].Task = nil
    self.Zones[TaskName].TaskPrefix = TaskPrefix
    
    self:ManageTasks()
    
    return self.Zones[TaskName] and self.Zones[TaskName].Task
  end


  --- Link an AI_A2G_DISPATCHER to the TASK_CAPTURE_DISPATCHER.
  -- @param #TASK_CAPTURE_DISPATCHER self
  -- @param AI.AI_A2G_Dispatcher#AI_A2G_DISPATCHER AI_A2G_Dispatcher The AI Dispatcher to be linked to the tasking. 
  -- @return Tasking.Task_Capture_Zone#TASK_CAPTURE_ZONE
  function TASK_CAPTURE_DISPATCHER:Link_AI_A2G_Dispatcher( AI_A2G_Dispatcher )

    self.AI_A2G_Dispatcher = AI_A2G_Dispatcher -- AI.AI_A2G_Dispatcher#AI_A2G_DISPATCHER
    AI_A2G_Dispatcher.Detection:LockDetectedItems()

    return self
  end


  --- Assigns tasks to the @{Core.Set#SET_GROUP}.
  -- @param #TASK_CAPTURE_DISPATCHER self
  -- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop.
  function TASK_CAPTURE_DISPATCHER:ManageTasks()
    self:F()
  
    local AreaMsg = {}
    local TaskMsg = {}
    local ChangeMsg = {}
    
    local Mission = self.Mission
    
    if Mission:IsIDLE() or Mission:IsENGAGED() then
    
      local TaskReport = REPORT:New()
      
      -- Checking the task queue for the dispatcher, and removing any obsolete task!
      for TaskIndex, TaskData in pairs( self.Tasks ) do
        local Task = TaskData -- Tasking.Task#TASK
        if Task:IsStatePlanned() then
        -- Here we need to check if the pilot is still existing.
--            Task = self:RemoveTask( TaskIndex )
        end
        
      end

      -- Now that all obsolete tasks are removed, loop through the Zone tasks.
      for TaskName, CaptureZone in pairs( self.Zones ) do
        
        if not CaptureZone.Task then
          -- New Transport Task
          CaptureZone.Task = TASK_CAPTURE_ZONE:New( Mission, self.SetGroup, TaskName, CaptureZone.CaptureZone, CaptureZone.Briefing )
          CaptureZone.Task.TaskPrefix = CaptureZone.TaskPrefix -- We keep the TaskPrefix for further reference!
          Mission:AddTask( CaptureZone.Task )
          TaskReport:Add( TaskName )
          
          -- Link the Task Dispatcher to the capture zone task, because it is used on the UpdateTaskInfo.
          CaptureZone.Task:SetDispatcher( self )
          CaptureZone.Task:UpdateTaskInfo()

          function CaptureZone.Task.OnEnterAssigned( Task, From, Event, To )
            if self.AI_A2G_Dispatcher then
              self.AI_A2G_Dispatcher:Unlock( Task.TaskZoneName ) -- This will unlock the zone to be defended by AI.
            end        
            CaptureZone.Task:UpdateTaskInfo()
            CaptureZone.Task.ZoneGoal.Attacked = true
          end
          
          function CaptureZone.Task.OnEnterSuccess( Task, From, Event, To )
            --self:Success( Task )
            if self.AI_A2G_Dispatcher then
              self.AI_A2G_Dispatcher:Lock( Task.TaskZoneName ) -- This will lock the zone from being defended by AI.
            end
            CaptureZone.Task:UpdateTaskInfo()
            CaptureZone.Task.ZoneGoal.Attacked = false
          end

          function CaptureZone.Task.OnEnterCancelled( Task, From, Event, To )
            self:Cancelled( Task )
            if self.AI_A2G_Dispatcher then
              self.AI_A2G_Dispatcher:Lock( Task.TaskZoneName ) -- This will lock the zone from being defended by AI.
            end
            CaptureZone.Task:UpdateTaskInfo()
            CaptureZone.Task.ZoneGoal.Attacked = false
          end
            
          function CaptureZone.Task.OnEnterFailed( Task, From, Event, To )
            self:Failed( Task )
            if self.AI_A2G_Dispatcher then
              self.AI_A2G_Dispatcher:Lock( Task.TaskZoneName ) -- This will lock the zone from being defended by AI.
            end
            CaptureZone.Task:UpdateTaskInfo()
            CaptureZone.Task.ZoneGoal.Attacked = false
          end

          function CaptureZone.Task.OnEnterAborted( Task, From, Event, To )
            self:Aborted( Task )
            if self.AI_A2G_Dispatcher then
              self.AI_A2G_Dispatcher:Lock( Task.TaskZoneName ) -- This will lock the zone from being defended by AI.
            end
            CaptureZone.Task:UpdateTaskInfo()
            CaptureZone.Task.ZoneGoal.Attacked = false
          end

          -- Now broadcast the onafterCargoPickedUp event to the Task Cargo Dispatcher.
          function CaptureZone.Task.OnAfterCaptured( Task, From, Event, To, TaskUnit )
            self:Captured( Task, Task.TaskPrefix, TaskUnit )
            if self.AI_A2G_Dispatcher then
              self.AI_A2G_Dispatcher:Lock( Task.TaskZoneName ) -- This will lock the zone from being defended by AI.
            end
            CaptureZone.Task:UpdateTaskInfo()
            CaptureZone.Task.ZoneGoal.Attacked = false
          end

        end
        
      end
      
      
      -- TODO set menus using the HQ coordinator
      Mission:GetCommandCenter():SetMenu()

      local TaskText = TaskReport:Text(", ")
      
      for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
        if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" and ( not self.FlashNewTask ) then
          Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup )
        end
      end
      
    end
    
    return true
  end

end
