--- **Functional** - Monitor airbase traffic and regulate speed while taxiing.
--
-- ===
--
-- ## Features:
-- 
--   * Monitor speed of the airplanes of players during taxi.
--   * Communicate ATC ground operations.
--   * Kick speeding players during taxi.
-- 
-- ===
-- 
-- ## Missions:
-- 
-- [ABP - Airbase Police](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/ABP%20-%20Airbase%20Police)
--
-- ===
--
-- ### Contributions: Dutch Baron - Concept & Testing
-- ### Author: FlightControl - Framework Design &  Programming
-- ### Refactoring to use the Runway auto-detection: Applevangelist
-- @date August 2022
--
-- ===
-- 
-- @module Functional.ATC_Ground
-- @image Air_Traffic_Control_Ground_Operations.JPG

---
-- @type ATC_GROUND
-- @field Core.Set#SET_CLIENT SetClient
-- @extends Core.Base#BASE

--- [DEPRECATED, use ATC_GROUND_UNIVERSAL] Base class for ATC\_GROUND implementations.
-- @field #ATC_GROUND
ATC_GROUND = {
  ClassName = "ATC_GROUND",
  SetClient = nil,
  Airbases = nil,
  AirbaseNames = nil,
}

---
-- @type ATC_GROUND.AirbaseNames
-- @list <#string>


--- [DEPRECATED, use ATC_GROUND_UNIVERSAL] Creates a new ATC\_GROUND object.
-- @param #ATC_GROUND self
-- @param Airbases A table of Airbase Names.
-- @return #ATC_GROUND self
function ATC_GROUND:New( Airbases, AirbaseList )

  -- Inherits from BASE
  local self = BASE:Inherit( self, BASE:New() ) -- #ATC_GROUND
  self:E( { self.ClassName, Airbases } )

  self.Airbases = Airbases
  self.AirbaseList = AirbaseList
  
  self.SetClient = SET_CLIENT:New():FilterCategories( "plane" ):FilterStart()
  

  for AirbaseID, Airbase in pairs( self.Airbases ) do
    -- Specified ZoneBoundary is used if set or Airbase radius by default
    if Airbase.ZoneBoundary then
      Airbase.ZoneBoundary = ZONE_POLYGON_BASE:New( "Boundary " .. AirbaseID, Airbase.ZoneBoundary )
    else
      Airbase.ZoneBoundary = _DATABASE:FindAirbase( AirbaseID ):GetZone()
    end

    Airbase.ZoneRunways = {}
    if Airbase.PointsRunways then
      for PointsRunwayID, PointsRunway in pairs( Airbase.PointsRunways ) do
        Airbase.ZoneRunways[PointsRunwayID] = ZONE_POLYGON_BASE:New( "Runway " .. PointsRunwayID, PointsRunway )
      end
    end
    Airbase.Monitor = self.AirbaseList and false or true -- When AirbaseList is not given, monitor every Airbase, otherwise don't monitor any (yet). 
  end

  -- Now activate the monitoring for the airbases that need to be monitored.
  for AirbaseID, AirbaseName in pairs( self.AirbaseList or {} ) do
    self.Airbases[AirbaseName].Monitor = true
  end

  self.SetClient:ForEachClient(
    -- @param Wrapper.Client#CLIENT Client
    function( Client )
      Client:SetState( self, "Speeding", false )
      Client:SetState( self, "Warnings", 0)
      Client:SetState( self, "IsOffRunway", false )
      Client:SetState( self, "OffRunwayWarnings", 0 )
      Client:SetState( self, "Taxi", false )
    end
  )

  -- This is simple slot blocker is used on the server.  
  SSB = USERFLAG:New( "SSB" )
  SSB:Set( 100 )
  
  return self
end

--- Smoke the airbases runways.
-- @param #ATC_GROUND self
-- @param Utilities.Utils#SMOKECOLOR SmokeColor The color of the smoke around the runways.
-- @return #ATC_GROUND self
function ATC_GROUND:SmokeRunways( SmokeColor )

  for AirbaseID, Airbase in pairs( self.Airbases ) do
    for PointsRunwayID, PointsRunway in pairs( Airbase.PointsRunways ) do
      Airbase.ZoneRunways[PointsRunwayID]:SmokeZone( SmokeColor )
      end
  end
end

--- Set the maximum speed in meters per second (Mps) until the player gets kicked.
-- An airbase can be specified to set the kick speed for.
-- @param #ATC_GROUND self
-- @param #number KickSpeed The speed in Mps.
-- @param Wrapper.Airbase#AIRBASE Airbase (optional) The airbase to set the kick speed for.
-- @return #ATC_GROUND self
-- @usage
-- 
--   -- Declare Atc_Ground using one of those, depending on the map.
-- 
--   Atc_Ground = ATC_GROUND_CAUCAUS:New()
--   Atc_Ground = ATC_GROUND_NEVADA:New()
--   Atc_Ground = ATC_GROUND_NORMANDY:New()
--   Atc_Ground = ATC_GROUND_PERSIANGULF:New()
--   
--   -- Then use one of these methods...
-- 
--   Atc_Ground:SetKickSpeed( UTILS.KmphToMps( 80 ) ) -- Kick the players at 80 kilometers per hour
-- 
--   Atc_Ground:SetKickSpeed( UTILS.MiphToMps( 100 ) ) -- Kick the players at 100 miles per hour
-- 
--   Atc_Ground:SetKickSpeed( 24 ) -- Kick the players at 24 meters per second ( 24 * 3.6 = 86.4 kilometers per hour )
-- 
function ATC_GROUND:SetKickSpeed( KickSpeed, Airbase )

  if not Airbase then
    self.KickSpeed = KickSpeed
  else
    self.Airbases[Airbase].KickSpeed = KickSpeed
  end
  
  return self
end

--- Set the maximum speed in Kmph until the player gets kicked.
-- @param #ATC_GROUND self
-- @param #number KickSpeed Set the speed in Kmph.
-- @param Wrapper.Airbase#AIRBASE Airbase (optional) The airbase to set the kick speed for.
-- @return #ATC_GROUND self
-- 
--   Atc_Ground:SetKickSpeedKmph( 80 ) -- Kick the players at 80 kilometers per hour
-- 
function ATC_GROUND:SetKickSpeedKmph( KickSpeed, Airbase )

  self:SetKickSpeed( UTILS.KmphToMps( KickSpeed ), Airbase )
  
  return self
end

--- Set the maximum speed in Miph until the player gets kicked.
-- @param #ATC_GROUND self
-- @param #number KickSpeedMiph Set the speed in Mph.
-- @param Wrapper.Airbase#AIRBASE Airbase (optional) The airbase to set the kick speed for.
-- @return #ATC_GROUND self
-- 
--   Atc_Ground:SetKickSpeedMiph( 100 ) -- Kick the players at 100 miles per hour
-- 
function ATC_GROUND:SetKickSpeedMiph( KickSpeedMiph, Airbase )

  self:SetKickSpeed( UTILS.MiphToMps( KickSpeedMiph ), Airbase )
  
  return self
end


--- Set the maximum kick speed in meters per second (Mps) until the player gets kicked.
-- There are no warnings given if this speed is reached, and is to prevent players to take off from the airbase!
-- An airbase can be specified to set the maximum kick speed for.
-- @param #ATC_GROUND self
-- @param #number MaximumKickSpeed The speed in Mps.
-- @param Wrapper.Airbase#AIRBASE Airbase (optional) The airbase to set the kick speed for.
-- @return #ATC_GROUND self
-- @usage
-- 
--   -- Declare Atc_Ground using one of those, depending on the map.
-- 
--   Atc_Ground = ATC_GROUND_CAUCAUS:New()
--   Atc_Ground = ATC_GROUND_NEVADA:New()
--   Atc_Ground = ATC_GROUND_NORMANDY:New()
--   Atc_Ground = ATC_GROUND_PERSIANGULF:New()
--   
--   -- Then use one of these methods...
-- 
--   Atc_Ground:SetMaximumKickSpeed( UTILS.KmphToMps( 80 ) ) -- Kick the players at 80 kilometers per hour
-- 
--   Atc_Ground:SetMaximumKickSpeed( UTILS.MiphToMps( 100 ) ) -- Kick the players at 100 miles per hour
-- 
--   Atc_Ground:SetMaximumKickSpeed( 24 ) -- Kick the players at 24 meters per second ( 24 * 3.6 = 86.4 kilometers per hour )
-- 
function ATC_GROUND:SetMaximumKickSpeed( MaximumKickSpeed, Airbase )

  if not Airbase then
    self.MaximumKickSpeed = MaximumKickSpeed
  else
    self.Airbases[Airbase].MaximumKickSpeed = MaximumKickSpeed
  end
  
  return self
end

