--- **Functional** - Short Range Air Defense System.
-- 
-- ===
--
-- ## Features: 
-- 
--   * Short Range Air Defense System
--   * Controls a network of short range air/missile defense groups.
-- 
-- ===
-- 
-- ## Missions:
--
-- ### [SHORAD - Short Range Air Defense](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SRD%20-%20SHORAD%20Defense)
-- 
-- ===
-- 
-- ### Author : **applevangelist **
-- 
-- @module Functional.Shorad
-- @image Functional.Shorad.jpg
--
-- Date: Nov 2021

-------------------------------------------------------------------------
--- **SHORAD** class, extends Core.Base#BASE
-- @type SHORAD
-- @field #string ClassName
-- @field #string name Name of this Shorad
-- @field #boolean debug Set the debug state
-- @field #string Prefixes String to be used to build the @{#Core.Set#SET_GROUP} 
-- @field #number Radius Shorad defense radius in meters
-- @field Core.Set#SET_GROUP Groupset The set of Shorad groups
-- @field Core.Set#SET_GROUP Samset The set of SAM groups to defend
-- @field #string Coalition The coalition of this Shorad
-- @field #number ActiveTimer How long a Shorad stays active after wake-up in seconds
-- @field #table ActiveGroups Table for the timer function
-- @field #string lid The log ID for the dcs.log
-- @field #boolean DefendHarms Default true, intercept incoming HARMS
-- @field #boolean DefendMavs Default true, intercept incoming AG-Missiles
-- @field #number DefenseLowProb Default 70, minimum detection limit
-- @field #number DefenseHighProb Default 90, maximim detection limit
-- @field #boolean UseEmOnOff Decide if we are using Emission on/off (default) or AlarmState red/green.
-- @extends Core.Base#BASE


