
//$reference parts\core\gamePlay.dll
using System;
using System.IO;
using maddox.game;
using maddox.game.world;
using maddox.GP;
using System.Collections.Generic;
using System.Globalization;

public class Mission : AMission
{
    private bool odenDebug = false;//true;// 
    internal enum AircraftClass { F, FB, B, R, M, T };
    private List<string> ArmyNone = new List<string>() { "nn", "du" };
    private List<string> ArmyRed = new List<string>() { "gb", "ru", "fr", "us", "pl", "rz", "sk" }; // sk is blue by default for whatever reason
    private List<string> ArmyBlue = new List<string>() { "de", "it", "ja", "fi", "hu", "ro" };
    internal class MissionMarker
    {
        internal double x;
        internal double y;
        internal int army;
        internal string key;
        public Point2d Pos2d() { return new Point2d(x, y); }
        public Point3d Pos3d() { return new Point3d(x, y, 0); }
        internal MissionMarker(double x, double y, int army, string key) { this.x = x; this.y = y; this.army = army; this.key = key; }
    }
    private List<MissionMarker> MissionMarkers = new List<MissionMarker>();
    internal class Squadron
    {
        internal int army;
        internal string id;
        internal string flight;
        internal string actype;
        internal AircraftClass AcClass;
        internal string formation;
        internal string weapons;
        internal bool active = false;
        internal List<string> borts = new List<string>();

        internal Squadron(int army, string id, string flight, string actype, string formation, string weapons)
        {
            this.army = army;
            this.id = id;
            this.flight = flight;
            this.actype = actype;
            this.formation = formation;
            this.weapons = weapons;
            for (int i = 1; i < 22; i++)
                borts.Add(i.ToString());
            this.AcClass = AircraftClass.F;
            if (actype.ToLower().StartsWith("aircraft.anson"))
                this.AcClass = AircraftClass.R;
            if (actype.ToLower().StartsWith("aircraft.dh82"))
                this.AcClass = AircraftClass.T;
            if (actype.ToLower().StartsWith("aircraft.wellington"))
                this.AcClass = AircraftClass.B;
            if (actype.ToLower().StartsWith("aircraft.blenheim") && !actype.ToLower().EndsWith("F"))
                this.AcClass = AircraftClass.B;
            if (actype.ToLower().StartsWith("aircraft.sunderland"))
                this.AcClass = AircraftClass.M;
            if (actype.ToLower().StartsWith("aircraft.walrus"))
                this.AcClass = AircraftClass.M;
            if (actype.ToLower().StartsWith("aircraft.bf-108"))
                this.AcClass = AircraftClass.T;
            if (actype.ToLower().StartsWith("aircraft.bf-110c-6"))
                this.AcClass = AircraftClass.FB;
            if (actype.ToLower().StartsWith("aircraft.bf-110c-7"))
                this.AcClass = AircraftClass.FB;
            if (actype.ToLower().StartsWith("aircraft.do-17"))
                this.AcClass = AircraftClass.B;
            if (actype.ToLower().StartsWith("aircraft.he-111"))
                this.AcClass = AircraftClass.B;
            if (actype.ToLower().StartsWith("aircraft.ju-87"))
                this.AcClass = AircraftClass.B;
            if (actype.ToLower().StartsWith("aircraft.ju-88"))
                this.AcClass = AircraftClass.B;
            if (actype.ToLower().StartsWith("aircraft.fw-200"))
                this.AcClass = AircraftClass.M;
            if (actype.ToLower().StartsWith("aircraft.he-115"))
                this.AcClass = AircraftClass.M;
        }
    }
    private List<Squadron> Squadrons = new List<Squadron>();
    const int RAF = 1;
    const int LW = 2;
    internal int PlayerArmy;
    Random rnd = new Random();
    CultureInfo cENUS = new CultureInfo("en-US");
    private int NextMissionNum;
    private string fSkill = "1 0.26 0.26 0.89 0.89 0.89 0.47 0.47"; // basic,adv,aware,gunnery,tact,vision,brave,dicipline
    //private string bSkill = "1 0.26 0.26 0.89 0.89 0.47 0.47 0.89"; // basic,adv,aware,gunnery,tact,vision,brave,dicipline
    private string MapName;
    private bool playerWasAirborne;
    AiAircraft PlayerAC = null;
    AiAirport homeAirport;
    DateTime gameDateTime = new DateTime(1940, 9, 10);
    DateTime timeoutRadio;
    private List<string> misAirGroups = new List<string>();
    private List<string> activatedAirports = new List<string>();
    private List<GroundStationary> ChainHomeRadars = new List<GroundStationary>();
    private List<GroundStationary> Industries = new List<GroundStationary>();
    private List<GroundStationary> PortCranes = new List<GroundStationary>();
    private List<AiGroundGroup> ConvoyShips = new List<AiGroundGroup>();
    private Point2d calcPos(Point2d pt2d, double dist, double dir)
    {
        dir = dir * Math.PI / 180;
        return new Point2d(pt2d.x + (dist * Math.Sin(dir)), pt2d.y + (dist * Math.Cos(dir)));
    }
    private Point3d calcPos(Point3d pt3d, double dist, double dir)
    {
        dir = dir * Math.PI / 180;
        return new Point3d(pt3d.x + (dist * Math.Sin(dir)), pt3d.y + (dist * Math.Cos(dir)), pt3d.z);
    }
    private double calcDir(Point2d from2d, Point2d to2d)
    {
        double rad = Math.Atan2(to2d.x - from2d.x, to2d.y - from2d.y);
        double deg = rad * 180 / Math.PI;
        if (deg < 0)
            return 360 + deg;
        return deg;
    }
    private double calcDir(Point3d from3d, Point3d to3d)
    {
        double rad = Math.Atan2(to3d.x - from3d.x, to3d.y - from3d.y);
        double deg = rad * 180 / Math.PI;
        if (deg < 0)
            return 360 + deg;
        return deg;
    }
    private double acHeading(AiAircraft ac)
    {
        double dir = ac.getParameter(part.ParameterTypes.Z_Orientation, 0);
        dir = 90f - dir; // shows 160 heading NW, 0 due east, -90 south and 90 north
        if (dir < 0) dir += 360f;
        return dir;
    }
    private bool acStandStill(AiAircraft ac)
    {
        double ias = ac.getParameter(part.ParameterTypes.I_VelocityIAS, 0);
        return ias < 1;
    }