--- Set the maximum kick speed in kilometers per hour (Kmph) until the player gets kicked.
-- There are no warnings given if this speed is reached, and is to prevent players to take off from the airbase!
-- An airbase can be specified to set the maximum kick speed for.
-- @param #ATC_GROUND self
-- @param #number MaximumKickSpeed Set the speed in Kmph.
-- @param Wrapper.Airbase#AIRBASE Airbase (optional) The airbase to set the kick speed for.
-- @return #ATC_GROUND self
-- 
--   Atc_Ground:SetMaximumKickSpeedKmph( 150 ) -- Kick the players at 150 kilometers per hour
-- 
function ATC_GROUND:SetMaximumKickSpeedKmph( MaximumKickSpeed, Airbase )

  self:SetMaximumKickSpeed( UTILS.KmphToMps( MaximumKickSpeed ), Airbase )
  
  return self
end

--- Set the maximum kick speed in miles per hour (Miph) until the player gets kicked.
-- There are no warnings given if this speed is reached, and is to prevent players to take off from the airbase!
-- An airbase can be specified to set the maximum kick speed for.
-- @param #ATC_GROUND self
-- @param #number MaximumKickSpeedMiph Set the speed in Mph.
-- @param Wrapper.Airbase#AIRBASE Airbase (optional) The airbase to set the kick speed for.
-- @return #ATC_GROUND self
-- 
--   Atc_Ground:SetMaximumKickSpeedMiph( 100 ) -- Kick the players at 100 miles per hour
-- 
function ATC_GROUND:SetMaximumKickSpeedMiph( MaximumKickSpeedMiph, Airbase )

  self:SetMaximumKickSpeed( UTILS.MiphToMps( MaximumKickSpeedMiph ), Airbase )
  
  return self
end

-- @param #ATC_GROUND self
function ATC_GROUND:_AirbaseMonitor()

  self.SetClient:ForEachClient(
    -- @param Wrapper.Client#CLIENT Client
    function( Client )

      if Client:IsAlive() then

        local IsOnGround = Client:InAir() == false

        for AirbaseID, AirbaseMeta in pairs( self.Airbases ) do
          self:E( AirbaseID, AirbaseMeta.KickSpeed )
  
          if AirbaseMeta.Monitor == true and Client:IsInZone( AirbaseMeta.ZoneBoundary )  then

            local NotInRunwayZone = true
            for ZoneRunwayID, ZoneRunway in pairs( AirbaseMeta.ZoneRunways ) do
              NotInRunwayZone = ( Client:IsNotInZone( ZoneRunway ) == true ) and NotInRunwayZone or false
            end

            if NotInRunwayZone then
              
              if IsOnGround then
                local Taxi = Client:GetState( self, "Taxi" )
                self:E( Taxi )
                if Taxi == false then
                  local Velocity = VELOCITY:New( AirbaseMeta.KickSpeed or self.KickSpeed )
                  Client:Message( "Welcome to " .. AirbaseID .. ". The maximum taxiing speed is " .. 
                                  Velocity:ToString() , 20, "ATC" )
                  Client:SetState( self, "Taxi", true )
                end
  
                -- TODO: GetVelocityKMH function usage
                local Velocity = VELOCITY_POSITIONABLE:New( Client )
                --MESSAGE:New( "Velocity = " .. Velocity:ToString(), 1 ):ToAll()
                local IsAboveRunway = Client:IsAboveRunway()
                self:T( IsAboveRunway, IsOnGround )
  
                if IsOnGround then
                  local Speeding = false
                  if AirbaseMeta.MaximumKickSpeed then 
                    if Velocity:Get() > AirbaseMeta.MaximumKickSpeed then
                      Speeding = true
                    end
                  else
                    if Velocity:Get() > self.MaximumKickSpeed then
                      Speeding = true
                    end
                  end
                  if Speeding == true then
                    MESSAGE:New( "Penalty! Player " .. Client:GetPlayerName() .. 
                                 " has been kicked, due to a severe airbase traffic rule violation ...", 10, "ATC" ):ToAll()
                    Client:Destroy()
                    Client:SetState( self, "Speeding", false )
                    Client:SetState( self, "Warnings", 0 )
                  end
                end                  
                  
  
                if IsOnGround then
  
                  local Speeding = false
                  if AirbaseMeta.KickSpeed then -- If there is a speed defined for the airbase, use that only.
                    if Velocity:Get() > AirbaseMeta.KickSpeed then
                      Speeding = true
                    end
                  else
                    if Velocity:Get() > self.KickSpeed then
                      Speeding = true
                    end
                  end
                  if Speeding == true then  
                    local IsSpeeding = Client:GetState( self, "Speeding" )
  
                    if IsSpeeding == true then
                      local SpeedingWarnings = Client:GetState( self, "Warnings" )
                      self:T( SpeedingWarnings )
  
                      if SpeedingWarnings <= 3 then
                        Client:Message( "Warning " .. SpeedingWarnings .. "/3! Airbase traffic rule violation! Slow down now! Your speed is " .. 
                                        Velocity:ToString(), 5, "ATC" )
                        Client:SetState( self, "Warnings", SpeedingWarnings + 1 )
                      else
                        MESSAGE:New( "Penalty! Player " .. Client:GetPlayerName() .. " has been kicked, due to a severe airbase traffic rule violation ...", 10, "ATC" ):ToAll()
                        -- @param Wrapper.Client#CLIENT Client
                        Client:Destroy()
                        Client:SetState( self, "Speeding", false )
                        Client:SetState( self, "Warnings", 0 )
                      end
  
                    else
                      Client:Message( "Attention! You are speeding on the taxiway, slow down! Your speed is " .. 
                                      Velocity:ToString(), 5, "ATC" )
                      Client:SetState( self, "Speeding", true )
                      Client:SetState( self, "Warnings", 1 )
                    end
  
                  else
                    Client:SetState( self, "Speeding", false )
                    Client:SetState( self, "Warnings", 0 )
                  end
                end

                if IsOnGround and not IsAboveRunway then
  
                  local IsOffRunway = Client:GetState( self, "IsOffRunway" )
  
                  if IsOffRunway == true then
                    local OffRunwayWarnings = Client:GetState( self, "OffRunwayWarnings" )
                    self:T( OffRunwayWarnings )
  
                    if OffRunwayWarnings <= 3 then
                      Client:Message( "Warning " .. OffRunwayWarnings .. "/3! Airbase traffic rule violation! Get back on the taxi immediately!", 5, "ATC" )
                      Client:SetState( self, "OffRunwayWarnings", OffRunwayWarnings + 1 )
                    else
                      MESSAGE:New( "Penalty! Player " .. Client:GetPlayerName() .. " has been kicked, due to a severe airbase traffic rule violation ...", 10, "ATC" ):ToAll()
                      -- @param Wrapper.Client#CLIENT Client
                      Client:Destroy()
                      Client:SetState( self, "IsOffRunway", false )
                      Client:SetState( self, "OffRunwayWarnings", 0 )
                    end
                  else
                    Client:Message( "Attention! You are off the taxiway. Get back on the taxiway immediately!", 5, "ATC" )
                    Client:SetState( self, "IsOffRunway", true )
                    Client:SetState( self, "OffRunwayWarnings", 1 )
                  end
  
                else
                  Client:SetState( self, "IsOffRunway", false )
                  Client:SetState( self, "OffRunwayWarnings", 0 )
                end
              end
            else
              Client:SetState( self, "Speeding", false )
              Client:SetState( self, "Warnings", 0 )
              Client:SetState( self, "IsOffRunway", false )
              Client:SetState( self, "OffRunwayWarnings", 0 )
              local Taxi = Client:GetState( self, "Taxi" )
              if Taxi == true then
                Client:Message( "You have progressed to the runway ... Await take-off clearance ...", 20, "ATC" )
                Client:SetState( self, "Taxi", false )
              end
            end
          end
        end
      else
        Client:SetState( self, "Taxi", false )
      end
    end
  )

  return true
end

---
-- @type ATC_GROUND_UNIVERSAL
-- @field Core.Set#SET_CLIENT SetClient
-- @field #string Version
-- @field #string ClassName
-- @field #table Airbases
-- @field #table AirbaseList
-- @field #number KickSpeed
-- @extends Core.Base#BASE

--- Base class for ATC\_GROUND\_UNIVERSAL implementations.
-- @field #ATC_GROUND_UNIVERSAL
ATC_GROUND_UNIVERSAL = {
  ClassName = "ATC_GROUND_UNIVERSAL",
  Version = "0.0.1",
  SetClient = nil,
  Airbases = nil,
  AirbaseList = nil,
  KickSpeed = nil, -- The maximum speed in meters per second for all airbases until a player gets kicked. This is overridden at each derived class.
}