--- *Good friends are worth defending.* Mr Tushman, Wonder (the Movie) 
-- 
-- Simple Class for a more intelligent Short Range Air Defense System
-- 
-- #SHORAD
-- Moose derived missile intercepting short range defense system.
-- Protects a network of SAM sites. Uses events to switch on the defense groups closest to the enemy.
-- Easily integrated with @{Functional.Mantis#MANTIS} to complete the defensive system setup.
--
-- ## Usage
--
-- Set up a #SET_GROUP for the SAM sites to be protected:  
-- 
--        `local SamSet = SET_GROUP:New():FilterPrefixes("Red SAM"):FilterCoalitions("red"):FilterStart()`   
--    
-- By default, SHORAD will defense against both HARMs and AG-Missiles with short to medium range. The default defense probability is 70-90%.
-- When a missile is detected, SHORAD will activate defense groups in the given radius around the target for 10 minutes. It will *not* react to friendly fire.    
--        
-- ### Start a new SHORAD system, parameters are:
--   
--  * Name: Name of this SHORAD.  
--  * ShoradPrefix: Filter for the Shorad #SET_GROUP.  
--  * Samset: The #SET_GROUP of SAM sites to defend.  
--  * Radius: Defense radius in meters. 
--  * ActiveTimer: Determines how many seconds the systems stay on red alert after wake-up call.  
--  * Coalition: Coalition, i.e. "blue", "red", or "neutral".* 
--    
--        `myshorad = SHORAD:New("RedShorad", "Red SHORAD", SamSet, 25000, 600, "red")`       
--
-- ## Customize options   
--  
--  * SHORAD:SwitchDebug(debug)
--  * SHORAD:SwitchHARMDefense(onoff)
--  * SHORAD:SwitchAGMDefense(onoff)
--  * SHORAD:SetDefenseLimits(low,high)
--  * SHORAD:SetActiveTimer(seconds)
--  * SHORAD:SetDefenseRadius(meters)
--
-- @field #SHORAD
SHORAD = {
  ClassName = "SHORAD",
  name = "MyShorad",
  debug = false,
  Prefixes = "",
  Radius = 20000,
  Groupset = nil,
  Samset = nil,
  Coalition = nil,
  ActiveTimer = 600, --stay on 10 mins
  ActiveGroups = {},
  lid = "",
  DefendHarms = true,
  DefendMavs = true,
  DefenseLowProb = 70,
  DefenseHighProb = 90,
  UseEmOnOff = false,  
}

-----------------------------------------------------------------------
-- SHORAD System
-----------------------------------------------------------------------

do
  -- TODO Complete list?
  --- Missile enumerators
  -- @field Harms
  SHORAD.Harms = {
  ["AGM_88"] = "AGM_88",
  --["AGM_45"] = "AGM_45",
  ["AGM_122"] = "AGM_122",
  ["AGM_84"] = "AGM_84",
  ["AGM_45"] = "AGM_45",
  ["ALARM"] = "ALARM",
  ["LD-10"] = "LD-10",
  ["X_58"] = "X_58",
  ["X_28"] = "X_28",
  ["X_25"] = "X_25",
  ["X_31"] = "X_31",
  ["Kh25"] = "Kh25",
  }
  
  --- TODO complete list?
  -- @field Mavs
  SHORAD.Mavs = {
  ["AGM"] = "AGM",
  ["C-701"] = "C-701",
  ["Kh25"] = "Kh25",
  ["Kh29"] = "Kh29",
  ["Kh31"] = "Kh31",
  ["Kh66"] = "Kh66",
  --["BGM_109"] = "BGM_109",
  }
  
  --- Instantiates a new SHORAD object
  -- @param #SHORAD self
  -- @param #string Name Name of this SHORAD
  -- @param #string ShoradPrefix Filter for the Shorad #SET_GROUP
  -- @param Core.Set#SET_GROUP Samset The #SET_GROUP of SAM sites to defend
  -- @param #number Radius Defense radius in meters, used to switch on SHORAD groups **within** this radius
  -- @param #number ActiveTimer Determines how many seconds the systems stay on red alert after wake-up call
  -- @param #string Coalition Coalition, i.e. "blue", "red", or "neutral"
  -- @param #boolean UseEmOnOff Use Emissions On/Off rather than Alarm State Red/Green (default: use Emissions switch)
  -- @retunr #SHORAD self
  function SHORAD:New(Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition, UseEmOnOff) 
    local self = BASE:Inherit( self, FSM:New() )
    self:T({Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition})
    
    local GroupSet = SET_GROUP:New():FilterPrefixes(ShoradPrefix):FilterCoalitions(Coalition):FilterCategoryGround():FilterStart()

    self.name = Name or "MyShorad"
    self.Prefixes = ShoradPrefix or "SAM SHORAD"
    self.Radius = Radius or 20000
    self.Coalition = Coalition or "blue"
    self.Samset = Samset or GroupSet
    self.ActiveTimer = ActiveTimer or 600
    self.ActiveGroups = {}
    self.Groupset = GroupSet
    self.DefendHarms = true
    self.DefendMavs = true
    self.DefenseLowProb = 70 -- probability to detect a missile shot, low margin
    self.DefenseHighProb = 90  -- probability to detect a missile shot, high margin
    self.UseEmOnOff = UseEmOnOff or false -- Decide if we are using Emission on/off (default) or AlarmState red/green
    self:I("*** SHORAD - Started Version 0.3.1")
    -- Set the string id for output to DCS.log file.
    self.lid=string.format("SHORAD %s | ", self.name)
    self:_InitState()
    self:HandleEvent(EVENTS.Shot, self.HandleEventShot)
    
    -- Start State.
    self:SetStartState("Running")
    self:AddTransition("*",             "WakeUpShorad",                 "*")
    self:AddTransition("*",             "CalculateHitZone",             "*")
    
    return self
  end
  
  --- Initially set all groups to alarm state GREEN
  -- @param #SHORAD self
  function SHORAD:_InitState()
    self:T(self.lid .. " _InitState")
    local table = {}
    local set = self.Groupset
    self:T({set = set})
    local aliveset = set:GetAliveSet() --#table
    for _,_group in pairs (aliveset) do
     if self.UseEmOnOff then
      --_group:SetAIOff()
      _group:EnableEmission(false)
      _group:OptionAlarmStateRed() --Wrapper.Group#GROUP
     else
      _group:OptionAlarmStateGreen() --Wrapper.Group#GROUP
     end
     _group:OptionDisperseOnAttack(30)
    end
    -- gather entropy
    for i=1,100 do
      math.random()
    end
    return self
  end
  
  --- Switch debug state on
  -- @param #SHORAD self
  -- @param #boolean debug Switch debug on (true) or off (false)
  function SHORAD:SwitchDebug(onoff)
    self:T( { onoff } )
    if onoff then
      self:SwitchDebugOn()
    else
      self:SwitchDebugOff()
    end
    return self
  end
  
  --- Switch debug state on
  -- @param #SHORAD self
  function SHORAD:SwitchDebugOn()
     self.debug = true
     --tracing
     BASE:TraceOn()
     BASE:TraceClass("SHORAD")
     return self
  end
  
  --- Switch debug state off
  -- @param #SHORAD self
  function SHORAD:SwitchDebugOff()
    self.debug = false
    BASE:TraceOff()
    return self
  end
  
  --- Switch defense for HARMs
  -- @param #SHORAD self
  -- @param #boolean onoff
  function SHORAD:SwitchHARMDefense(onoff)
    self:T( { onoff } )
    local onoff = onoff or true
    self.DefendHarms = onoff
    return self
  end
  
  --- Switch defense for AGMs
  -- @param #SHORAD self
  -- @param #boolean onoff
  function SHORAD:SwitchAGMDefense(onoff)
    self:T( { onoff } )
    local onoff = onoff or true
    self.DefendMavs = onoff
    return self
  end
  
  --- Set defense probability limits
  -- @param #SHORAD self
  -- @param #number low Minimum detection limit, integer 1-100
  -- @param #number high Maximum detection limit integer 1-100
  function SHORAD:SetDefenseLimits(low,high)
    self:T( { low, high } )
    local low = low or 70
    local high = high or 90
    if (low < 0) or (low > 100) or (low > high) then
      low = 70
    end
    if (high < 0) or (high > 100) or (high < low ) then
      high = 90
    end
    self.DefenseLowProb = low
    self.DefenseHighProb = high
    return self
  end
  
  --- Set the number of seconds a SHORAD site will stay active
  -- @param #SHORAD self
  -- @param #number seconds Number of seconds systems stay active
  function SHORAD:SetActiveTimer(seconds)
    self:T(self.lid .. " SetActiveTimer")
    local timer = seconds or 600
    if timer < 0 then
      timer = 600
    end
    self.ActiveTimer = timer
    return self
  end

  --- Set the number of meters for the SHORAD defense zone
  -- @param #SHORAD self
  -- @param #number meters Radius of the defense search zone in meters. #SHORADs in this range around a targeted group will go active 
  function SHORAD:SetDefenseRadius(meters)
  self:T(self.lid .. " SetDefenseRadius")
    local radius = meters or 20000
    if radius < 0 then
      radius = 20000
    end
    self.Radius = radius
    return self
  end
  
  --- Set using Emission on/off instead of changing alarm state
  -- @param #SHORAD self
  -- @param #boolean switch Decide if we are changing alarm state or AI state
  function SHORAD:SetUsingEmOnOff(switch)
  self:T(self.lid .. " SetUsingEmOnOff")
    self.UseEmOnOff = switch or false
    return self
  end
  
  --- Check if a HARM was fired
  -- @param #SHORAD self
  -- @param #string WeaponName
  -- @return #boolean Returns true for a match
  function SHORAD:_CheckHarms(WeaponName)
    self:T(self.lid .. " _CheckHarms")
    self:T( { WeaponName } )
    local hit = false
    if self.DefendHarms then
      for _,_name in pairs (SHORAD.Harms) do
        if string.find(WeaponName,_name,1,true) then hit = true end
      end
    end
    return hit
  end
  
  --- Check if an AGM was fired
  -- @param #SHORAD self
  -- @param #string WeaponName
  -- @return #boolean Returns true for a match
  function SHORAD:_CheckMavs(WeaponName)
    self:T(self.lid .. " _CheckMavs")
    self:T( { WeaponName } )
    local hit = false
    if self.DefendMavs then
      for _,_name in pairs (SHORAD.Mavs) do
        if string.find(WeaponName,_name,1,true) then hit = true end
      end
    end
    return hit
  end
  
  --- Check the coalition of the attacker
  -- @param #SHORAD self
  -- @param #string Coalition name
  -- @return #boolean Returns false for a match
  function SHORAD:_CheckCoalition(Coalition)
    self:T(self.lid .. " _CheckCoalition")
    local owncoalition = self.Coalition
    local othercoalition = ""
    if Coalition == 0 then 
      othercoalition = "neutral"
    elseif Coalition == 1 then
      othercoalition = "red"
    else
      othercoalition = "blue"
    end
    self:T({owncoalition = owncoalition, othercoalition = othercoalition})
    if owncoalition ~= othercoalition then
      return true
    else
      return false
    end
  end
  
  --- Check if the missile is aimed at a SHORAD
  -- @param #SHORAD self
  -- @param #string TargetGroupName Name of the target group
  -- @return #boolean Returns true for a match, else false
  function SHORAD:_CheckShotAtShorad(TargetGroupName)
    self:T(self.lid .. " _CheckShotAtShorad")
    local tgtgrp = TargetGroupName
    local shorad = self.Groupset
    local shoradset = shorad:GetAliveSet() --#table
    local returnname = false
    for _,_groups in pairs (shoradset) do
      local groupname = _groups:GetName()
      if string.find(groupname, tgtgrp, 1, true) then
        returnname = true
        --_groups:RelocateGroundRandomInRadius(7,100,false,false) -- be a bit evasive
      end
    end
    return returnname  
  end
  
  --- Check if the missile is aimed at a SAM site
  -- @param #SHORAD self
  -- @param #string TargetGroupName Name of the target group
  -- @return #boolean Returns true for a match, else false
  function SHORAD:_CheckShotAtSams(TargetGroupName)
    self:T(self.lid .. " _CheckShotAtSams")
    local tgtgrp = TargetGroupName
    local shorad = self.Samset
    --local shoradset = shorad:GetAliveSet() --#table
    local shoradset = shorad:GetSet() --#table
    local returnname = false
    for _,_groups in pairs (shoradset) do
      local groupname = _groups:GetName()
      if string.find(groupname, tgtgrp, 1, true) then
        returnname = true
      end
    end
    return returnname
  end
  
  --- Calculate if the missile shot is detected
  -- @param #SHORAD self
  -- @return #boolean Returns true for a detection, else false
  function SHORAD:_ShotIsDetected()
    self:T(self.lid .. " _ShotIsDetected")
    if self.debug then return true end
    local IsDetected = false
    local DetectionProb = math.random(self.DefenseLowProb, self.DefenseHighProb)  -- reference value
    local ActualDetection = math.random(1,100) -- value for this shot
    if ActualDetection <= DetectionProb then
      IsDetected = true
    end
    return IsDetected
  end
  
  --- Wake up #SHORADs in a zone with diameter Radius for ActiveTimer seconds
  -- @param #SHORAD self
  -- @param #string TargetGroup Name of the target group used to build the #ZONE
  -- @param #number Radius Radius of the #ZONE
  -- @param #number ActiveTimer Number of seconds to stay active
  -- @param #number TargetCat (optional) Category, i.e. Object.Category.UNIT or Object.Category.STATIC
  -- @usage Use this function to integrate with other systems, example   
  -- 
  -- local SamSet = SET_GROUP:New():FilterPrefixes("Blue SAM"):FilterCoalitions("blue"):FilterStart()
  -- myshorad = SHORAD:New("BlueShorad", "Blue SHORAD", SamSet, 22000, 600, "blue")
  -- myshorad:SwitchDebug(true)
  -- mymantis = MANTIS:New("BlueMantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")
  -- mymantis:AddShorad(myshorad,720)
  -- mymantis:Start()
  function SHORAD:onafterWakeUpShorad(From, Event, To, TargetGroup, Radius, ActiveTimer, TargetCat)
    self:T(self.lid .. " WakeUpShorad")
    self:T({TargetGroup, Radius, ActiveTimer, TargetCat})
    local targetcat = TargetCat or Object.Category.UNIT
    local targetgroup = TargetGroup
    local targetvec2 = nil
    if targetcat == Object.Category.UNIT then
      targetvec2 = GROUP:FindByName(targetgroup):GetVec2()
    elseif targetcat == Object.Category.STATIC then
      targetvec2 = STATIC:FindByName(targetgroup,false):GetVec2()
    else
      local samset = self.Samset
      local sam = samset:GetRandom()
      targetvec2 = sam:GetVec2()
    end
    local targetzone = ZONE_RADIUS:New("Shorad",targetvec2,Radius)  -- create a defense zone to check
    local groupset = self.Groupset --Core.Set#SET_GROUP
    local shoradset = groupset:GetAliveSet() --#table
    -- local function to switch off shorad again
    local function SleepShorad(group)
      local groupname = group:GetName()
      self.ActiveGroups[groupname] = nil
      if self.UseEmOnOff then
        group:EnableEmission(false)
        --group:SetAIOff()
      else
        group:OptionAlarmStateGreen()
      end
      local text = string.format("Sleeping SHORAD %s", group:GetName())
      self:T(text)
      local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug)
    end
    -- go through set and find the one(s) to activate
    for _,_group in pairs (shoradset) do
      if _group:IsAnyInZone(targetzone) then
        local text = string.format("Waking up SHORAD %s", _group:GetName())
        self:T(text)
        local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug)
        if self.UseEmOnOff then
          --_group:SetAIOn()
          _group:EnableEmission(true)
        end
        _group:OptionAlarmStateRed()
        local groupname = _group:GetName()
        if self.ActiveGroups[groupname] == nil then -- no timer yet for this group
          self.ActiveGroups[groupname] = { Timing = ActiveTimer }
          local endtime = timer.getTime() + (ActiveTimer * math.random(75,100) / 100 ) -- randomize wakeup a bit
          timer.scheduleFunction(SleepShorad, _group, endtime)
        end
      end
    end
    return self
  end
  