    public override void OnBattleStarted()
    {
        base.OnBattleStarted();

        MissionNumberListener = -1;
        NextMissionNum = GamePlay.gpNextMissionNumber();
        if (GamePlay is GameServerDef)
        {
            if (odenDebug) GamePlay.gpLogServer(null, "OnBattleStarted: '{0}' {1}", new object[] { "GameServerDef", NextMissionNum });
            GameServerDef gameServer = (GameServerDef)GamePlay;
            ISectionFile missFile = gameServer.game.FirstMissionFile();
            ReadMissionFile(missFile);
        };
        if (GamePlay is GameSingleDef)
        {
            if (odenDebug) GamePlay.gpLogServer(null, "OnBattleStarted: '{0}' {1}", new object[] { "GameSingleDef", NextMissionNum });
            GameSingleDef gameServer = (GameSingleDef)GamePlay;
            ISectionFile missFile = gameServer.game.FirstMissionFile();
            ReadMissionFile(missFile);
        };

        //AiAirport ap = GamePlay.gpAirports()[0];
        PlayerArmy = GamePlay.gpPlayer().Army();
        PlayerAC = (AiAircraft)GamePlay.gpPlayer().Place();
        homeAirport = FindAirport(GamePlay.gpPlayer().Army(), GamePlay.gpPlayer().Place().Pos(), 0);
        if (odenDebug) GamePlay.gpLogServer(null, "OnBattleStarted: '{0}' {1} {2} {3} {4}", new object[] { "StartMission", GamePlay.gpPlayer().Name(), GamePlay.gpPlayer().Army(), GamePlay.gpPlayer().Place().Pos(), homeAirport.Name() });

        GamePlay.gpLogServer(null, " ", new object[] { });
        GamePlay.gpLogServer(null, "New Day", new object[] { });
        GamePlay.gpLogServer(null, "{0}", new object[] { gameDateTime.ToString("D", cENUS) });
        GamePlay.gpLogServer(null, "{0}", new object[] { homeAirport.Name().Replace('_', ' ') });
        StartMission(homeAirport);
    }
    public override void OnMissionLoaded(int missionNumber)
    {
        base.OnMissionLoaded(missionNumber);

    }
    public override void OnTickGame()
    {
        base.OnTickGame();

        if (Time.tickCounter() % 3600 == 1500) // every 2min c:a
        {
            DateTime currDT = DateTime.Now;
            if (odenDebug) GamePlay.gpLogServer(null, "Tick {0}", new object[] { currDT.Minute });
            List<AiAirGroup> AirGroups = new List<AiAirGroup>();
            if (GamePlay.gpAirGroups(LW) != null)// wth?
                foreach (AiAirGroup ag in GamePlay.gpAirGroups(LW))
                    AirGroups.Add(ag);
            if (GamePlay.gpAirGroups(RAF) != null)
                foreach (AiAirGroup ag in GamePlay.gpAirGroups(RAF))
                    AirGroups.Add(ag);
            int planes = 0;
            foreach (AiAirGroup ag in AirGroups)
            {
                planes += ag.NOfAirc;
            };
            foreach (Squadron sqn in Squadrons)
                sqn.active = AirGroups.Exists(g => g.Name().EndsWith(sqn.id));

            if (odenDebug) GamePlay.gpLogServer(null, "OnTickGame {0}", new object[] { planes });
            Player player = GamePlay.gpPlayer();
            if (planes < 55 && player != null)
            {
                AiActor actor = player.Place();
                AiAircraft ac = (AiAircraft)actor;
                if (ac != null)
                {
                    Point3d pt3d = ac.Pos();
                    if (ac.IsAirborne())
                    {
                        double dir = acHeading(ac);
                        //Point2d pt2d = new Point2d(pt3d.x, pt3d.y);
                        RedirectAirGroups(ac.Army(), calcPos(pt3d, 16093 / 4, dir));
                    };
                    List<AiAirport> aps = SortedAirports(0, ac.Pos(), false);
                    AiAirport ap = aps[0];
                    if (!ac.IsAirborne() && aps.Count > 1)
                        ap = aps[1];
                    if (!PlayerArmy.Equals(GamePlay.gpFrontArmy(pt3d.x, pt3d.y)) && aps.Count > 8)
                        ap = aps[rnd.Next(8)];
                    ISectionFile f = GamePlay.gpCreateSectionFile();
                    CreateFlight(f, ap, 0);
                    GamePlay.gpPostMissionLoad(f);
                    NextMissionNum++;
                };
            };


        };
    }
    public override void OnTrigger(int missionNumber, string shortName, bool active)
    {
        base.OnTrigger(missionNumber, shortName, active);

        if (odenDebug) GamePlay.gpLogServer(null, "Trigger for ({0}:) {1} active={2}", new object[] { missionNumber, shortName, active.ToString() });

        if (active)
        {
        }
        else
        {   // triggers going false for logic
        };
    }
    public override void OnPlaceEnter(Player player, AiActor actor, int placeIndex)
    {
        base.OnPlaceEnter(player, actor, placeIndex);

        if (player.Place() is AiAircraft)
        {
            AiAircraft ac = player.Place() as AiAircraft;
            if (ac.IsAirborne())
                playerWasAirborne = true;
            if (odenDebug) GamePlay.gpLogServer(null, "OnPlaceEnter: {0} actor: {1} {2}", new object[] { player, actor.Name(), playerWasAirborne });
            if (player.Army() == RAF)
                Timeout(5, () =>
                {   // bite me
                    double dir = acHeading(ac);// ac.getParameter(part.ParameterTypes.Z_Orientation, 0);
                    //dir = 90f - dir; // shows 160 heading NW, 0 due east, -90 south and 90 north
                    //if (dir < 0) dir += 360f;
                    if (odenDebug) GamePlay.gpLogServer(null, "OnPlaceEnter: dir:{0} mag: {1}", new object[] { dir, ac.getParameter(part.ParameterTypes.Z_Orientation, 0) });
                    GamePlay.gpHUDLogCenter(new Player[] { player }, "Set your gyro to " + dir.ToString("F0"));
                });
            //SetOrderMissionMenuOden(player);
        };
    }
    public override void OnActorCreated(int missionNumber, string shortName, AiActor actor)
    {
        base.OnActorCreated(missionNumber, shortName, actor);

        if (shortName == "NONAME")
            return;
        if (odenDebug) GamePlay.gpLogServer(null, "OnActorCreated: shortName:{0} actor.Name(): {1}", new object[] { shortName, actor.Name() });
        if (actor != null)
        {
            if (actor is AiAircraft)
            {
            };

            if (actor is AiAirGroup)
            {
                if (odenDebug) GamePlay.gpLogServer(null, "OnActorCreated AiAirGroup: {0} actor: {1}", new object[] { shortName, actor.Name() });
            };
        };
    }
    public override void OnActorDead(int missionNumber, string shortName, AiActor actor, List<DamagerScore> initiatorList)
    {
        base.OnActorDead(missionNumber, shortName, actor, initiatorList);

        // this trigger on classes not starting with "Stationary" such as "Vehicle" and "Artillery"
        if (actor == null)
            return;
        if (Time.tickCounter() < 900) // spawn error within first 30 secs (30 ticks a second)
            return;

        if (odenDebug) GamePlay.gpLogServer(null, "OnActorDead shortName: {0} actor: {1} hit by:{2}", new object[] { shortName, actor.Name(), initiatorList.Count });

        if (GamePlay.gpPlayer() != null)
        {
            if (GamePlay.gpPlayer().Place() != null)
            {
                //if (GamePlay.gpPlayer().Place() == actor)
                //    playerWasKilled = true;
            };
        };

        if (actor is AiGroundActor)
        {
            AiGroundActor gndActor = (AiGroundActor)actor;
            if (gndActor.Type().ToString().Equals("ShipTransport"))
            {
            }
            else
            {
            };
        };

        if (actor is AiAircraft)
        {
            AiAircraft ac = (AiAircraft)actor;
            AiAirGroup ag = ac.AirGroup();
            if (odenDebug) GamePlay.gpLogServer(null, "OnActorDead AiAircraft: '{0}' '{1}' '{2}'", new object[] { ac.InternalTypeName(), ac.Regiment().name(), ag.Name() });

            if (GamePlay.gpPlayer() == null)
                return;
            if (GamePlay.gpPlayer().Place() == null)
                return;
            if ((AiGroup)ag == GamePlay.gpPlayer().Place().Group())
            {
                AiAircraft pac = (AiAircraft)GamePlay.gpPlayer().Place();
                if (pac != null)
                {
                    AiActor leader = GetFlightLeader(ag);
                    int entry = rnd.Next(0, 17);
                    if (actor == leader)
                        entry = 18 + rnd.Next(0, 2);

                    switch (entry)
                    {
                        case 0: pac.SayToGroup(pac.AirGroup(), "Watch_out1"); break;
                        case 1: pac.SayToGroup(pac.AirGroup(), "Watch_out2"); break;
                        case 2: pac.SayToGroup(pac.AirGroup(), "Watch_out3"); break;
                        case 3: pac.SayToGroup(pac.AirGroup(), "Watch_out4"); break;
                        case 4: pac.SayToGroup(pac.AirGroup(), "We_are_under_attack_Repeat_we_are_under_attack_We_have_lost_aircraft"); break;
                        case 5: pac.SayToGroup(pac.AirGroup(), "We_are_under_attack_requesting_immediate_assistance"); break;
                        case 6: pac.SayToGroup(pac.AirGroup(), "We_are_under_attack_we_need_assistance_over"); break;
                        case 7: pac.SayToGroup(pac.AirGroup(), "You_Come_on_men_we_can_do_better_than_that"); break;
                        case 8: pac.SayToGroup(pac.AirGroup(), "Hell"); break;
                        case 9: pac.SayToGroup(pac.AirGroup(), "Hell1"); break;
                        case 10: pac.SayToGroup(pac.AirGroup(), "Hell2"); break;
                        case 11: pac.SayToGroup(pac.AirGroup(), "Hell3"); break;
                        case 12: pac.SayToGroup(pac.AirGroup(), "Hell4"); break;
                        case 13: pac.SayToGroup(pac.AirGroup(), "Damn"); break;
                        case 14: pac.SayToGroup(pac.AirGroup(), "Damn1"); break;
                        case 15: pac.SayToGroup(pac.AirGroup(), "Bail_out_for_Gods_sakes"); break;
                        case 16: pac.SayToGroup(pac.AirGroup(), "Bail_out_Bail_out"); break;
                        case 17: pac.SayToGroup(pac.AirGroup(), "Bail_Out"); break;
                        case 18: pac.SayToGroup(pac.AirGroup(), "Blast_it_Is_that_the_squadron_leader_going_down"); break;
                        case 19: pac.SayToGroup(pac.AirGroup(), "Bloody_hell_we_ve_lost_the_squadron_leader"); break;
                        case 20: pac.SayToGroup(pac.AirGroup(), "The_squadron_leader_s_done_for_What_are_we_going_to_do"); break;
                    };
                };
            };
        }
        else
        {
            //if (actor.Army() == RAF) deadStaticsRed++;
            //if (actor.Army() == LW) deadStaticsBlue++;
        };
    }
    public override void OnAircraftTookOff(int missionNumber, string shortName, AiAircraft aircraft)
    {
        base.OnAircraftTookOff(missionNumber, shortName, aircraft);

        if (aircraft != null)
            if (aircraft.Player(0) != null)
            {
                playerWasAirborne = true;
                //Battle.doSingleBattleSuccess(true);
            };
    }
    public override void OnAircraftLanded(int missionNumber, string shortName, AiAircraft aircraft)
    {
        base.OnAircraftLanded(missionNumber, shortName, aircraft);

        if (aircraft == null)
            return;
        AiAirGroup ag = aircraft.AirGroup();
        if (ag == null)
            return;

        Point2d lnd2d = new Point2d(aircraft.Pos().x, aircraft.Pos().y);
        AiAirport ap = FindAirport(ag.Army(), lnd2d, 0);
        if (ap != null)
        {
            Point3d pt3d = aircraft.Pos();
            if (ap.Pos().distance(ref pt3d) < ap.CoverageR())
            {
                if (aircraft.Player(0) != null)
                {
                    if (ag.Army() == GamePlay.gpFrontArmy(lnd2d.x, lnd2d.y))
                    {
                        if (homeAirport.Name().Equals(ap.Name()))
                            GamePlay.gpHUDLogCenter(new Player[] { aircraft.Player(0) }, "Welcome back to " + ap.Name().Replace('_', ' '));
                        else GamePlay.gpHUDLogCenter(new Player[] { aircraft.Player(0) }, "Welcome to " + ap.Name().Replace('_', ' ') + ", there's free beer in the mess.");

                        Timeout(10, () =>
                        {
                            aircraft.RefuelPlane(100);
                            aircraft.RearmPlane(false);
                            GamePlay.gpHUDLogCenter(new Player[] { aircraft.Player(0) }, "Aircraft rearmed and refueled");
                        });
                    }
                    else
                    {
                        GamePlay.gpHUDLogCenter(new Player[] { aircraft.Player(0) }, "Erh.. you do know " + ap.Name().Replace('_', ' ') + " is enemy territory?");
                    };
                }
            }
        }
        if (aircraft.Player(0) == null)
            Timeout(150, () => { if (aircraft != null) aircraft.Destroy(); });
    }
    public override void OnAircraftCrashLanded(int missionNumber, string shortName, AiAircraft aircraft)
    {
        base.OnAircraftCrashLanded(missionNumber, shortName, aircraft);
    }


