# ****************************************************************************
# CUI
#
# The Advanced Framework for Simulation, Integration, and Modeling (AFSIM)
#
# The use, dissemination or disclosure of data in this file is subject to
# limitation or restriction. See accompanying README and LICENSE for details.
# ****************************************************************************

// converted BRAWLER v7.5 aslct7.f and akshn7.f
// Replicates the missile fire 7,3,1,x alternative behavior

include_once BrawlerScriptUtil.txt

behavior weapon_decision_missile

   script_debug_writes off

   script_variables

      WsfQuantumTaskerProcessor processor;

      //**********************************************************************//
      //** debugging parameters                                             **//
      //**********************************************************************//
      bool     mDrawSteering     = false;

      //**********************************************************************//
      //** alternative parameters                                           **//
      //**********************************************************************//
      // Flag used to enable/disable this alternative
      // Takes the place of production rules
      bool mAlternative7311Enabled = true;

      // Alternative ID
      int ilevel = 7;
      int kalt   = 3;
      int icall  = 1;  // Weapon number, for missiles 1 - 6
      int lcall  = 1;

      // Max/Min range for a SRM (meters)
      double mMaxRange = 25000 * Math.M_PER_FT();
      double mMinRange = 4000 * Math.M_PER_FT();

      // Maximum angle a shot can be taken at (degrees)
      double mMaxFiringAngle = 15.0;

      // Percent range is reduced by for simple envelope
      double mPercentRange = 0.2;

      // Maximum number of missiles on a target
      int mTargetSaturation = 1;
      // Required track quality for a shot
      double mRequiredTrackQuality = 0.8;

      //**********************************************************************//
      //********* VARIABLES BELOW THIS LINE ARE NOT FOR USER EDITING *********//
      //**********************************************************************//
      double grav   = 32.17405; // From pcon.fi

   end_script_variables

   on_init
      if (PROCESSOR.IsA_TypeOf("WSF_QUANTUM_TASKER_PROCESSOR"))
      {
         processor = (WsfQuantumTaskerProcessor)PROCESSOR;
      }
   end_on_init

   // Replicates the checks done in canfirm.f
   // Return true if the missile can be fired
   // Sets the string to the fail reason
   script bool canfirm(WsfLocalTrack target, string failMsg)
      /*
      cnfire = .false.
      --MISSILE COMING OFF RAIL
      if (lnchng) then
         rsfail = 'MISL COMING OFF RAIL'
         return
      endif

      --SWITCHOLOGY DELAYS
      pswtch = (time .ge. timemf)
      if (.not. pswtch)then
         rsfail = 'SWITCHOLOGY'
         return
      endif

      --CHECK IF TARGET IS IN ENVELOPE
      --NOTE THAT SOME PPM... VARIABLES ARE RECALCULATED.
      call mslenv_typ_mm(ppmiac,renv_typ)
      call mslenv(iacid,xp(1,me),vp(1,me),ap(1,me),
     1 alphap,xp(1,ppmiac),vp(1,ppmiac),ap(1,ppmiac),
     2 ppmptr, rbep, rwep, ext_mle, .true., renv_typ, ppmrmn, ppmrmx,
     3 ppmapt, ppmse, ppmaof, ppmenv, ppmtrk, ppmohr,l_config)
      if (.not. ppmenv)then
         rsfail = 'TGT NOT IN ENV'
         return
      endif

      --Does pilot beleive that target is within max range (R<Rmax1)?
      pinrng = prmax2(ppmiac,ppmrmx,1.,0.2)
      pbinrg = (pinrng .gt. pr2lim)
      if (.not.pbinrg)then
         rsfail = 'LOW PROB. TGT IN RNG'
         return
      endif
      if(lnchma.eq.0)then
         rsfail = 'FAIL ALL FIRE MODES'
         return
      endif
      cnfire = .true.
      */

      // Not currently checking for another missile coming off the rail

      // Not currently checking for switchology delays

      // CHECK IF TARGET IS IN ENVELOPE
      // Using a very simplified replacement of mslenv()
      // that take a percentage of ange and a simple angle check
      double range = PLATFORM.SlantRangeTo(target);
      double absRelativeBearing = MATH.Fabs(PLATFORM.RelativeBearingTo(target));
      if (range  < (mMinRange + mMinRange * mPercentRange) ||
          range  > (mMaxRange - mMaxRange * mPercentRange) ||
          absRelativeBearing >= mMaxFiringAngle )
      {
        #writeln("absRelativeBearing = ", absRelativeBearing, ", range = ", range);
        #failMsg = "TGT NOT IN ENV";
         target.SetAuxData("msg", "TGT NOT IN ENV");
         return false;
      }

      // Does pilot beleive that target is within max range (R<Rmax1)?
      // Using simplified version of prmax2
      // and assuming short range missile with rmax = 25000 ft
      double drng = (mMaxRange - PLATFORM.SlantRangeTo(target));
      if (drng < 0.0)
      {
        #failMsg = "LOW PROB. TGT IN RNG";
         target.SetAuxData("msg", "LOW PROB. TGT IN RNG");
         return false;
      }

      // Not currently checking launch modes
      // lnchma set if fctest() passes checks

      return true;
   end_script

   // Replicates the checks done in shldfrm.f
   // Return true if the missile should be fired
   // Sets the string to the fail reason if any
   script bool shldfrm(WsfLocalTrack target, string failMsg)
      /*
      C     --CHECK THAT PILOT BELIEVES RANGE IS OK FOR TARGETING
      pinrng = prmax2(ppmiac,ppmrmx,ppm_rpeak,0.2)
      pbinrg = pinrng.ge.pr2lim
      call pdset('PROB_IN_RANGE',pbinrg)
      call mslenv_typ_mm(ppmiac,renv_typ)
C
C     --CALCULATE tgleav
C     --POSSIBLE PROJECTIONS
C     --    ATTACKER                      TARGET
C     --(1) CONSTANT VELOCITY XEUAN       CONSTANT VELOCITY vecinc
C     --(2) CURRENT MANEUVER  XEUA        CURRENT MANEUVER  XEUT
C     --(3) CURRENT MANEUVER  XEUA        CONSTANT VELOCITY vecinc
C     --(4) CONSTANT VELOCITY XEUAN       CURRENT MANEUVER  XEUT
C     --LOGIC IS AS FOLLOWS:
C     --IF CASE (1) (neither maneuver) IS IN ENVELOPE THEN
C     --   IF CASE (2) (both maneuver) IS OUT OF ENVELOPE
C     --      IF CASE (3) (atkr maneuvers) IS IN ENVELOPE
C     --         IF CASE(4) (tgt maneuvers) IS OUT OF ENVELOPE
C     --            ASSERT: TARGET AT FAULT
C
C     CASE 1: CALCULATE INENVP_CC
      if (ient.eq.sament) then
         call xmit3(xp(1,me),xme)
         call xmit3(vp(1,me),vme)
         call xmit(9,rbep,rbeme)
         call xmit(9,rwep,rweme)
      else
         call xmit3(xeuan,xme)
         call xmit3(veuan,vme)
         call xmit(9,rbeuan,rbeme)
         call xmit(9,rweuan,rweme)
      endif
      call vecinc(xp(1,ppmiac),tproj3,vp(1,ppmiac),xtgt)
      call xmit3(vp(1,ppmiac),vtgt)
      call zero3(azero)
      xtgt(3) = amin1(xtgt(3),-50.)
      call mslenv(iacid,xme,vme,azero,alphap,xtgt,vtgt,azero,
     1  misl(mslpp),rbeme,rweme,.false.,.true.,renv_typ,rmin_mm,
     2  rmax_mm,aimp,se_mm,aof_mm,inenvp_cc,trkbl,ovrhoz,l_config)
C     --"target leaving" CANNOT BE TRUE IF NOT IN ENVELOPE WHEN NEITHER
C     --MANEUVERING
      if(.not.inenvp_cc)then
         tgleav = .false.
         goto 200
      endif
C
C     --CASE 2: CALCULATE INENVP_MM
      if (ient.eq.sament) then
C        --USE SAME AS CASE 1 SINCE I CAN'T MANEUVER
         continue
      else
         call xmit3(xeua,xme)
         call xmit3(veua,vme)
         call xmit(9,rbeua,rbeme)
         call xmit(9,rweua,rweme)
      endif
      call xmit3(xeut(1,ppmiac),xtgt)
      call xmit3(veut(1,ppmiac),vtgt)
      call mslenv(iacid,xme,vme,azero,alphap,xtgt,vtgt,azero,
     1  misl(mslpp),rbeme,rweme,.false.,.true.,renv_typ,rmin,rmax,
     2  aimp,se,aof,inenvp_mm,trkbl,ovrhoz,l_config)
C     --"target leaving" CANNOT BE TRUE IF STILL IN ENV. WHEN BOTH MANEUVER
      if(inenvp_mm)then
         tgleav = .false.
         goto 200
      endif
C
C     --CASE 3: CALCULATE INENVP_MC
      if (ient.eq.sament) then
C        --USE SAME AS CASE 1 SINCE I CAN'T MANEUVER
         continue
      else
         call xmit3(xeua,xme)
         call xmit3(veua,vme)
         call xmit(9,rbeua,rbeme)
         call xmit(9,rweua,rweme)
      endif
      call vecinc(xp(1,ppmiac),tproj3,vp(1,ppmiac),xtgt)
      call xmit3(vp(1,ppmiac),vtgt)
      xtgt(3) = amin1(xtgt(3),-50.)
      call mslenv(iacid,xme,vme,azero,alphap,xtgt,vtgt,azero,
     1  misl(mslpp),rbeme,rweme,.false.,.true.,renv_typ,rmin,rmax,
     2  aimp,se,aof,inenvp_mc,trkbl,ovrhoz,l_config)
      if(.not.inenvp_mc)then
         tgleav = .false.
         goto 200
      endif
C
C     --CASE 4: CALCULATE INENVP_CM
      if (ient.eq.sament) then
C        --USE SAME AS CASE 1 SINCE I CAN'T MANEUVER
         continue
      else
         call xmit3(xeuan,xme)
         call xmit3(veuan,vme)
         call xmit(9,rbeuan,rbeme)
         call xmit(9,rweuan,rweme)
      endif
      call xmit3(xeut(1,ppmiac),xtgt)
      call xmit3(veut(1,ppmiac),vtgt)
      call mslenv(iacid,xme,vme,azero,alphap,xtgt,vtgt,azero,
     1  misl(mslpp),rbeme,rweme,.false.,.true.,renv_typ,rmin,rmax,aimp,
     2  se,aof,inenvp_cm,trkbl,ovrhoz,l_config)
      tgleav = .not.inenvp_cm
C
200   continue
      call pdset('TGT_LEAVING_ENV',tgleav)
C
C     --Calculate current envelope level (WORSE)
      elevnow= envlvl(rngnow(me,ppmiac),rdotme(ppmiac),ppmse,
     1 ppmaof,ppmrmn,ppmrmx,ppm_rpeak,ppm_semax,aoffmx,
     2 ppmtrk,ppmohr,'SHOOT ')
C
      if (ient.ne.sament) then
         elevprj= envlvl(rng(ppmiac),rngr(ppmiac),se_mm,aof_mm,rmin_mm,
     1    rmax_mm,ppm_rpeak,ppm_semax,aoffmx,trkbl,ovrhoz,'SHOOT ')
         if(lprnt) write(ioutp,2000) rmin_mm,rmax_mm,
     1    rng(ppmiac),aimp,se_mm,aof_mm,elevprj
      else
C        --rng, rngr NOT SET FOR SAM, USE rngun, rngrun
         elevprj= envlvl(rngun(ppmiac),rngrun(ppmiac),se_mm,aof_mm,
     1    rmin_mm,rmax_mm,ppm_rpeak,ppm_semax,aoffmx,trkbl,ovrhoz,
     2    'SHOOT ')
         if(lprnt) write(ioutp,2000) rmin_mm,rmax_mm,
     1    rngun(ppmiac),aimp,se_mm,aof_mm,elevprj
      endif
C     --CHECK PROJECTION -- IS LEVEL DECREASING?
      worse = (elevnow.ge.elevprj)
      call pdset('SHOT_WORSENING',worse)
10    continue
C
C     --Check if target is too near beam for successful semi-active
C     -- missile shot
      lbeam =  beamsht()
      call pdset('SA_BEAM_SHOT',lbeam)
C
C     --If positive target ID required to fire, has ID been
C     --established?
      needid = (irel(ppmiac).eq.0) .and. (id_mode.ne.bvr_id_md)
      call pdset('SURE_BAD_GUY',.not.needid)
C
C     --check for fratricide risk
      call set_hldcod(mslpp,ppmiac,hldcode,pp_skrs,pp_undes,lfrshot)
      lfrat = lbit(hldcode,2)
      call pdset('FRAT_RISK',lfrat)
C
C     --CHECK IF FRIEND HAS A BETTER SHOT
      lfrnd = lbit(hldcode,1)
      call pdset('FRND_BETTER_SHOT',lfrnd)
C
C     --Is target already engaged with max number of missiles/target?
      other_tgtd = 0
      do 100 igg=1,ngg
         iacf = listf(igg)
         acidf = iacidt(iacf)
         if (iacid .eq. acidf)goto 100
C        --Don't count another's selection if you have a much better
C        --shot, since he will wait for you to shoot
         if (.not.lfrshot(iacf)) goto 100
C        --Don't count another's selection if you have selected the
C        --same target earlier!  RMK 9 Apr 92
C        --(Cant use others_tgt(iacid) here since not set until akshn7)
         if (others_tgt(acidf).eq.ppmiac)then
C           --However, we do need to use others_tgt(iacid) here, since
C           --we need to use others_msgtim(iacid), which is only correct
C           --if others_tgt(iacid) .eq. ppmiac
            if(others_tgt(iacid).eq.ppmiac) then
C              --I have selected this tgt previously
               if (others_msgtim(acidf).lt.others_msgtim(iacid)) then
C                 --He selected this target earlier than I, so count him
                  other_tgtd = other_tgtd + 1
               endif
            else
C              --I just changed to this target, so if anybody else has
C              --sent me a message selecting him, they must have gotten
C              --there first
               other_tgtd = other_tgtd + 1
            endif
         endif
100   continue
110   continue
      satur = ((nmhutl(ppmiac)+other_tgtd).ge.mxtgt_ac(ppmjid))
      call pdset('TARGET_SATURATED',satur)
      skr_lock = .false.
      do 300 iseek = 1, mx_skr
         if (skr_acquired(iseek)) then
            skr_lock = .true.
            goto 305
         endif
300   continue
305   continue
      call pdset('SEEKER_LOCK',skr_lock)
C
C     --CHECK WEAPON QUALITY TRACK
C     --no_trk indicates track not needed for this launch mode
      no_trk = (lnchma .eq. ppsli) .or.
     1         (lnchma .eq. ppslr) .or.
     2         (lnchma .eq. pcguv) .or.
     3         (lnchma .eq. pvtnhms)
      if (.not.usewqt(iflite)) then
C        --Flight does not require WQT
         needwqt = .false.
      elseif (no_trk) then
C        --Using a luanch mode that is not cued from a track
         needwqt = .false.
      else
         needwqt = .not.wpnqual
      endif
400   continue
C
C     --FIRING LOGIC:
      lfire =
     1 .not.(needid.or.satur.or.lfrat.or.lfrnd.or.lbeam.or.needwqt)
     2 .and.(pbinrg.or.(worse.and..not.tgleav))
      if(lfire)then
         rsfail = ' '
      else
         if(needid)then
            rsfail = 'NEED ID'
         elseif(satur)then
            rsfail = 'MXTGT_AC LIMIT'
         elseif(lfrat)then
            rsfail = 'FRATRICIDE RISK'
         elseif(lfrnd)then
            rsfail = 'FRND HAS BETTER SHOT'
         elseif(lbeam)then
            rsfail = 'TGT TOO NEAR BEAM'
         elseif(.not.(pbinrg.or.(worse.and..not.tgleav)))then
            rsfail = 'FAILED ENV LOGIC'
         elseif (needwqt) then
            rsfail = 'NO WEAPON QUAL TRK'
         else
            call nabort('SHLDFRM..unknown failure reason')
         endif
      endif
      */

      // ONLY Checking target stauration and track quality for now
      if (PLATFORM.WeaponsActiveFor(target.TrackId()) >= mTargetSaturation)
      {
         //lfire = true;
         writeln_d("   ", PLATFORM.Name(), " shouldn't fire ", target.TargetName(), " saturated");
         failMsg = "MXTGT_AC LIMIT";
         return false;
      }
      if (target.TrackQuality() < mRequiredTrackQuality)
      {
         failMsg = "NO WEAPON QUAL TRK";
         return false;
      }

      return true;
   end_script

   precondition
      #writeln_d(PLATFORM.Name(), " precondition weapon_decision_missile, T=", TIME_NOW);

      ### Evaluate conditions that would prevent behavior alternative from running

      if (!PROCESSOR.IsA_TypeOf("WSF_QUANTUM_TASKER_PROCESSOR"))
      {
         writeln_d("not a quantum tasker!");
         return Failure("behavior not attached to a WSF_QUANTUM_TASKER_PROCESSOR");
      }

      // aslct7.f line 123 - 132