--- (Internal) Calculate hit zone of an AGM-88
-- @param #SHORAD self
-- @param #table SEADWeapon DCS.Weapon object
-- @param Core.Point#COORDINATE pos0 Position of the plane when it fired
-- @param #number height Height when the missile was fired
-- @param Wrapper.Group#GROUP SEADGroup Attacker group
-- @return #SHORAD self 
function SHORAD:onafterCalculateHitZone(From,Event,To,SEADWeapon,pos0,height,SEADGroup)
  self:T("**** Calculating hit zone")
  if SEADWeapon and SEADWeapon:isExist() then
    --local pos = SEADWeapon:getPoint()
    
    -- postion and height
    local position = SEADWeapon:getPosition()
    local mheight = height
    -- heading
    local wph = math.atan2(position.x.z, position.x.x)      
    if wph < 0 then
      wph=wph+2*math.pi
    end   
    wph=math.deg(wph)
    
    -- velocity
    local wpndata = SEAD.HarmData["AGM_88"]
    local mveloc = math.floor(wpndata[2] * 340.29)
    local c1 = (2*mheight*9.81)/(mveloc^2)
    local c2 = (mveloc^2) / 9.81
    local Ropt = c2 * math.sqrt(c1+1) 
    if height <= 5000 then
      Ropt = Ropt * 0.72
    elseif height <= 7500 then
      Ropt = Ropt * 0.82  
    elseif height <= 10000 then
      Ropt = Ropt * 0.87
    elseif height <= 12500 then
      Ropt = Ropt * 0.98
    end
    
    -- look at a couple of zones across the trajectory
    for n=1,3 do
      local dist = Ropt - ((n-1)*20000)
      local predpos= pos0:Translate(dist,wph)
      if predpos then
  
        local targetzone = ZONE_RADIUS:New("Target Zone",predpos:GetVec2(),20000)
        
        if self.debug then
          predpos:MarkToAll(string.format("height=%dm | heading=%d | velocity=%ddeg | Ropt=%dm",mheight,wph,mveloc,Ropt),false)
          targetzone:DrawZone(coalition.side.BLUE,{0,0,1},0.2,nil,nil,3,true)
        end  
        
        local seadset = self.Groupset
        local tgtcoord = targetzone:GetRandomPointVec2()
        local tgtgrp = seadset:FindNearestGroupFromPointVec2(tgtcoord)
        local _targetgroup = nil
        local _targetgroupname = "none"
        local _targetskill = "Random"
        if tgtgrp and tgtgrp:IsAlive() then
          _targetgroup = tgtgrp
          _targetgroupname = tgtgrp:GetName() -- group name
          _targetskill = tgtgrp:GetUnit(1):GetSkill()
          self:T("*** Found Target = ".. _targetgroupname)
          self:WakeUpShorad(_targetgroupname, self.Radius, self.ActiveTimer, Object.Category.UNIT)
        end
      end
    end     
  end
  return self