    private void ReadMissionFile(ISectionFile missFile)
    {
        //GamePlay.gpLogServer(null, "PathMyself: '{0}' '{1}' '{2}'", new object[] { PathMyself, sPathMyself, BaseMissionPath });
        //PathMyself = mission/quick/LeisureFlight.cs
        //sPathMyself = mission/quick/LeisureFlight.cs
        //BaseMissionPath = C:\Users\<name>\Documents\1C SoftClub\il-2 sturmovik cliffs of dover\mission\quick\
        string sectAG = "AirGroups";
        if (PathMyself.Contains("/quick/"))
            sectAG = "AiGroups";
        string key, value;
        MapName = missFile.get("MAIN", "MAP");
        int n = missFile.lines("MAIN");
        for (int i = 0; i < n; i++)
        {
            missFile.get("MAIN", i, out key, out value);
            switch (key)
            {
                case "Army":
                    string[] strs = value.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                    switch (strs[0])
                    {
                        case "0"://neutrals
                            ArmyNone.Clear();
                            foreach (string str in strs)
                                if (str.Length > 1)
                                    ArmyNone.Add(str);
                            break;
                        case "1"://RAF
                            ArmyRed.Clear();
                            foreach (string str in strs)
                                if (str.Length > 1)
                                    ArmyRed.Add(str);
                            break;
                        case "2"://LW
                            ArmyBlue.Clear();
                            foreach (string str in strs)
                                if (str.Length > 1)
                                    ArmyBlue.Add(str);
                            break;
                    };
                    break;
            }
        };
        n = missFile.lines("FrontMarker");
        for (int i = 0; i < n; i++)
        {
            key = "FrontMarker" + i.ToString();
            if (missFile.exist("FrontMarker", key))
            {	//FrontMarker0 287950.90 85215.27 2
                value = missFile.get("FrontMarker", key);
                string[] strs = value.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                if (strs.Length == 3)
                {	//double.TryParse(s, System.Globalization.NumberStyles.Any, rts.RTSConf.enCultureInfo, out res)
                    MissionMarker mm = new MissionMarker(0, 0, 0, key);
                    double.TryParse(strs[0], System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture.NumberFormat, out mm.x);
                    double.TryParse(strs[1], System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture.NumberFormat, out mm.y);
                    int.TryParse(strs[2], out mm.army);
                    MissionMarkers.Add(mm);
                };
            };
        };
        n = missFile.lines(sectAG);
        for (int i = 0; i < n; i++)
        {
            missFile.get(sectAG, i, out key, out value);
            if (odenDebug) GamePlay.gpLogServer(null, "AirGroups: {0} : {1}", new object[] { key, value });
            misAirGroups.Add(key);
        };
        foreach (string sect in misAirGroups)
        {
            string flight = missFile.get(sect, "Flight0");
            string actype = missFile.get(sect, "Class");
            string form = missFile.get(sect, "Formation");
            string wpns = missFile.get(sect, "Weapons");
            string script = missFile.get(sect, "SpawnFromScript");
            int army = 2;//LW
            if (sect.Contains("_RAF_"))
                army = 1;
            if (sect.Contains("_RAAF_"))
                army = 1;
            if (sect.Contains("_SAAF_"))
                army = 1;
            if (sect.Contains("_AA_"))
                army = ArmyRed.Contains("fr") ? 1 : 2;// AdA
            if (sect.Contains("_CAF_"))
                army = 1;
            if (sect.Contains("_USAAF_"))
                army = 1;
            if (sect.Contains("LONDON"))
                army = 1;
            if (sect.Contains("_RA_"))
                army = ArmyRed.Contains("it") ? 1 : 2;// Regia Aeronautica
            if (script != null)
                Squadrons.Add(new Squadron(army, sect, flight, actype, form, wpns));
        };
    }
    private void StartMission(AiAirport ap)
    {
        timeoutRadio = DateTime.Now;
        timeoutRadio.AddSeconds(120);
        //if (odenDebug) GamePlay.gpLogServer(null, "StartMission: {0}", new object[] { "" });


        AiAircraft pac = (AiAircraft)GamePlay.gpPlayer().Place();
        if (pac == null)
        {
            return;
        };

        AiAirport apE = FindAirport(GamePlay.gpPlayer().Army() == RAF ? LW : RAF, GamePlay.gpPlayer().Place().Pos(), 0);
        ISectionFile f1 = GamePlay.gpCreateSectionFile();
        CreateFlight(f1, ap, 8);
        CreateFlight(f1, ap, 0);
        CreateFlight(f1, apE, 0);
        GamePlay.gpPostMissionLoad(f1);
        NextMissionNum++;

        string strSay = "You_re_cleared_for_take_off";
        switch (rnd.Next(1, 5))
        {
            case 1: strSay = "Takeoff";
                break;
            case 2: strSay = "Wingmen_make_sure_you_stay_with_your_leaders";
                break;
            case 3: strSay = "Stay_sharp_gentlemen_And_make_sure_to_stay_in_formation";
                if (GamePlay.gpPlayer().Army() == LW) strSay = "Use_your_rudder_to_counter_the_yaw";
                break;
            case 4: strSay = "Prepare_For_Takeoff";
                break;
        };

        AiAirGroup pag = pac.AirGroup();
        AiActor leader = GetFlightLeader(pag);
        AiAircraft lac = (AiAircraft)leader;
        if (lac != null)
            Timeout(rnd.Next(4, 12), () => { lac.SayToGroup(pag, strSay); });
        Point3d pt3d = pac.Pos();
        //oldWP = new AiWayPoint(ref pt3d, 0);

        Point2d pt2d = new Point2d(pt3d.x, pt3d.y);

    }