#      --CHECK IF TARGET SELECTED
#      if (ppmiac .eq. 0 .and. .not.dewvmsl)then
#         rsfail = 'NO TARGET SELECTED'
#         goto 800
#      endif
#      --CHECK IF WEAPON SELECTED
#      if (ppmptr .eq. 0)then
#         rsfail = 'NO WEAPON SELECTED'
#         goto 800
#      endif
      WsfTaskList tasks = processor.TasksReceivedOfType("WEAPON");
      if(tasks.Count() <= 0)
      {
         return Failure("no tasks");
      }
      // Assuming 1v1 and first and only task is the one we want
      WsfLocalTrack targetTrack = PLATFORM.MasterTrackList().FindTrack(tasks.Entry(0).LocalTrackId());
      if(tasks.Count() <= 0 || targetTrack.IsNull() || !targetTrack.IsValid())
      {
         writeln_d("   ", PLATFORM.Name(), " no target selected at time: ", TIME_NOW);
         return Failure("no target selected");
      }
      // Weapon selection done in selwpn.f (called from aslct2.f)
      // checks weapon inventory. Use a similar check here as a replacement
      if (!PLATFORM.Weapon("srm").IsValid() || PLATFORM.Weapon("srm").QuantityRemaining() <= 0)
      {
         writeln_d("   ", PLATFORM.Name(), " no weapon selected at time: ", TIME_NOW);
         return Failure("no weapon selected");
      }

      // aslct7() calls canfir().
      // canfir.f checks production rules and GCI inhibit
      // just doing a simple flag for now
      if (!mAlternative7311Enabled)
      {
         writeln_d("behavior not enabled!");
         return Failure("behavior alternative not enabled");
      }

      //canfir.f calls canfirm()
      string msg = "";
      if (!canfirm(targetTrack, msg))
      {
         msg = targetTrack.AuxDataString("msg");
         writeln_d("   ", PLATFORM.Name(), " couldn't fire at time: ", TIME_NOW, " ", msg);
         return Failure(msg);
      }

      // aslct7 line 199 - 205
      // if undamaged calls shldfr().
      // if we are damaged skip checks and fire now
      if (PLATFORM.DamageFactor() <= 0.0)
      {
         // shldfr.f just calls the specific weapon type
         if (!shldfrm(targetTrack, msg))
         {
            writeln_d("   ", PLATFORM.Name(), " shouldn't fire at time: ", TIME_NOW, " ", msg);
            return Failure(msg);
         }
      }

      // aslct7 creates and alternative to evaluate.
      // The alternative has no projection and always
      // evaluates to 1, so skip that processing

      // All conditions pass, weapon can fire
      return true;
   end_precondition

   execute
      // Replicates akshn7.f
      // We don't need to do all the bookeeping that BRAWLER
      // does. Just launch the weapon.

      WsfTaskList tasks = processor.TasksReceivedOfType("WEAPON");
      // Assuming 1v1 and first and only task is the one we want
      WsfLocalTrack targetTrack = PLATFORM.MasterTrackList().FindTrack(tasks.Entry(0).LocalTrackId());

      //Assuming weapon name, checked in precondition
      WsfWeapon weapon = PLATFORM.Weapon("srm");
      bool launched = weapon.FireSalvo(targetTrack, 1); // Always firing one for now
      if (!launched)
      {
         writeln_d("   ", PLATFORM.Name(), " could NOT fire at track: ", targetTrack.TargetName(), " at time: ", TIME_NOW);
      }
   end_execute

end_behavior