end
  
  --- Main function - work on the EventData
  -- @param #SHORAD self
  -- @param Core.Event#EVENTDATA EventData The event details table data set
  function SHORAD:HandleEventShot( EventData )
    self:T( { EventData } )
    self:T(self.lid .. " HandleEventShot")
    --local ShootingUnit = EventData.IniDCSUnit
    --local ShootingUnitName = EventData.IniDCSUnitName
    local ShootingWeapon = EventData.Weapon -- Identify the weapon fired
    local ShootingWeaponName = EventData.WeaponName -- return weapon type
    -- get firing coalition
    local weaponcoalition = EventData.IniGroup:GetCoalition()
    -- get detection probability
    if self:_CheckCoalition(weaponcoalition) then --avoid overhead on friendly fire
      local IsDetected = self:_ShotIsDetected()
      -- convert to text
      local DetectedText = "false"
      if IsDetected then 
        DetectedText = "true"
      end
      local text = string.format("%s Missile Launched = %s | Detected probability state is %s", self.lid, ShootingWeaponName, DetectedText)
      self:T( text )
      local m = MESSAGE:New(text,10,"Info"):ToAllIf(self.debug)
      --
      if (self:_CheckHarms(ShootingWeaponName) or self:_CheckMavs(ShootingWeaponName)) and IsDetected then
        -- get target data
        local targetdata = EventData.Weapon:getTarget() -- Identify target
        -- Is there target data?
        if not targetdata or self.debug then 
          if string.find(ShootingWeaponName,"AGM_88",1,true) then
            self:I("**** Tracking AGM-88 with no target data.")
            local pos0 = EventData.IniUnit:GetCoordinate()
            local fheight = EventData.IniUnit:GetHeight()
            self:__CalculateHitZone(20,ShootingWeapon,pos0,fheight,EventData.IniGroup)
          end  
          return self
        end
        
        local targetcat = targetdata:getCategory() -- Identify category
        self:T(string.format("Target Category (3=STATIC, 1=UNIT)= %s",tostring(targetcat)))
        self:T({targetdata})
        local targetunit = nil
        if targetcat == Object.Category.UNIT then -- UNIT
          targetunit = UNIT:Find(targetdata)
        elseif targetcat == Object.Category.STATIC then  -- STATIC
          --self:T("Static Target Data")
          --self:T({targetdata:isExist()})
          --self:T({targetdata:getPoint()})
          local tgtcoord = COORDINATE:NewFromVec3(targetdata:getPoint())
          --tgtcoord:MarkToAll("Missile Target",true)
          
          local tgtgrp1 = self.Samset:FindNearestGroupFromPointVec2(tgtcoord)
          local tgtcoord1 = tgtgrp1:GetCoordinate()
          --tgtcoord1:MarkToAll("Close target SAM",true)
          
          local tgtgrp2 = self.Groupset:FindNearestGroupFromPointVec2(tgtcoord)
          local tgtcoord2 = tgtgrp2:GetCoordinate()
          --tgtcoord2:MarkToAll("Close target SHORAD",true)
          
          local dist1 = tgtcoord:Get2DDistance(tgtcoord1)
          local dist2 = tgtcoord:Get2DDistance(tgtcoord2)
          
          if dist1 < dist2 then
            targetunit = tgtgrp1
            targetcat = Object.Category.UNIT
          else
            targetunit = tgtgrp2
            targetcat = Object.Category.UNIT
          end
        end   
        --local targetunitname = Unit.getName(targetdata) -- Unit name
        if targetunit and targetunit:IsAlive() then
          local targetunitname = targetunit:GetName()
          --local targetgroup = Unit.getGroup(Weapon.getTarget(ShootingWeapon)) --targeted group
          local targetgroup = nil
          local targetgroupname = "none"
          if targetcat == Object.Category.UNIT then
            if targetunit.ClassName == "UNIT" then
              targetgroup = targetunit:GetGroup()
            elseif targetunit.ClassName == "GROUP" then
              targetgroup = targetunit
            end
            targetgroupname = targetgroup:GetName() -- group name
          elseif targetcat == Object.Category.STATIC then
            targetgroup = targetunit
            targetgroupname = targetunitname
          end
          local text = string.format("%s Missile Target = %s", self.lid, tostring(targetgroupname))
          self:T( text )
          local m = MESSAGE:New(text,10,"Info"):ToAllIf(self.debug)
          -- check if we or a SAM site are the target 
          --local TargetGroup = EventData.TgtGroup -- Wrapper.Group#GROUP
          local shotatus = self:_CheckShotAtShorad(targetgroupname) --#boolean
          local shotatsams = self:_CheckShotAtSams(targetgroupname) --#boolean
          -- if being shot at, find closest SHORADs to activate
          if shotatsams or shotatus then
            self:T({shotatsams=shotatsams,shotatus=shotatus})
            self:WakeUpShorad(targetgroupname, self.Radius, self.ActiveTimer, targetcat)
          end
        end  
      end
    end
    return self
  end 
--
end
-----------------------------------------------------------------------
-- SHORAD end
-----------------------------------------------------------------------