    private void AircraftUpdateDead(AiAircraft ac)
    {
        if (ac == null)
            return;

        AiAirGroup ag = ac.AirGroup();
    }
    private void ArmyHUDLogCenter(int army, string msg)
    {
        Player localPlayer = GamePlay.gpPlayer();
        List<Player> players = new List<Player>();
        if (GamePlay.gpRemotePlayers() != null)
            foreach (Player p in GamePlay.gpRemotePlayers())
            {
                if (p.Army() == army) players.Add(p);
            };

        if (localPlayer != null)
            if (localPlayer.Army() == army) players.Add(localPlayer);
        GamePlay.gpHUDLogCenter(players.ToArray(), msg);
    }
    private List<Player> AllPlayers(int army)
    {
        List<Player> players = new List<Player>();
        Player lp = GamePlay.gpPlayer();
        if (lp != null)
            if (lp.Army() == army || army == 0)
                players.Add(lp);
        if (GamePlay.gpRemotePlayers() != null)
            foreach (Player p in GamePlay.gpRemotePlayers())
                if (p.Army() == army || army == 0)
                    players.Add(p);
        return players;
    }
    private bool PlayerIsBomber()
    {
        Player lp = GamePlay.gpPlayer();
        if (lp == null)
            return false;
        AiActor actor = lp.Place();
        if (actor == null)
            return false;
        AiAircraft pac = (actor as AiAircraft);
        if (pac == null)
            return false;
        AiAirGroup pag = pac.AirGroup();
        return (pag.hasBombs()
            || pag.hasTorpedos()
            || pag.hasBombsInInternalBays()
            || pac.Type() > AircraftType.JaBo);
    }
    private bool AiAirGroupHasGroundTarget(AiAirGroup ag)
    {   // tags fighters going groundtarget too
        if (ag == null)
            return false;
        AiWayPoint[] arrWP = ag.GetWay();
        foreach (AiWayPoint wp in arrWP)
            if (wp is AiAirWayPoint)
                switch ((wp as AiAirWayPoint).Action)
                {
                    case AiAirWayPointType.GATTACK_POINT:
                    case AiAirWayPointType.GATTACK_TARG:
                        return true;
                };
        return false;
    }
    private bool AiAirGroupCanIntercept(AiAirGroup ag)
    {
        if (ag == null)
            return false;
        AiAircraft ac = (AiAircraft)GetFlightLeader(ag);
        if (ac == null)
            return false;
        if (!ac.IsAirborne())
            return false;
        return !(ag.hasBombs()
            || ag.hasTorpedos()
            || ag.hasBombsInInternalBays()
            || ac.Type() > AircraftType.JaBo);
    }
    internal AiAirport FindAirport(int army, Point2d pt2d, int runwaylength)
    {   // requires fronline
        Point3d pt3d = new Point3d(pt2d.x, pt2d.y, 0);
        return FindAirport(army, pt3d, runwaylength);
    }
    internal AiAirport FindAirport(int army, Point3d pt3d, int runwaylength)
    {   // requires fronline
        AiAirport result = null;
        foreach (AiAirport ap in GamePlay.gpAirports())
        {
            if ((GamePlay.gpFrontArmy(ap.Pos().x, ap.Pos().y) == army || army == 0) && ap.FieldR() > runwaylength)
            //if ((ap.Army() == army || army == 0) && ap.FieldR() > runwaylength)
            {
                if (result != null)
                {
                    if (result.Pos().distance(ref pt3d) > ap.Pos().distance(ref pt3d))
                        result = ap;
                }
                else
                    result = ap;
            };
        };
        return result;
    }
    internal List<AiAirport> SortedAirports(int army, Point2d pt2d, bool onlyBattleArea)
    {   // requires fronline
        Point3d pt3d = new Point3d(pt2d.x, pt2d.y, 0);
        return SortedAirports(army, pt3d, onlyBattleArea);
    }
    internal List<AiAirport> SortedAirports(int army, Point3d pt3d, bool onlyBattleArea)
    {   // requires fronline
        List<AiAirport> SortedAirports = new List<AiAirport>();

        foreach (AiAirport ap in GamePlay.gpAirports())
        {
            if (!GamePlay.gpLandType(ap.Pos().x, ap.Pos().y).Equals(LandTypes.WATER))
            {
                string sectorName = "";
                if (onlyBattleArea)
                    sectorName = GamePlay.gpSectorName(ap.Pos().x, ap.Pos().y);
                if ((GamePlay.gpFrontArmy(ap.Pos().x, ap.Pos().y) == army || army == 0) && (sectorName.IndexOf(">") == -1 && sectorName.IndexOf("<") == -1))
                {
                    int forEachIndex = 0;
                    foreach (AiAirport sap in SortedAirports)
                    {
                        if (sap.Pos().distance(ref pt3d) > ap.Pos().distance(ref pt3d))
                        {
                            SortedAirports.Insert(forEachIndex, ap);
                            break;
                        };
                        forEachIndex++;
                    };
                    if (!SortedAirports.Contains(ap)) SortedAirports.Add(ap); // add last
                };
            };
        };
        return SortedAirports;
    }
    internal List<MissionMarker> SortedMissionMarkers(Point2d pt2d, int army)
    {   // requires frontline
        List<MissionMarker> SortedMissionMarkers = new List<MissionMarker>();
        foreach (MissionMarker mm in MissionMarkers)
        {
            if (mm.army == army)
            {
                int forEachIndex = 0;
                foreach (MissionMarker smm in SortedMissionMarkers)
                {
                    if (smm.Pos2d().distance(ref pt2d) > mm.Pos2d().distance(ref pt2d))
                    {
                        SortedMissionMarkers.Insert(forEachIndex, mm);
                        break;
                    };
                    forEachIndex++;
                };
                if (!SortedMissionMarkers.Exists(m => m.key == mm.key)) SortedMissionMarkers.Add(mm); // add last
            };
        };
        return SortedMissionMarkers;
    }
    internal GroundStationary FindChainHomeRadar(Point2d pt2d)
    {
        GroundStationary result = null;
        Point3d pt3d = new Point3d(pt2d.x, pt2d.y, 0);
        foreach (GroundStationary gs in ChainHomeRadars)
        {
            if (gs != null)
                if (result != null)
                {
                    if (result.pos.distance(ref pt3d) > gs.pos.distance(ref pt3d))
                        result = gs;
                }
                else
                    result = gs;
        };
        return result;
    }
    internal List<AiAircraft> SortedAircrafts(int army, Point3d pt3d)
    {
        List<AiAircraft> sorted = new List<AiAircraft>();
        AiAirGroup[] airGroups = GamePlay.gpAirGroups(army);

        if (airGroups != null)
            foreach (AiAirGroup airGroup in airGroups)
                if (airGroup != null)
                    if (airGroup.GetItems().Length > 0)
                        foreach (AiActor actor in airGroup.GetItems())
                            if (actor != null)
                                if (actor is AiAircraft)
                                {
                                    AiAircraft ac = (AiAircraft)actor;
                                    int forEachIndex = 0;
                                    foreach (AiAircraft sac in sorted)
                                    {
                                        if (sac.Pos().distance(ref pt3d) > ac.Pos().distance(ref pt3d))
                                        {
                                            sorted.Insert(forEachIndex, ac);
                                            break;
                                        };
                                        forEachIndex++;
                                    };
                                    if (!sorted.Contains(ac)) sorted.Add(ac); // add last
                                };

        return sorted;
    }
    internal List<AiActor> FindAircrafts(int army)
    {
        List<AiActor> AllActors = new List<AiActor>();
        foreach (AiAirGroup airGroup in GamePlay.gpAirGroups(army))
        {
            foreach (AiActor actor in ((airGroup as AiGroup).GetItems()))
                AllActors.Add(actor);
        };
        return AllActors;
    }
    internal AiActor GetFlightLeader(AiAirGroup ag)
    {
        if (ag == null)
            return null;
        if (ag.GetItems().Length < 1)
            return null;
        foreach (AiActor a in ag.GetItems())
            if (GamePlay.gpActorIsValid(a))
                return a;
        return null;
    }
    internal List<Point2d> SortByDistance(List<Point2d> ListPt2d, Point2d RefPt2d)
    {
        List<Point2d> SortedPt2d = new List<Point2d>();

        foreach (Point2d pt2d in ListPt2d)
        {
            if (SortedPt2d.Count == 0) SortedPt2d.Add(pt2d);
            else
            {
                int forEachIndex = 0;
                foreach (Point2d spt2d in SortedPt2d)
                {
                    if (spt2d.distance(ref RefPt2d) > pt2d.distance(ref RefPt2d))
                    {
                        SortedPt2d.Insert(forEachIndex, pt2d);
                        break;
                    };
                    forEachIndex++;
                };
            };
            if (!SortedPt2d.Contains(pt2d)) SortedPt2d.Add(pt2d);
        };

        return SortedPt2d;
    }
    private void AddToFile(ISectionFile f, string sect, string key, string value)
    {
        if (odenDebug) GamePlay.gpLogServer(null, "AddToFile {0} {1} {2}", new object[] { sect, key, value });
        f.add(sect, key, value);
    }