--- Creates a new ATC\_GROUND\_UNIVERSAL object. This works on any map.
-- @param #ATC_GROUND_UNIVERSAL self
-- @param AirbaseList (Optional) A table of Airbase Names.
-- @return #ATC_GROUND_UNIVERSAL self
function ATC_GROUND_UNIVERSAL:New(AirbaseList)

  -- Inherits from BASE
  local self = BASE:Inherit( self, BASE:New() ) -- #ATC_GROUND
  self:E( { self.ClassName } )

  self.Airbases = {}

  for _name,_ in pairs(_DATABASE.AIRBASES) do
    self.Airbases[_name]={}  
  end
  
  self.AirbaseList = AirbaseList
  
  self.SetClient = SET_CLIENT:New():FilterCategories( "plane" ):FilterStart()
  

  for AirbaseID, Airbase in pairs( self.Airbases ) do
    -- Specified ZoneBoundary is used if set or Airbase radius by default
    if Airbase.ZoneBoundary then
      Airbase.ZoneBoundary = ZONE_POLYGON_BASE:New( "Boundary " .. AirbaseID, Airbase.ZoneBoundary )
    else
      Airbase.ZoneBoundary = _DATABASE:FindAirbase( AirbaseID ):GetZone()
    end

    Airbase.ZoneRunways = AIRBASE:FindByName(AirbaseID):GetRunways()
    Airbase.Monitor = self.AirbaseList and false or true -- When AirbaseList is not given, monitor every Airbase, otherwise don't monitor any (yet). 
  end

  -- Now activate the monitoring for the airbases that need to be monitored.
  for AirbaseID, AirbaseName in pairs( self.AirbaseList or {} ) do
    self.Airbases[AirbaseName].Monitor = true
  end

  self.SetClient:ForEachClient(
    -- @param Wrapper.Client#CLIENT Client
    function( Client )
      Client:SetState( self, "Speeding", false )
      Client:SetState( self, "Warnings", 0)
      Client:SetState( self, "IsOffRunway", false )
      Client:SetState( self, "OffRunwayWarnings", 0 )
      Client:SetState( self, "Taxi", false )
    end
  )

  -- This is simple slot blocker is used on the server.  
  SSB = USERFLAG:New( "SSB" )
  SSB:Set( 100 )
  
  -- Kickspeed
  self.KickSpeed = UTILS.KnotsToMps(10)
  self:SetMaximumKickSpeedMiph(30)
  
  return self
end


--- Add a specific Airbase Boundary if you don't want to use the round zone that is auto-created.
-- @param #ATC_GROUND_UNIVERSAL self
-- @param #string Airbase The name of the Airbase
-- @param Core.Zone#ZONE Zone The ZONE object to be used, e.g. a ZONE_POLYGON
-- @return #ATC_GROUND_UNIVERSAL self
function ATC_GROUND_UNIVERSAL:SetAirbaseBoundaries(Airbase, Zone)
  self.Airbases[Airbase].ZoneBoundary = Zone
  return self
end

--- Smoke the airbases runways.
-- @param #ATC_GROUND_UNIVERSAL self
-- @param Utilities.Utils#SMOKECOLOR SmokeColor The color of the smoke around the runways.
-- @return #ATC_GROUND_UNIVERSAL self
function ATC_GROUND_UNIVERSAL:SmokeRunways( SmokeColor )
  
  local SmokeColor = SmokeColor or SMOKECOLOR.Red
  for AirbaseID, Airbase in pairs( self.Airbases ) do
    if Airbase.ZoneRunways then
      for _,_runwaydata in pairs (Airbase.ZoneRunways) do
        local runwaydata = _runwaydata -- Wrapper.Airbase#AIRBASE.Runway
        runwaydata.zone:SmokeZone(SmokeColor)
      end
    end
  end
  
  return self
end

--- Draw the airbases runways.
-- @param #ATC_GROUND_UNIVERSAL self
-- @param #table Color The color of the line around the runways, in RGB, e.g `{1,0,0}` for red.
-- @return #ATC_GROUND_UNIVERSAL self
function ATC_GROUND_UNIVERSAL:DrawRunways( Color )
  
  local Color = Color or {1,0,0}
  for AirbaseID, Airbase in pairs( self.Airbases ) do
    if Airbase.ZoneRunways then
      for _,_runwaydata in pairs (Airbase.ZoneRunways) do
        local runwaydata = _runwaydata -- Wrapper.Airbase#AIRBASE.Runway
        runwaydata.zone:DrawZone(-1,Color)
      end
    end
  end
  
  return self
end

--- Draw the airbases boundaries.
-- @param #ATC_GROUND_UNIVERSAL self
-- @param #table Color The color of the line around the runways, in RGB, e.g `{1,0,0}` for red.
-- @return #ATC_GROUND_UNIVERSAL self
function ATC_GROUND_UNIVERSAL:DrawBoundaries( Color )
  
  local Color = Color or {1,0,0}
  for AirbaseID, Airbase in pairs( self.Airbases ) do
    if Airbase.ZoneBoundary then
       Airbase.ZoneBoundary:DrawZone(-1, Color) 
    end
  end
  
  return self
end

--- Set the maximum speed in meters per second (Mps) until the player gets kicked.
-- An airbase can be specified to set the kick speed for.
-- @param #ATC_GROUND_UNIVERSAL self
-- @param #number KickSpeed The speed in Mps.
-- @param #string Airbase (optional) The airbase name to set the kick speed for.
-- @return #ATC_GROUND_UNIVERSAL self
-- @usage
-- 
--   -- Declare Atc_Ground
-- 
--   Atc_Ground = ATC_GROUND_UNIVERSAL:New()
--   
--   -- Then use one of these methods...
-- 
--   Atc_Ground:SetKickSpeed( UTILS.KmphToMps( 80 ) ) -- Kick the players at 80 kilometers per hour
-- 
--   Atc_Ground:SetKickSpeed( UTILS.MiphToMps( 100 ) ) -- Kick the players at 100 miles per hour
-- 
--   Atc_Ground:SetKickSpeed( 24 ) -- Kick the players at 24 meters per second ( 24 * 3.6 = 86.4 kilometers per hour )
-- 
function ATC_GROUND_UNIVERSAL:SetKickSpeed( KickSpeed, Airbase )

  if not Airbase then
    self.KickSpeed = KickSpeed
  else
    self.Airbases[Airbase].KickSpeed = KickSpeed
  end
  
  return self
end

--- Set the maximum speed in Kmph until the player gets kicked.
-- @param #ATC_GROUND_UNIVERSAL self
-- @param #number KickSpeed Set the speed in Kmph.
-- @param #string Airbase (optional) The airbase name to set the kick speed for.
-- @return #ATC_GROUND_UNIVERSAL self
-- 
--   Atc_Ground:SetKickSpeedKmph( 80 ) -- Kick the players at 80 kilometers per hour
-- 
function ATC_GROUND_UNIVERSAL:SetKickSpeedKmph( KickSpeed, Airbase )

  self:SetKickSpeed( UTILS.KmphToMps( KickSpeed ), Airbase )
  
  return self
end

--- Set the maximum speed in Miph until the player gets kicked.
-- @param #ATC_GROUND_UNIVERSAL self
-- @param #number KickSpeedMiph Set the speed in Mph.
-- @param #string Airbase (optional) The airbase name to set the kick speed for.
-- @return #ATC_GROUND_UNIVERSAL self
-- 
--   Atc_Ground:SetKickSpeedMiph( 100 ) -- Kick the players at 100 miles per hour
-- 
function ATC_GROUND_UNIVERSAL:SetKickSpeedMiph( KickSpeedMiph, Airbase )

  self:SetKickSpeed( UTILS.MiphToMps( KickSpeedMiph ), Airbase )
  
  return self
end


--- Set the maximum kick speed in meters per second (Mps) until the player gets kicked.
-- There are no warnings given if this speed is reached, and is to prevent players to take off from the airbase!
-- An airbase can be specified to set the maximum kick speed for.
-- @param #ATC_GROUND_UNIVERSAL self
-- @param #number MaximumKickSpeed The speed in Mps.
-- @param #string Airbase (optional) The airbase name to set the kick speed for.
-- @return #ATC_GROUND_UNIVERSAL self
-- @usage
-- 
--   -- Declare Atc_Ground
-- 
--   Atc_Ground = ATC_GROUND_UNIVERSAL:New()
--   
--   -- Then use one of these methods...
-- 
--   Atc_Ground:SetMaximumKickSpeed( UTILS.KmphToMps( 80 ) ) -- Kick the players at 80 kilometers per hour
-- 
--   Atc_Ground:SetMaximumKickSpeed( UTILS.MiphToMps( 100 ) ) -- Kick the players at 100 miles per hour
-- 
--   Atc_Ground:SetMaximumKickSpeed( 24 ) -- Kick the players at 24 meters per second ( 24 * 3.6 = 86.4 kilometers per hour )
-- 
function ATC_GROUND_UNIVERSAL:SetMaximumKickSpeed( MaximumKickSpeed, Airbase )

  if not Airbase then
    self.MaximumKickSpeed = MaximumKickSpeed
  else
    self.Airbases[Airbase].MaximumKickSpeed = MaximumKickSpeed
  end
  
  return self