    private void CreateFlight(ISectionFile f, AiAirport ap, int size)
    {
        if (odenDebug) GamePlay.gpLogServer(null, "CreateFlight {0} {1}-ship", new object[] { ap.Name(), size });
        Point3d pt3d = ap.Pos();
        Point2d pt2d = new Point2d(pt3d.x, pt3d.y);
        int army = GamePlay.gpFrontArmy(pt2d.x, pt2d.y);
        bool isEnemy = !PlayerArmy.Equals(army);
        List<AiAirport> aps = SortedAirports(army, pt2d, false);
        if (aps.Count < 2)
            return;

        AiActor[] actorsQueue = null;
        string apName = "AIR";
        if (ap != null)
        {
            actorsQueue = ap.QueueTakeoff();
            apName = ap.Name();
        };
        bool setOnPark = false;
        if (actorsQueue != null || activatedAirports.Contains(apName))
            setOnPark = true;
        if (ap != null)
            activatedAirports.Add(apName);

        if (odenDebug) GamePlay.gpLogServer(null, "CreateFlight {0} from {1} airports", new object[] { apName, aps.Count });
        List<Squadron> sqns = Squadrons.FindAll(s => s.army.Equals(army) && !s.active);
        if (!PlayerArmy.Equals(army))
            if (PlayerAC.IsAirborne())
                sqns = Squadrons.FindAll(s => s.army.Equals(army) && !s.active && s.AcClass.Equals(AircraftClass.F));
            else sqns = Squadrons.FindAll(s => s.army.Equals(army) && !s.active && !s.AcClass.Equals(AircraftClass.F));
        if (sqns.Count == 0)
            return;


        Squadron sqn = sqns[rnd.Next(sqns.Count)];
        if (odenDebug) GamePlay.gpLogServer(null, "CreateFlight Sqn: {0} {1}", new object[] { sqn.id, sqn.flight });
        string[] FlightSection = sqn.flight.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
        if (size == 0)
            size = 1 + rnd.Next(FlightSection.Length);
        size = Math.Min(FlightSection.Length, size);
        size = Math.Min(sqn.borts.Count, size);
        if (size < 1)
            return;

        if (odenDebug) GamePlay.gpLogServer(null, "CreateFlight {0}-ship", new object[] { size });
        string flight = sqn.borts[rnd.Next(sqn.borts.Count)];
        for (int b = 1; b < size; b++)
            flight += " " + sqn.borts[rnd.Next(sqn.borts.Count)];
        foreach (string bortno in flight.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries))
            sqn.borts.Remove(bortno);

        AddToFile(f, "AirGroups", sqn.id, "");
        AddToFile(f, sqn.id, "Flight0", flight);
        AddToFile(f, sqn.id, "Class", sqn.actype);
        AddToFile(f, sqn.id, "Formation", sqn.formation);
        AddToFile(f, sqn.id, "CallSign", ((int)(rnd.Next(0, 35))).ToString());
        AddToFile(f, sqn.id, "Fuel", "84");
        AddToFile(f, sqn.id, "Weapons", sqn.weapons);
        if (setOnPark)
            AddToFile(f, sqn.id, "SetOnPark", "1");
        AddToFile(f, sqn.id, "Scramble", "1");
        AddToFile(f, sqn.id, "Skill", fSkill);
        int aging = rnd.Next(1, 99);// (gameDateTime - StartDateTime).Days;
        AddToFile(f, sqn.id, "Aging", aging.ToString());

        Point3d to3d = aps[1].Pos();
        if (!PlayerArmy.Equals(army) && PlayerAC != null)
            if (PlayerAC.IsAirborne())
                to3d = calcPos(PlayerAC.Pos(), 16093, acHeading(PlayerAC));
            else to3d = MissionMarkers[rnd.Next(MissionMarkers.Count)].Pos3d();

        Point2d to2d = new Point2d(to3d.x, to3d.y);
        double heading = calcDir(pt2d, to2d);