end

--- Set the maximum kick speed in kilometers per hour (Kmph) until the player gets kicked.
-- There are no warnings given if this speed is reached, and is to prevent players to take off from the airbase!
-- An airbase can be specified to set the maximum kick speed for.
-- @param #ATC_GROUND_UNIVERSAL self
-- @param #number MaximumKickSpeed Set the speed in Kmph.
-- @param #string Airbase (optional) The airbase name to set the kick speed for.
-- @return #ATC_GROUND_UNIVERSAL self
-- 
--   Atc_Ground:SetMaximumKickSpeedKmph( 150 ) -- Kick the players at 150 kilometers per hour
-- 
function ATC_GROUND_UNIVERSAL:SetMaximumKickSpeedKmph( MaximumKickSpeed, Airbase )

  self:SetMaximumKickSpeed( UTILS.KmphToMps( MaximumKickSpeed ), Airbase )
  
  return self
end

--- Set the maximum kick speed in miles per hour (Miph) until the player gets kicked.
-- There are no warnings given if this speed is reached, and is to prevent players to take off from the airbase!
-- An airbase can be specified to set the maximum kick speed for.
-- @param #ATC_GROUND_UNIVERSAL self
-- @param #number MaximumKickSpeedMiph Set the speed in Mph.
-- @param #string Airbase (optional) The airbase name to set the kick speed for.
-- @return #ATC_GROUND_UNIVERSAL self
-- 
--   Atc_Ground:SetMaximumKickSpeedMiph( 100 ) -- Kick the players at 100 miles per hour
-- 
function ATC_GROUND_UNIVERSAL:SetMaximumKickSpeedMiph( MaximumKickSpeedMiph, Airbase )

  self:SetMaximumKickSpeed( UTILS.MiphToMps( MaximumKickSpeedMiph ), Airbase )
  
  return self
end

--- [Internal] Monitoring function
-- @param #ATC_GROUND_UNIVERSAL self
-- @return #ATC_GROUND_UNIVERSAL self
function ATC_GROUND_UNIVERSAL:_AirbaseMonitor()

  self.SetClient:ForEachClient(
    -- @param Wrapper.Client#CLIENT Client
    function( Client )

      if Client:IsAlive() then

        local IsOnGround = Client:InAir() == false

        for AirbaseID, AirbaseMeta in pairs( self.Airbases ) do
          self:E( AirbaseID, AirbaseMeta.KickSpeed )
  
          if AirbaseMeta.Monitor == true and Client:IsInZone( AirbaseMeta.ZoneBoundary )  then

            local NotInRunwayZone = true
            
            if AirbaseMeta.ZoneRunways then
              for _,_runwaydata in pairs (AirbaseMeta.ZoneRunways) do
                local runwaydata = _runwaydata -- Wrapper.Airbase#AIRBASE.Runway
                  NotInRunwayZone = ( Client:IsNotInZone( _runwaydata.zone ) == true ) and NotInRunwayZone or false
              end
            end

            if NotInRunwayZone then
              
              if IsOnGround then
                local Taxi = Client:GetState( self, "Taxi" )
                self:E( Taxi )
                if Taxi == false then
                  local Velocity = VELOCITY:New( AirbaseMeta.KickSpeed or self.KickSpeed )
                  Client:Message( "Welcome to " .. AirbaseID .. ". The maximum taxiing speed is " .. 
                                  Velocity:ToString() , 20, "ATC" )
                  Client:SetState( self, "Taxi", true )
                end
  
                -- TODO: GetVelocityKMH function usage
                local Velocity = VELOCITY_POSITIONABLE:New( Client )
                --MESSAGE:New( "Velocity = " .. Velocity:ToString(), 1 ):ToAll()
                local IsAboveRunway = Client:IsAboveRunway()
                self:T( {IsAboveRunway, IsOnGround, Velocity:Get() })
  
                if IsOnGround then
                  local Speeding = false
                  if AirbaseMeta.MaximumKickSpeed then 
                    if Velocity:Get() > AirbaseMeta.MaximumKickSpeed then
                      Speeding = true
                    end
                  else
                    if Velocity:Get() > self.MaximumKickSpeed then
                      Speeding = true
                    end
                  end
                  if Speeding == true then
                    MESSAGE:New( "Penalty! Player " .. Client:GetPlayerName() .. 
                                 " has been kicked, due to a severe airbase traffic rule violation ...", 10, "ATC" ):ToAll()
                    Client:Destroy()
                    Client:SetState( self, "Speeding", false )
                    Client:SetState( self, "Warnings", 0 )
                  end
                end                  
                  
  
                if IsOnGround then
  
                  local Speeding = false
                  if AirbaseMeta.KickSpeed then -- If there is a speed defined for the airbase, use that only.
                    if Velocity:Get() > AirbaseMeta.KickSpeed then
                      Speeding = true
                    end
                  else
                    if Velocity:Get() > self.KickSpeed then
                      Speeding = true
                    end
                  end
                  if Speeding == true then  
                    local IsSpeeding = Client:GetState( self, "Speeding" )
  
                    if IsSpeeding == true then
                      local SpeedingWarnings = Client:GetState( self, "Warnings" )
                      self:T( SpeedingWarnings )
  
                      if SpeedingWarnings <= 3 then
                        Client:Message( "Warning " .. SpeedingWarnings .. "/3! Airbase traffic rule violation! Slow down now! Your speed is " .. 
                                        Velocity:ToString(), 5, "ATC" )
                        Client:SetState( self, "Warnings", SpeedingWarnings + 1 )
                      else
                        MESSAGE:New( "Penalty! Player " .. Client:GetPlayerName() .. " has been kicked, due to a severe airbase traffic rule violation ...", 10, "ATC" ):ToAll()
                        -- @param Wrapper.Client#CLIENT Client
                        Client:Destroy()
                        Client:SetState( self, "Speeding", false )
                        Client:SetState( self, "Warnings", 0 )
                      end
  
                    else
                      Client:Message( "Attention! You are speeding on the taxiway, slow down! Your speed is " .. 
                                      Velocity:ToString(), 5, "ATC" )
                      Client:SetState( self, "Speeding", true )
                      Client:SetState( self, "Warnings", 1 )
                    end
  
                  else
                    Client:SetState( self, "Speeding", false )
                    Client:SetState( self, "Warnings", 0 )
                  end
                end

                if IsOnGround and not IsAboveRunway then
  
                  local IsOffRunway = Client:GetState( self, "IsOffRunway" )
  
                  if IsOffRunway == true then
                    local OffRunwayWarnings = Client:GetState( self, "OffRunwayWarnings" )
                    self:T( OffRunwayWarnings )
  
                    if OffRunwayWarnings <= 3 then
                      Client:Message( "Warning " .. OffRunwayWarnings .. "/3! Airbase traffic rule violation! Get back on the taxi immediately!", 5, "ATC" )
                      Client:SetState( self, "OffRunwayWarnings", OffRunwayWarnings + 1 )
                    else
                      MESSAGE:New( "Penalty! Player " .. Client:GetPlayerName() .. " has been kicked, due to a severe airbase traffic rule violation ...", 10, "ATC" ):ToAll()
                      -- @param Wrapper.Client#CLIENT Client
                      Client:Destroy()
                      Client:SetState( self, "IsOffRunway", false )
                      Client:SetState( self, "OffRunwayWarnings", 0 )
                    end
                  else
                    Client:Message( "Attention! You are off the taxiway. Get back on the taxiway immediately!", 5, "ATC" )
                    Client:SetState( self, "IsOffRunway", true )
                    Client:SetState( self, "OffRunwayWarnings", 1 )
                  end
  
                else
                  Client:SetState( self, "IsOffRunway", false )
                  Client:SetState( self, "OffRunwayWarnings", 0 )
                end
              end
            else
              Client:SetState( self, "Speeding", false )
              Client:SetState( self, "Warnings", 0 )
              Client:SetState( self, "IsOffRunway", false )
              Client:SetState( self, "OffRunwayWarnings", 0 )
              local Taxi = Client:GetState( self, "Taxi" )
              if Taxi == true then
                Client:Message( "You have progressed to the runway ... Await take-off clearance ...", 20, "ATC" )
                Client:SetState( self, "Taxi", false )
              end
            end
          end
        end
      else
        Client:SetState( self, "Taxi", false )
      end
    end
  )

  return true
end