        int alt = rnd.Next(500, 3500);
        int speed = rnd.Next(300, 400);
        bool airstart = ap == null;
        if (sqn.actype.StartsWith("Aircraft.Sunderland"))
            airstart = true;
        if (sqn.actype.StartsWith("Aircraft.He-115"))
            airstart = true;

        if (airstart)
        {   // airstart if map entry point
            pt2d = calcPos(pt2d, rnd.Next(2000, 3000), heading);
            AddToFile(f, sqn.id + "_Way", "NORMFLY", pt2d.x.ToString() + " " + pt2d.y.ToString() + " " + alt.ToString() + " " + speed.ToString());
        }
        else
        {
            AddToFile(f, sqn.id + "_Way", "TAKEOFF", pt2d.x.ToString("F2") + " " + pt2d.y.ToString("F2") + " 0 0");
            double dir = calcDir(pt2d, to2d);
            Point2d wp2d = calcPos(pt2d, 16090 / 2, dir);
            AddToFile(f, sqn.id + "_Way", "NORMFLY", wp2d.x.ToString("F2") + " " + wp2d.y.ToString("F2") + " 1680.00" + " 280.00");
        };
        AddToFile(f, sqn.id + "_Way", "NORMFLY", to2d.x.ToString("F2") + " " + to2d.y.ToString("F2") + " " + alt.ToString() + ".00 " + speed.ToString() + ".00");
        if (isEnemy)
        {
            foreach (MissionMarker mm in MissionMarkers.FindAll(m => m.army.Equals(army)))
                AddToFile(f, sqn.id + "_Way", "NORMFLY", mm.x.ToString("F2") + " " + mm.y.ToString("F2") + " " + alt.ToString() + ".00 " + speed.ToString() + ".00");
        };


        if (airstart)
            AddToFile(f, sqn.id + "_Way", "NORMFLY", pt3d.x.ToString("F2") + " " + pt3d.y.ToString("F2") + " " + "0.00 " + speed.ToString() + ".00");
        else AddToFile(f, sqn.id + "_Way", "LANDING", pt3d.x.ToString("F2") + " " + pt3d.y.ToString("F2") + " " + "0.00 " + speed.ToString() + ".00");
        if (odenDebug) GamePlay.gpLogServer(null, "CreateFlight Done {0}", new object[] { sqn.actype });
        //return sqn;
    }

    private void RedirectAirGroups(int army, Point3d pt3d)
    {
        AiAirGroup[] ags = GamePlay.gpAirGroups(army);
        if (ags != null)
            foreach (AiAirGroup ag in ags)
                GiveNewWP((AiGroup)ag, pt3d);
    }
    private void GiveNewWP(AiGroup grp, Point3d wp3d)
    {
        if (grp == null)
            return;
        if (grp.GetItems().Length < 1)
            return;
        AiActor grpLeader = GetFlightLeader((AiAirGroup)grp);

        Point3d refPos3d = grpLeader.Pos();
        AiAircraft ac = (AiAircraft)grpLeader;
        if (30 > ac.GetCurrentFuelQuantityInPercent())
            return;

        int alt = (int)Math.Max(300, wp3d.z - rnd.Next(900) + rnd.Next(600));
        AiAirGroup ag = ac.AirGroup();

        AiWayPoint[] arrWP = grp.GetWay();
        int currWPidx = grp.GetCurrentWayPoint();
        AiAirWayPoint currAWP = (AiAirWayPoint)arrWP[currWPidx];
        if (currAWP.Action == AiAirWayPointType.TAKEOFF)
            return;

        AiAirWayPoint landAWP = (AiAirWayPoint)arrWP[arrWP.Length - 1];

        //Point2d wp2d = new Point2d(wp3d.x, wp3d.y);
        AiAirWayPoint nextAWP = new AiAirWayPoint(ref wp3d, currAWP.Speed);
        nextAWP.Action = currAWP.Action;

        arrWP = new AiWayPoint[] { currAWP, nextAWP, landAWP };
        grp.SetWay(arrWP);
        if (odenDebug) GamePlay.gpLogServer(null, "Redirected {0} {1}", new object[] { grp.Name(), alt });
    }
    private void sayDirection(AiAircraft ac, AiAirGroup grp, Point3d pt3d)
    {
        if (ac == null)
            return;
        if (grp == null)
            return;
        AiActor refActor = GetFlightLeader(grp);
        if (refActor == null)
            return;
        int dir = (int)(calcDir(new Point2d(refActor.Pos().x, refActor.Pos().y), new Point2d(pt3d.x, pt3d.y)));
        string strDir = dir.ToString();
        for (int i = 0; i < strDir.Length; i++)
            ac.SayToGroup(grp, strDir[i].ToString());
    }
    private void sayDistance(AiAircraft ac, AiAirGroup grp, Point3d pt3d)
    {
        if (ac == null)
            return;
        if (grp == null)
            return;
        AiActor refActor = GetFlightLeader(grp);
        if (refActor == null)
            return;
        Point3d refPos3d = refActor.Pos();
        int dist = (int)(pt3d.distance(ref refPos3d) / 1000);
        if (grp.Army() == RAF)
        {
            int miles = (int)(dist * .62f);
            if (miles > 5)
            {
                ac.SayToGroup(grp, "Distance"); // "Range"
                if (miles < 16) ac.SayToGroup(grp, miles.ToString() + "_miles");
                else
                {
                    int tens = 10 * (miles / 10); // and hundreds
                    sayNumber(ac, grp, tens.ToString());
                    sayNumber(ac, grp, (miles % 10).ToString() + "_miles");
                };
            };
        }
        else
        {
            if (dist > 34)
                return;
            int km = dist;
            if (km > 15) km = km - (km % 5);
            if (km > 2)
            {
                ac.SayToGroup(grp, "Distance"); // "Range"
                ac.SayToGroup(grp, km.ToString() + "_kilometers");
            };
        };
    }
    private void sayAltitude(AiAircraft ac, AiAirGroup grp, double m)
    {
        if (ac == null)
            return;
        if (grp == null)
            return;
        if (grp.Army() == RAF)
        {
            int angels = (int)((m + 164) / 328);
            if (angels > 0)
                ac.SayToGroup(grp, angels.ToString() + "0hi");
        }
        else
        {
            int km500 = (int)((m + 250) / 100);
            km500 = km500 - (km500 % 5);
            if (km500 > 0)
            {
                ac.SayToGroup(grp, "Height");
                ac.SayToGroup(grp, km500.ToString() + "h");
            }
            else
            {
                int m100 = (int)(m + 50 / 100);
                if (m100 > 0)
                {
                    ac.SayToGroup(grp, "Height");
                    ac.SayToGroup(grp, m100.ToString() + "h");
                }
            };
            //if (odenDebug) GamePlay.gpLogServer(null, "sayAltitude: {0} >> {1}", new object[] { m, km500.ToString() });
        };
    }
    private void sayGrid(AiAircraft ac, AiAirGroup grp, string targetSector)
    {
        if (ac == null)
            return;
        if (grp == null)
            return;
        ac.SayToGroup(grp, "In_square");
        string[] arrEWNS = { "vb_East_of", "vb_West_of" };
        for (int i = 0; i < targetSector.Length; i++)
        {
            switch (targetSector[i])
            {
                case ',':
                    arrEWNS[0] = "vb_North_of";
                    arrEWNS[1] = "vb_South_of";
                    break;
                case '>':
                    ac.SayToGroup(grp, arrEWNS[0]);
                    break;
                case '<':
                    ac.SayToGroup(grp, arrEWNS[1]);
                    break;
                default:
                    ac.SayToGroup(grp, targetSector[i].ToString());
                    break;
            };
        };
    }
    private void sayNumber(AiAircraft ac, AiAirGroup ag, string mixedString)
    {
        if (ac == null)
            return;
        if (ag == null)
            return;
        //char[] numerals = new char[]{'0','1','2','3','4','5','6','7','8','9'};
        List<string> nums = new List<string>();
        string num = "0123456789";

        //foreach (char ch in ac.Regiment().name()) //BoB_RAF_F_141Sqn_Early
        for (int i = 0; i < mixedString.Length; i++)
            if (num.Contains(mixedString[i].ToString()))
                nums.Add(mixedString[i].ToString());

        num = ""; // hundreds
        if (nums.Count == 3)
        {
            num = nums[0] + "h";
            nums.RemoveAt(0);
        };
        if (nums.Count == 2)
            switch (nums[0])
            {
                case "0":
                    nums.RemoveAt(0);
                    break;
                case "1":
                    nums[0] = nums[0] + nums[1];
                    nums.RemoveAt(1);
                    break;
                case "2":
                    if (nums[1].IndexOfAny(new char[] { '4', '5', '6', '7', '8' }) > -1)
                    {
                        nums[0] = nums[0] + nums[1];
                        nums.RemoveAt(1);
                    };
                    break;
                case "3":
                    nums[0] = nums[0] + nums[1];
                    nums.RemoveAt(1);
                    break;
                case "4": nums[0] = "n40"; break;
                case "5": nums[0] = "50s"; break;
                case "6": nums[0] = "60s"; break;
                case "7": nums[0] = "70s"; break;
                case "8": nums[0] = "80s"; break;
                case "9": nums[0] = "90s"; break;
            };

        if (num.Length > 0) ac.SayToGroup(ag, num); // hundreds
        foreach (string str in nums)
            ac.SayToGroup(ag, str);
    }

}