--- Start SCHEDULER for ATC_GROUND_UNIVERSAL object.
-- @param #ATC_GROUND_UNIVERSAL self
-- @param RepeatScanSeconds Time in second for defining occurency of alerts.
-- @return #ATC_GROUND_UNIVERSAL self
function ATC_GROUND_UNIVERSAL:Start( RepeatScanSeconds )
  RepeatScanSeconds = RepeatScanSeconds or 0.05
  self.AirbaseMonitor = SCHEDULER:New( self, self._AirbaseMonitor, { self }, 0, 2, RepeatScanSeconds )
  return self
end

---
-- @type ATC_GROUND_CAUCASUS
-- @extends #ATC_GROUND

--- # ATC\_GROUND\_CAUCASUS, extends @{#ATC_GROUND_UNIVERSAL}
-- 
-- The ATC\_GROUND\_CAUCASUS class monitors the speed of the airplanes at the airbase during taxi.
-- The pilots may not drive faster than the maximum speed for the airbase, or they will be despawned.
-- 
-- ---
--
-- ![Banner Image](..\Presentations\ATC_GROUND\Dia1.JPG)
--
-- ---
--  
-- The default maximum speed for the airbases at Caucasus is **50 km/h**. Warnings are given if this speed limit is trespassed.
-- Players will be immediately kicked when driving faster than **150 km/h** on the taxi way.
-- 
-- 
-- The pilot will receive 3 times a warning during speeding. After the 3rd warning, if the pilot is still driving
-- faster than the maximum allowed speed, the pilot will be kicked.
-- 
-- Different airbases have different maximum speeds, according safety regulations.
-- 
-- # Airbases monitored
-- 
-- The following airbases are monitored at the Caucasus region.
-- Use the @{Wrapper.Airbase#AIRBASE.Caucasus} enumeration to select the airbases to be monitored.
-- 
--   * `AIRBASE.Caucasus.Anapa_Vityazevo`
--   * `AIRBASE.Caucasus.Batumi`
--   * `AIRBASE.Caucasus.Beslan`
--   * `AIRBASE.Caucasus.Gelendzhik`
--   * `AIRBASE.Caucasus.Gudauta`
--   * `AIRBASE.Caucasus.Kobuleti`
--   * `AIRBASE.Caucasus.Krasnodar_Center`
--   * `AIRBASE.Caucasus.Krasnodar_Pashkovsky`
--   * `AIRBASE.Caucasus.Krymsk`
--   * `AIRBASE.Caucasus.Kutaisi`
--   * `AIRBASE.Caucasus.Maykop_Khanskaya`
--   * `AIRBASE.Caucasus.Mineralnye_Vody`
--   * `AIRBASE.Caucasus.Mozdok`
--   * `AIRBASE.Caucasus.Nalchik`
--   * `AIRBASE.Caucasus.Novorossiysk`
--   * `AIRBASE.Caucasus.Senaki_Kolkhi`
--   * `AIRBASE.Caucasus.Sochi_Adler`
--   * `AIRBASE.Caucasus.Soganlug`
--   * `AIRBASE.Caucasus.Sukhumi_Babushara`
--   * `AIRBASE.Caucasus.Tbilisi_Lochini`
--   * `AIRBASE.Caucasus.Vaziani`
--
-- 
-- # Installation
-- 
-- ## In Single Player Missions
-- 
-- ATC\_GROUND is fully functional in single player.
-- 
-- ## In Multi Player Missions
-- 
-- ATC\_GROUND is functional in multi player, however ...
-- 
-- Due to a bug in DCS since release 1.5, the despawning of clients are not anymore working in multi player.
-- To **work around this problem**, a much better solution has been made, using the **slot blocker** script designed
-- by Ciribob. 
-- 
-- With the help of __Ciribob__, this script has been extended to also kick client players while in flight.
-- ATC\_GROUND is communicating with this modified script to kick players!
-- 
-- Install the file **SimpleSlotBlockGameGUI.lua** on the server, following the installation instructions described by Ciribob.
-- 
-- [Simple Slot Blocker from Ciribob & FlightControl](https://github.com/ciribob/DCS-SimpleSlotBlock)
-- 
-- # Script it!
-- 
-- ## 1. ATC\_GROUND\_CAUCASUS Constructor
-- 
-- Creates a new ATC_GROUND_CAUCASUS object that will monitor pilots taxiing behaviour.
-- 
--     -- This creates a new ATC_GROUND_CAUCASUS object.
-- 
--     -- Monitor all the airbases.
--     ATC_Ground = ATC_GROUND_CAUCASUS:New()
--     
--     -- Monitor specific airbases only.
-- 
--     ATC_Ground = ATC_GROUND_CAUCASUS:New(
--       { AIRBASE.Caucasus.Gelendzhik,     
--         AIRBASE.Caucasus.Krymsk          
--       }                                  
--     )                                    
-- 
-- ## 2. Set various options
-- 
-- There are various methods that you can use to tweak the behaviour of the ATC\_GROUND classes.
-- 
-- ### 2.1 Speed limit at an airbase.
-- 
--   * @{#ATC_GROUND.SetKickSpeed}(): Set the speed limit allowed at an airbase in meters per second.
--   * @{#ATC_GROUND.SetKickSpeedKmph}(): Set the speed limit allowed at an airbase in kilometers per hour.
--   * @{#ATC_GROUND.SetKickSpeedMiph}(): Set the speed limit allowed at an airbase in miles per hour.
--   
-- ### 2.2 Prevent Takeoff at an airbase. Players will be kicked immediately.
-- 
--   * @{#ATC_GROUND.SetMaximumKickSpeed}(): Set the maximum speed allowed at an airbase in meters per second. 
--   * @{#ATC_GROUND.SetMaximumKickSpeedKmph}(): Set the maximum speed allowed at an airbase in kilometers per hour.
--   * @{#ATC_GROUND.SetMaximumKickSpeedMiph}(): Set the maximum speed allowed at an airbase in miles per hour.
--   
-- 
-- @field #ATC_GROUND_CAUCASUS
ATC_GROUND_CAUCASUS = {
  ClassName = "ATC_GROUND_CAUCASUS",
}

--- Creates a new ATC_GROUND_CAUCASUS object.
-- @param #ATC_GROUND_CAUCASUS self
-- @param AirbaseNames A list {} of airbase names (Use AIRBASE.Caucasus enumerator).
-- @return #ATC_GROUND_CAUCASUS self
function ATC_GROUND_CAUCASUS:New( AirbaseNames )

  -- Inherits from BASE
  local self = BASE:Inherit( self, ATC_GROUND_UNIVERSAL:New(AirbaseNames) )

  self:SetKickSpeedKmph( 50 )
  self:SetMaximumKickSpeedKmph( 150 )

  return self
end


--- Start SCHEDULER for ATC_GROUND_CAUCASUS object.
-- @param #ATC_GROUND_CAUCASUS self
-- @param RepeatScanSeconds Time in second for defining occurency of alerts.
-- @return nothing
function ATC_GROUND_CAUCASUS:Start( RepeatScanSeconds )
  RepeatScanSeconds = RepeatScanSeconds or 0.05
  self.AirbaseMonitor = SCHEDULER:New( self, self._AirbaseMonitor, { self }, 0, 2, RepeatScanSeconds )
end


---
-- @type ATC_GROUND_NEVADA
-- @extends #ATC_GROUND


--- # ATC\_GROUND\_NEVADA, extends @{#ATC_GROUND}
-- 
-- The ATC\_GROUND\_NEVADA class monitors the speed of the airplanes at the airbase during taxi.
-- The pilots may not drive faster than the maximum speed for the airbase, or they will be despawned.
-- 
-- ---
--
-- ![Banner Image](..\Presentations\ATC_GROUND\Dia1.JPG)
-- 
-- ---
-- 
-- The default maximum speed for the airbases at Nevada is **50 km/h**. Warnings are given if this speed limit is trespassed.
-- Players will be immediately kicked when driving faster than **150 km/h** on the taxi way.
-- 
-- The ATC\_GROUND\_NEVADA class monitors the speed of the airplanes at the airbase during taxi.
-- The pilots may not drive faster than the maximum speed for the airbase, or they will be despawned.
-- 
-- The pilot will receive 3 times a warning during speeding. After the 3rd warning, if the pilot is still driving
-- faster than the maximum allowed speed, the pilot will be kicked.
-- 
-- Different airbases have different maximum speeds, according safety regulations.
-- 
-- # Airbases monitored
-- 
-- The following airbases are monitored at the Nevada region.
-- Use the @{Wrapper.Airbase#AIRBASE.Nevada} enumeration to select the airbases to be monitored.
-- 
--    * `AIRBASE.Nevada.Beatty_Airport`
--    * `AIRBASE.Nevada.Boulder_City_Airport`
--    * `AIRBASE.Nevada.Creech_AFB`
--    * `AIRBASE.Nevada.Echo_Bay`
--    * `AIRBASE.Nevada.Groom_Lake_AFB`
--    * `AIRBASE.Nevada.Henderson_Executive_Airport`
--    * `AIRBASE.Nevada.Jean_Airport`
--    * `AIRBASE.Nevada.Laughlin_Airport`
--    * `AIRBASE.Nevada.Lincoln_County`
--    * `AIRBASE.Nevada.McCarran_International_Airport`
--    * `AIRBASE.Nevada.Mesquite`
--    * `AIRBASE.Nevada.Mina_Airport`
--    * `AIRBASE.Nevada.Nellis_AFB`
--    * `AIRBASE.Nevada.North_Las_Vegas`
--    * `AIRBASE.Nevada.Pahute_Mesa_Airstrip`
--    * `AIRBASE.Nevada.Tonopah_Airport`
--    * `AIRBASE.Nevada.Tonopah_Test_Range_Airfield`
--
-- # Installation
-- 
-- ## In Single Player Missions
-- 
-- ATC\_GROUND is fully functional in single player.
-- 
-- ## In Multi Player Missions
-- 
-- ATC\_GROUND is functional in multi player, however ...
-- 
-- Due to a bug in DCS since release 1.5, the despawning of clients are not anymore working in multi player.
-- To **work around this problem**, a much better solution has been made, using the **slot blocker** script designed
-- by Ciribob. 
-- 
-- With the help of __Ciribob__, this script has been extended to also kick client players while in flight.
-- ATC\_GROUND is communicating with this modified script to kick players!
-- 
-- Install the file **SimpleSlotBlockGameGUI.lua** on the server, following the installation instructions described by Ciribob.
-- 
-- [Simple Slot Blocker from Ciribob & FlightControl](https://github.com/ciribob/DCS-SimpleSlotBlock)
-- 
-- # Script it!
-- 
-- ## 1. ATC_GROUND_NEVADA Constructor
-- 
-- Creates a new ATC_GROUND_NEVADA object that will monitor pilots taxiing behaviour.
-- 
--     -- This creates a new ATC_GROUND_NEVADA object.
-- 
--     -- Monitor all the airbases.
--     ATC_Ground = ATC_GROUND_NEVADA:New()
-- 
--    
--     -- Monitor specific airbases.
--     ATC_Ground = ATC_GROUND_NEVADA:New(              
--       { AIRBASE.Nevada.Laughlin_Airport,                        
--         AIRBASE.Nevada.Lincoln_County,               
--         AIRBASE.Nevada.North_Las_Vegas,              
--         AIRBASE.Nevada.McCarran_International_Airport
--       }                                              
--     )                                                
-- 
-- ## 2. Set various options
-- 
-- There are various methods that you can use to tweak the behaviour of the ATC\_GROUND classes.
-- 
-- ### 2.1 Speed limit at an airbase.
-- 
--   * @{#ATC_GROUND.SetKickSpeed}(): Set the speed limit allowed at an airbase in meters per second.
--   * @{#ATC_GROUND.SetKickSpeedKmph}(): Set the speed limit allowed at an airbase in kilometers per hour.
--   * @{#ATC_GROUND.SetKickSpeedMiph}(): Set the speed limit allowed at an airbase in miles per hour.
--   
-- ### 2.2 Prevent Takeoff at an airbase. Players will be kicked immediately.
-- 
--   * @{#ATC_GROUND.SetMaximumKickSpeed}(): Set the maximum speed allowed at an airbase in meters per second. 
--   * @{#ATC_GROUND.SetMaximumKickSpeedKmph}(): Set the maximum speed allowed at an airbase in kilometers per hour.
--   * @{#ATC_GROUND.SetMaximumKickSpeedMiph}(): Set the maximum speed allowed at an airbase in miles per hour.
-- 
--   
-- @field #ATC_GROUND_NEVADA
ATC_GROUND_NEVADA = {
  ClassName = "ATC_GROUND_NEVADA",
}

--- Creates a new ATC_GROUND_NEVADA object.
-- @param #ATC_GROUND_NEVADA self
-- @param AirbaseNames A list {} of airbase names (Use AIRBASE.Nevada enumerator).
-- @return #ATC_GROUND_NEVADA self
function ATC_GROUND_NEVADA:New( AirbaseNames )

  -- Inherits from BASE
  local self = BASE:Inherit( self, ATC_GROUND_UNIVERSAL:New( AirbaseNames ) )

  self:SetKickSpeedKmph( 50 )
  self:SetMaximumKickSpeedKmph( 150 )

  return self
end

--- Start SCHEDULER for ATC_GROUND_NEVADA object.
-- @param #ATC_GROUND_NEVADA self
-- @param RepeatScanSeconds Time in second for defining occurency of alerts.
-- @return nothing
function ATC_GROUND_NEVADA:Start( RepeatScanSeconds )
  RepeatScanSeconds = RepeatScanSeconds or 0.05
  self.AirbaseMonitor = SCHEDULER:New( self, self._AirbaseMonitor, { self }, 0, 2, RepeatScanSeconds )
end

---
-- @type ATC_GROUND_NORMANDY
-- @extends #ATC_GROUND


--- # ATC\_GROUND\_NORMANDY, extends @{#ATC_GROUND}
-- 
-- The ATC\_GROUND\_NORMANDY class monitors the speed of the airplanes at the airbase during taxi.
-- The pilots may not drive faster than the maximum speed for the airbase, or they will be despawned.
-- 
-- ---
-- 
-- ![Banner Image](..\Presentations\ATC_GROUND\Dia1.JPG)
-- 
-- ---
-- 
-- The default maximum speed for the airbases at Normandy is **40 km/h**. Warnings are given if this speed limit is trespassed.
-- Players will be immediately kicked when driving faster than **100 km/h** on the taxi way.
-- 
-- The ATC\_GROUND\_NORMANDY class monitors the speed of the airplanes at the airbase during taxi.
-- The pilots may not drive faster than the maximum speed for the airbase, or they will be despawned.
-- 
-- The pilot will receive 3 times a warning during speeding. After the 3rd warning, if the pilot is still driving
-- faster than the maximum allowed speed, the pilot will be kicked.
-- 
-- Different airbases have different maximum speeds, according safety regulations.
-- 
-- # Airbases monitored
-- 
-- The following airbases are monitored at the Normandy region.
-- Use the @{Wrapper.Airbase#AIRBASE.Normandy} enumeration to select the airbases to be monitored.
-- 
--   * `AIRBASE.Normandy.Azeville`
--   * `AIRBASE.Normandy.Bazenville`
--   * `AIRBASE.Normandy.Beny_sur_Mer`
--   * `AIRBASE.Normandy.Beuzeville`
--   * `AIRBASE.Normandy.Biniville`
--   * `AIRBASE.Normandy.Brucheville`
--   * `AIRBASE.Normandy.Cardonville`
--   * `AIRBASE.Normandy.Carpiquet`
--   * `AIRBASE.Normandy.Chailey`
--   * `AIRBASE.Normandy.Chippelle`
--   * `AIRBASE.Normandy.Cretteville`
--   * `AIRBASE.Normandy.Cricqueville_en_Bessin`
--   * `AIRBASE.Normandy.Deux_Jumeaux`
--   * `AIRBASE.Normandy.Evreux`
--   * `AIRBASE.Normandy.Ford`
--   * `AIRBASE.Normandy.Funtington`
--   * `AIRBASE.Normandy.Lantheuil`
--   * `AIRBASE.Normandy.Le_Molay`
--   * `AIRBASE.Normandy.Lessay`
--   * `AIRBASE.Normandy.Lignerolles`
--   * `AIRBASE.Normandy.Longues_sur_Mer`
--   * `AIRBASE.Normandy.Maupertus`
--   * `AIRBASE.Normandy.Meautis`
--   * `AIRBASE.Normandy.Needs_Oar_Point`
--   * `AIRBASE.Normandy.Picauville`
--   * `AIRBASE.Normandy.Rucqueville`
--   * `AIRBASE.Normandy.Saint_Pierre_du_Mont`
--   * `AIRBASE.Normandy.Sainte_Croix_sur_Mer`
--   * `AIRBASE.Normandy.Sainte_Laurent_sur_Mer`
--   * `AIRBASE.Normandy.Sommervieu`
--   * `AIRBASE.Normandy.Tangmere`
--   * `AIRBASE.Normandy.Argentan`
--   * `AIRBASE.Normandy.Goulet`
--   * `AIRBASE.Normandy.Essay`
--   * `AIRBASE.Normandy.Hauterive`
--   * `AIRBASE.Normandy.Barville`
--   * `AIRBASE.Normandy.Conches`
--   * `AIRBASE.Normandy.Vrigny`
--
-- # Installation
-- 
-- ## In Single Player Missions
-- 
-- ATC\_GROUND is fully functional in single player.
-- 
-- ## In Multi Player Missions
-- 
-- ATC\_GROUND is functional in multi player, however ...
-- 
-- Due to a bug in DCS since release 1.5, the despawning of clients are not anymore working in multi player.
-- To **work around this problem**, a much better solution has been made, using the **slot blocker** script designed
-- by Ciribob. 
-- 
-- With the help of __Ciribob__, this script has been extended to also kick client players while in flight.
-- ATC\_GROUND is communicating with this modified script to kick players!
-- 
-- Install the file **SimpleSlotBlockGameGUI.lua** on the server, following the installation instructions described by Ciribob.
-- 
-- [Simple Slot Blocker from Ciribob & FlightControl](https://github.com/ciribob/DCS-SimpleSlotBlock)
-- 
-- # Script it!
-- 
-- ## 1. ATC_GROUND_NORMANDY Constructor
-- 
-- Creates a new ATC_GROUND_NORMANDY object that will monitor pilots taxiing behaviour.
-- 
--     -- This creates a new ATC_GROUND_NORMANDY object.
-- 
--     -- Monitor for these clients the airbases.
--     AirbasePoliceCaucasus = ATC_GROUND_NORMANDY:New()
--     
--     ATC_Ground = ATC_GROUND_NORMANDY:New( 
--       { AIRBASE.Normandy.Chippelle,
--         AIRBASE.Normandy.Beuzeville 
--       } 
--     )
-- 
--     
-- ## 2. Set various options
-- 
-- There are various methods that you can use to tweak the behaviour of the ATC\_GROUND classes.
-- 
-- ### 2.1 Speed limit at an airbase.
-- 
--   * @{#ATC_GROUND.SetKickSpeed}(): Set the speed limit allowed at an airbase in meters per second.
--   * @{#ATC_GROUND.SetKickSpeedKmph}(): Set the speed limit allowed at an airbase in kilometers per hour.
--   * @{#ATC_GROUND.SetKickSpeedMiph}(): Set the speed limit allowed at an airbase in miles per hour.
--   
-- ### 2.2 Prevent Takeoff at an airbase. Players will be kicked immediately.
-- 
--   * @{#ATC_GROUND.SetMaximumKickSpeed}(): Set the maximum speed allowed at an airbase in meters per second. 
--   * @{#ATC_GROUND.SetMaximumKickSpeedKmph}(): Set the maximum speed allowed at an airbase in kilometers per hour.
--   * @{#ATC_GROUND.SetMaximumKickSpeedMiph}(): Set the maximum speed allowed at an airbase in miles per hour.
--   
-- @field #ATC_GROUND_NORMANDY
ATC_GROUND_NORMANDY = {
  ClassName = "ATC_GROUND_NORMANDY", 
}


--- Creates a new ATC_GROUND_NORMANDY object.
-- @param #ATC_GROUND_NORMANDY self
-- @param AirbaseNames A list {} of airbase names (Use AIRBASE.Normandy enumerator).
-- @return #ATC_GROUND_NORMANDY self
function ATC_GROUND_NORMANDY:New( AirbaseNames )

  -- Inherits from BASE
  local self = BASE:Inherit( self, ATC_GROUND_UNIVERSAL:New( AirbaseNames ) ) -- #ATC_GROUND_NORMANDY
  
  self:SetKickSpeedKmph( 40 )
  self:SetMaximumKickSpeedKmph( 100 )
  
  return self
end

     
--- Start SCHEDULER for ATC_GROUND_NORMANDY object.
-- @param #ATC_GROUND_NORMANDY self
-- @param RepeatScanSeconds Time in second for defining occurency of alerts.
-- @return nothing
function ATC_GROUND_NORMANDY:Start( RepeatScanSeconds )
  RepeatScanSeconds = RepeatScanSeconds or 0.05
  self.AirbaseMonitor = SCHEDULER:New( self, self._AirbaseMonitor, { self }, 0, 2, RepeatScanSeconds )
end

---
-- @type ATC_GROUND_PERSIANGULF
-- @extends #ATC_GROUND


--- # ATC\_GROUND\_PERSIANGULF, extends @{#ATC_GROUND}
-- 
-- The ATC\_GROUND\_PERSIANGULF class monitors the speed of the airplanes at the airbase during taxi.
-- The pilots may not drive faster than the maximum speed for the airbase, or they will be despawned.
-- 
-- ---
-- 
-- ![Banner Image](..\Presentations\ATC_GROUND\Dia1.JPG)
-- 
-- ---
-- 
-- The default maximum speed for the airbases at Persian Gulf is **50 km/h**. Warnings are given if this speed limit is trespassed.
-- Players will be immediately kicked when driving faster than **150 km/h** on the taxi way.
-- 
-- The ATC\_GROUND\_PERSIANGULF class monitors the speed of the airplanes at the airbase during taxi.
-- The pilots may not drive faster than the maximum speed for the airbase, or they will be despawned.
-- 
-- The pilot will receive 3 times a warning during speeding. After the 3rd warning, if the pilot is still driving
-- faster than the maximum allowed speed, the pilot will be kicked.
-- 
-- Different airbases have different maximum speeds, according safety regulations.
-- 
-- # Airbases monitored
-- 
-- The following airbases are monitored at the PersianGulf region.
-- Use the @{Wrapper.Airbase#AIRBASE.PersianGulf} enumeration to select the airbases to be monitored.
-- 
--   * `AIRBASE.PersianGulf.Abu_Musa_Island_Airport`
--   * `AIRBASE.PersianGulf.Al_Dhafra_AB`
--   * `AIRBASE.PersianGulf.Al_Maktoum_Intl`
--   * `AIRBASE.PersianGulf.Al_Minhad_AB`
--   * `AIRBASE.PersianGulf.Bandar_Abbas_Intl`
--   * `AIRBASE.PersianGulf.Bandar_Lengeh`
--   * `AIRBASE.PersianGulf.Dubai_Intl`
--   * `AIRBASE.PersianGulf.Fujairah_Intl`
--   * `AIRBASE.PersianGulf.Havadarya`
--   * `AIRBASE.PersianGulf.Kerman_Airport`
--   * `AIRBASE.PersianGulf.Khasab`
--   * `AIRBASE.PersianGulf.Lar_Airbase`
--   * `AIRBASE.PersianGulf.Qeshm_Island`
--   * `AIRBASE.PersianGulf.Sharjah_Intl`
--   * `AIRBASE.PersianGulf.Shiraz_International_Airport`
--   * `AIRBASE.PersianGulf.Sir_Abu_Nuayr`
--   * `AIRBASE.PersianGulf.Sirri_Island`
--   * `AIRBASE.PersianGulf.Tunb_Island_AFB`
--   * `AIRBASE.PersianGulf.Tunb_Kochak`
--   * `AIRBASE.PersianGulf.Sas_Al_Nakheel_Airport`
--   * `AIRBASE.PersianGulf.Bandar_e_Jask_airfield`
--   * `AIRBASE.PersianGulf.Abu_Dhabi_International_Airport`
--   * `AIRBASE.PersianGulf.Al_Bateen_Airport`
--   * `AIRBASE.PersianGulf.Kish_International_Airport`
--   * `AIRBASE.PersianGulf.Al_Ain_International_Airport`
--   * `AIRBASE.PersianGulf.Lavan_Island_Airport`
--   * `AIRBASE.PersianGulf.Jiroft_Airport`
--
-- # Installation
-- 
-- ## In Single Player Missions
-- 
-- ATC\_GROUND is fully functional in single player.
-- 
-- ## In Multi Player Missions
-- 
-- ATC\_GROUND is functional in multi player, however ...
-- 
-- Due to a bug in DCS since release 1.5, the despawning of clients are not anymore working in multi player.
-- To **work around this problem**, a much better solution has been made, using the **slot blocker** script designed
-- by Ciribob. 
-- 
-- With the help of __Ciribob__, this script has been extended to also kick client players while in flight.
-- ATC\_GROUND is communicating with this modified script to kick players!
-- 
-- Install the file **SimpleSlotBlockGameGUI.lua** on the server, following the installation instructions described by Ciribob.
-- 
-- [Simple Slot Blocker from Ciribob & FlightControl](https://github.com/ciribob/DCS-SimpleSlotBlock)
-- 
-- # Script it!
-- 
-- ## 1. ATC_GROUND_PERSIANGULF Constructor
-- 
-- Creates a new ATC_GROUND_PERSIANGULF object that will monitor pilots taxiing behaviour.
-- 
--     -- This creates a new ATC_GROUND_PERSIANGULF object.
-- 
--     -- Monitor for these clients the airbases.
--     AirbasePoliceCaucasus = ATC_GROUND_PERSIANGULF:New()
--     
--     ATC_Ground = ATC_GROUND_PERSIANGULF:New( 
--       { AIRBASE.PersianGulf.Kerman_Airport,
--         AIRBASE.PersianGulf.Al_Minhad_AB 
--       } 
--     )
-- 
--     
-- ## 2. Set various options
-- 
-- There are various methods that you can use to tweak the behaviour of the ATC\_GROUND classes.
-- 
-- ### 2.1 Speed limit at an airbase.
-- 
--   * @{#ATC_GROUND.SetKickSpeed}(): Set the speed limit allowed at an airbase in meters per second.
--   * @{#ATC_GROUND.SetKickSpeedKmph}(): Set the speed limit allowed at an airbase in kilometers per hour.
--   * @{#ATC_GROUND.SetKickSpeedMiph}(): Set the speed limit allowed at an airbase in miles per hour.
--   
-- ### 2.2 Prevent Takeoff at an airbase. Players will be kicked immediately.
-- 
--   * @{#ATC_GROUND.SetMaximumKickSpeed}(): Set the maximum speed allowed at an airbase in meters per second. 
--   * @{#ATC_GROUND.SetMaximumKickSpeedKmph}(): Set the maximum speed allowed at an airbase in kilometers per hour.
--   * @{#ATC_GROUND.SetMaximumKickSpeedMiph}(): Set the maximum speed allowed at an airbase in miles per hour.
--   
-- @field #ATC_GROUND_PERSIANGULF
ATC_GROUND_PERSIANGULF = {
  ClassName = "ATC_GROUND_PERSIANGULF",
}

--- Creates a new ATC_GROUND_PERSIANGULF object.
-- @param #ATC_GROUND_PERSIANGULF self
-- @param AirbaseNames A list {} of airbase names (Use AIRBASE.PersianGulf enumerator).
-- @return #ATC_GROUND_PERSIANGULF self
function ATC_GROUND_PERSIANGULF:New( AirbaseNames )

  -- Inherits from BASE
  local self = BASE:Inherit( self, ATC_GROUND_UNIVERSAL:New( AirbaseNames ) ) -- #ATC_GROUND_PERSIANGULF
  
  self:SetKickSpeedKmph( 50 )
  self:SetMaximumKickSpeedKmph( 150 )
  
end

--- Start SCHEDULER for ATC_GROUND_PERSIANGULF object.
-- @param #ATC_GROUND_PERSIANGULF self
-- @param RepeatScanSeconds Time in second for defining occurency of alerts.
-- @return nothing
function ATC_GROUND_PERSIANGULF:Start( RepeatScanSeconds )
  RepeatScanSeconds = RepeatScanSeconds or 0.05
  self.AirbaseMonitor = SCHEDULER:New( self, self._AirbaseMonitor, { self }, 0, 2, RepeatScanSeconds )
end
          

 -- @type ATC_GROUND_MARIANAISLANDS
-- @extends #ATC_GROUND

     

--- # ATC\_GROUND\_MARIANA, extends @{#ATC_GROUND}
-- 
-- The ATC\_GROUND\_MARIANA class monitors the speed of the airplanes at the airbase during taxi.
-- The pilots may not drive faster than the maximum speed for the airbase, or they will be despawned.
-- 
-- ---
-- 
-- ![Banner Image](..\Presentations\ATC_GROUND\Dia1.JPG)
-- 
-- ---
-- 
-- The default maximum speed for the airbases at Persian Gulf is **50 km/h**. Warnings are given if this speed limit is trespassed.
-- Players will be immediately kicked when driving faster than **150 km/h** on the taxi way.
-- 
-- The ATC\_GROUND\_MARIANA class monitors the speed of the airplanes at the airbase during taxi.
-- The pilots may not drive faster than the maximum speed for the airbase, or they will be despawned.
-- 
-- The pilot will receive 3 times a warning during speeding. After the 3rd warning, if the pilot is still driving
-- faster than the maximum allowed speed, the pilot will be kicked.
-- 
-- Different airbases have different maximum speeds, according safety regulations.
-- 
-- # Airbases monitored
-- 
-- The following airbases are monitored at the Mariana Island region.
-- Use the @{Wrapper.Airbase#AIRBASE.MarianaIslands} enumeration to select the airbases to be monitored.
-- 
-- * AIRBASE.MarianaIslands.Rota_Intl
-- * AIRBASE.MarianaIslands.Andersen_AFB
-- * AIRBASE.MarianaIslands.Antonio_B_Won_Pat_Intl
-- * AIRBASE.MarianaIslands.Saipan_Intl
-- * AIRBASE.MarianaIslands.Tinian_Intl
-- * AIRBASE.MarianaIslands.Olf_Orote
--
-- # Installation
-- 
-- ## In Single Player Missions
-- 
-- ATC\_GROUND is fully functional in single player.
-- 
-- ## In Multi Player Missions
-- 
-- ATC\_GROUND is functional in multi player, however ...
-- 
-- Due to a bug in DCS since release 1.5, the despawning of clients are not anymore working in multi player.
-- To **work around this problem**, a much better solution has been made, using the **slot blocker** script designed
-- by Ciribob. 
-- 
-- With the help of __Ciribob__, this script has been extended to also kick client players while in flight.
-- ATC\_GROUND is communicating with this modified script to kick players!
-- 
-- Install the file **SimpleSlotBlockGameGUI.lua** on the server, following the installation instructions described by Ciribob.
-- 
-- [Simple Slot Blocker from Ciribob & FlightControl](https://github.com/ciribob/DCS-SimpleSlotBlock)
-- 
-- # Script it!
-- 
-- ## 1. ATC_GROUND_MARIANAISLANDS Constructor
-- 
-- Creates a new ATC_GROUND_MARIANAISLANDS object that will monitor pilots taxiing behaviour.
-- 
--     -- This creates a new ATC_GROUND_MARIANAISLANDS object.
-- 
--     -- Monitor for these clients the airbases.
--     AirbasePoliceCaucasus = ATC_GROUND_MARIANAISLANDS:New()
--     
--     ATC_Ground = ATC_GROUND_MARIANAISLANDS:New( 
--       { AIRBASE.MarianaIslands.Andersen_AFB,
--         AIRBASE.MarianaIslands.Saipan_Intl 
--       } 
--     )
-- 
--     
-- ## 2. Set various options
-- 
-- There are various methods that you can use to tweak the behaviour of the ATC\_GROUND classes.
-- 
-- ### 2.1 Speed limit at an airbase.
-- 
--   * @{#ATC_GROUND.SetKickSpeed}(): Set the speed limit allowed at an airbase in meters per second.
--   * @{#ATC_GROUND.SetKickSpeedKmph}(): Set the speed limit allowed at an airbase in kilometers per hour.
--   * @{#ATC_GROUND.SetKickSpeedMiph}(): Set the speed limit allowed at an airbase in miles per hour.
--   
-- ### 2.2 Prevent Takeoff at an airbase. Players will be kicked immediately.
-- 
--   * @{#ATC_GROUND.SetMaximumKickSpeed}(): Set the maximum speed allowed at an airbase in meters per second. 
--   * @{#ATC_GROUND.SetMaximumKickSpeedKmph}(): Set the maximum speed allowed at an airbase in kilometers per hour.
--   * @{#ATC_GROUND.SetMaximumKickSpeedMiph}(): Set the maximum speed allowed at an airbase in miles per hour.
--     
-- @field #ATC_GROUND_MARIANAISLANDS
ATC_GROUND_MARIANAISLANDS = {
  ClassName = "ATC_GROUND_MARIANAISLANDS",
}

--- Creates a new ATC_GROUND_MARIANAISLANDS object.
-- @param #ATC_GROUND_MARIANAISLANDS self
-- @param AirbaseNames A list {} of airbase names (Use AIRBASE.MarianaIslands enumerator).
-- @return #ATC_GROUND_MARIANAISLANDS self
function ATC_GROUND_MARIANAISLANDS:New( AirbaseNames )

  -- Inherits from BASE
  local self = BASE:Inherit( self, ATC_GROUND_UNIVERSAL:New( AirbaseNames ) )

  self:SetKickSpeedKmph( 50 )
  self:SetMaximumKickSpeedKmph( 150 )

  return self
end

--- Start SCHEDULER for ATC_GROUND_MARIANAISLANDS object.
-- @param #ATC_GROUND_MARIANAISLANDS self
-- @param RepeatScanSeconds Time in second for defining occurency of alerts.
-- @return nothing
function ATC_GROUND_MARIANAISLANDS:Start( RepeatScanSeconds )
  RepeatScanSeconds = RepeatScanSeconds or 0.05
  self.AirbaseMonitor = SCHEDULER:New( self, self._AirbaseMonitor, { self }, 0, 2, RepeatScanSeconds )
end
