using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;
using System.Diagnostics;

namespace Shared {
  [DataContract]
  [AiAttribute ("Custom", "1.00")]
  public class AI_Custom : AI_Base {
    [DataMember]
    private int _mainCapitalAreaIndex;
    [DataMember]
    private int _regionToConquerIndex;
    [DataMember]
    private int _nbYearsLeftAttackingRegion;
    [DataMember]
    private int _nbArmyMaxNeutralToAttack;
    [DataMember]
    private bool _canAttackComputers;
    [DataMember]
    private int _nbYearsBeforeCanAttackComputers;
    [DataMember]
    private bool [] _majorsEnemyHasAttackedCapital;

    public AI_Custom (int majorIndex)
      : base (majorIndex) {
    }

    public override void Create () {

      //if (!_isInitialized) {
      // init main capital
      _mainCapitalAreaIndex = -1;
      foreach (Area area in ShuffleList<Area> (Major.Capitals)) {
        if (!area.IsCapitalColonized) {
          if (area.IsOuter) {
            _mainCapitalAreaIndex = area.Index;
            break;
          }
        }
      }
      if (_mainCapitalAreaIndex == -1) {
        foreach (Area area in ShuffleList<Area> (Major.Capitals)) {
          if (area.IsOuter) {
            _mainCapitalAreaIndex = area.Index;
            break;
          }
        }
        if (_mainCapitalAreaIndex == -1) {
          foreach (Area area in ShuffleList<Area> (Major.Capitals)) {
            _mainCapitalAreaIndex = area.Index;
            break;
          }
        }
      }

      _nbYearsLeftAttackingRegion = Random.Next ("", 1, 4);
      _regionToConquerIndex = Major.GetRandomRegion (Random).Index;
      _nbArmyMaxNeutralToAttack = Major.NbArmyMaxNeutralToAttack;
      _canAttackComputers = false;
      _nbYearsBeforeCanAttackComputers = 1 + Random.Next ("", 1, 3);
      _majorsEnemyHasAttackedCapital = new bool [Board.Majors.Count];
      for (int i = 0; i < _majorsEnemyHasAttackedCapital.Length; i++) {
        _majorsEnemyHasAttackedCapital [i] = false;
      }
    }

    public override void Diplomacy () {
      //foreach (EconomicAid economicAid in Board.GetEconomicAids (Major)) {
      foreach (EconomicAid economicAid in Board.EconomicAids) {
        if (economicAid.Type == EconomicAid.EconomicAidType.TreatyAsk) {
          if (economicAid.TargetMajorIndex == Major.Index) {
            bool reply = false;
            if (economicAid.Loan >= Random.Next ("", 3, 80)) {
              reply = true;
            }
            MajorOrderEconomicAidReply (economicAid, reply);
          }
        }
      }
    }

    public override void Think () {
      if (Board.IsBuildSeason) {
        AI_PhaseComputerBuild ();
      }
      AI_PhaseComputerMoves ();
    }

    private void AI_PhaseComputerBuild () {
      Area buildArea = Board.Areas [_mainCapitalAreaIndex];
      if (buildArea.IsCapitalColonized) {
        buildArea = null;
        foreach (Area area in Major.Capitals) {
          if (!area.IsCapitalColonized) {
            buildArea = area;
            break;
          }
        }
      }

      // ST: give 2.000.000$ per level
      // XL: give 1.000.000$ per level

      if (_nbYearsLeftAttackingRegion == 0) {
        _nbYearsLeftAttackingRegion = Random.Next ("", 1, 4);
        _regionToConquerIndex = Major.GetRandomRegion (Random).Index;
      }
      else {
        // XL only
        // can't change the value of other majors
        //foreach (Major major in Board.Majors) {
        //  major.NbArmyMaxNeutralToAttack += 5;
        //}
        _nbYearsLeftAttackingRegion--;
      }

      _nbArmyMaxNeutralToAttack += 5;

      // ST only
      if (Board.Year > 1900) {
        _canAttackComputers = true;
      }

      if (!_canAttackComputers) {
        _nbYearsBeforeCanAttackComputers--;
        if (_nbYearsBeforeCanAttackComputers == 0) {
          _canAttackComputers = true;
        }
      }

      for (; ; ) {
        // fortification: at least $10.000.000 left
        if (Major.Treasury >= 100) {
          if (Random.Next ("", 0, 1) == 0) {
            // bug XL: pay even if no more area to fortify
            List<Area> areas = new List<Area> ();
            foreach (Area area in Board.Areas) {
              if (area.Owner != Major) {
                continue;
              }
              if (area.IsFortified) {
                continue;
              }
              areas.Add (area);
            }
            if (areas.Count > 0) {
              // todo: $0.500.000 instead of $1.000.000
              MajorOrderBuildFortification (areas [Random.Next ("", 0, areas.Count - 1)]);
            }
            else {
              if (buildArea == null) {
                return;
              }
            }
          }
        }
        else {
          if (buildArea == null) {
            return;
          }
        }

        if (buildArea == null) {
          continue;
        }
        //for (int i = 0; i < Major.ArmyNavyDistribution; i++) {
        //  if (Major.Treasury < Major.ArmyCost) {
        //    return;
        //  }
        //  // todo: 150.000 men instead of 100.000 men
        //  Major.OrderBuildArmy (buildArea.Index, 1);
        //}
        int nbArmy = Major.ArmyNavyDistribution;
        if (Major.Treasury < (nbArmy * Major.ArmyCost)) {
          nbArmy = ((int) Major.Treasury) / Major.ArmyCost;
        }
        if (nbArmy == 0) {
          return;
        }
        MajorOrderBuildArmy (buildArea, nbArmy);

        if (buildArea.IsOuter) {
          if (Major.Treasury < Major.NavyCost) {
            return;
          }
          // todo XL: 15 (!) boats instead of 5 boats
          // todo ST: 8 boats instead of 5 boats
          MajorOrderBuildNavy (buildArea, 1);
        }
      }
    }

    private void AI_PhaseComputerMoves () {
      // ST only
      // AI DEFEND CAPITAL
      Area mainCapital = Board.Areas [_mainCapitalAreaIndex];
      Major capitalOwner = mainCapital.Owner;
      if (capitalOwner != Major) {
        _majorsEnemyHasAttackedCapital [capitalOwner.Index] = true;
        DefendMainCapital ();
      }

      // COMPUTE MIN ARMY/NAVY TO LEFT
      // ST: 10 per level
      // XL: 25 per level
      int armyToLeftFunctionOfLevel = ((int) Major.Level) * 10;
      // XL
      //int armyMinToLeftInitial = Major.BaseArmyToLeft + armyToLeftFunctionOfLevel + Random.Next ("", 0, 39);
      int armyMinToLeftInitial = Major.BaseArmyToLeft + armyToLeftFunctionOfLevel + Random.Next ("", 0, 29);
      int navyMinToLeft = 5 + ((int) Major.Level) * 5 + Random.Next ("", 0, 19);

      // ST only
      // bug for areAllOthersMajorsNeutral
      // AI_AllOutAttack

      // RANDOMLY ATTACK MAJOR
      if (Random.Next ("", 0, 31) == 0) {
        // XL bug: can attack itself, do not uses _canAttackComputers
        // ST bug: always attack itself
        Major majorToAttack = null;
        for (; ; ) {
          majorToAttack = Board.Majors [Random.Next ("", 0, Board.Majors.Count - 1)];
          if (majorToAttack.Type == Major.TypeEnum.Neutral) {
            continue;
          }
          if (majorToAttack != Major) {
            break;
          }
        }

        if ((majorToAttack.Type == Major.TypeEnum.Human) ||
            ((majorToAttack.Type == Major.TypeEnum.Computer) && (_canAttackComputers))) {
          AI_PhaseComputerWar (majorToAttack, armyMinToLeftInitial, armyToLeftFunctionOfLevel, navyMinToLeft);
        }
      }

      // WARTABLE
      foreach (Major major in ShuffleList<Major> (Board.Majors)) {
        if (Major == major) {
          continue;
        }
        if (major.IsNeutral) {
          continue;
        }
        if (major.Type == Major.TypeEnum.Computer) {
          if (!_canAttackComputers) {
            if (!_majorsEnemyHasAttackedCapital [major.Index]) {
              // make peace <->
            }
          }
        }

        bool isAttacked = (major.GetNbRoundsNotAttacking (Major) != int.MinValue);
        if ((isAttacked) &&
            (Random.Next ("", 0, 7) != 0)) {
          // declare war ->
          AI_PhaseComputerWar (major, armyMinToLeftInitial, armyToLeftFunctionOfLevel, navyMinToLeft);
        }
        else {
          if (!_majorsEnemyHasAttackedCapital [major.Index]) {
            // make peace <->
          }
        }
      }

      // WINNING SCORE
      foreach (Major major in ShuffleList<Major> (Board.Majors)) {
        if (major == Major) {
          continue;
        }
        if (major.IsNeutral) {
          continue;
        }
        if (major.Score < 1000) {
          if (Board.WinningScore == null) {
            continue;
          }
          //int temp = ((int) Board.WinningScore) - 200;
          //temp += 35 + Random.Next ("", 0, 37);
          int temp = ((int) Board.WinningScore) - 250;
          temp += Random.Next ("", 30, 100);
          if (major.Score < temp) {
            continue;
          }
        }
        if (Random.Next ("", 0, 15) == 0) {
          continue;
        }
        if ((major.Type == Major.TypeEnum.Human) ||
            ((major.Type == Major.TypeEnum.Computer) && (_canAttackComputers))) {
          AI_PhaseComputerWar (major, armyMinToLeftInitial, armyToLeftFunctionOfLevel, navyMinToLeft);
        }
      }

      // NEUTRALS & FRIENDLY
      bool neutralAreaFound = false;
      foreach (Area areaSrc in ShuffleList<Area> (Board.Areas)) {
        if (neutralAreaFound) {
          break;
        }
        if (areaSrc.Owner != Major) {
          continue;
        }

        int armyMinToLeft = armyMinToLeftInitial;
        if (areaSrc.Index == _mainCapitalAreaIndex) {
          armyMinToLeft += armyToLeftFunctionOfLevel;
        }

        // not enough army to attack
        if (areaSrc.Army <= armyMinToLeft) {
          // AI_MoveEmptyNavyToSameRegion
          AiMoveEmptyNavyToStrongest (areaSrc, navyMinToLeft);
          continue;
        }

        // enough army to attack
        // try to attack by army the conquer region first
        bool goNextArea = false;
        // recherche de l'area neutre la + faible accessible
        // par terre dans la region de conquete courante
        // et contigue à l'area src
        if ((Major.ArmyMoves.Count < 20) &&
            (!areaSrc.IsIsland)) {
          // 2B0AA
          int armyMinfound = -1;
          Area areaDst = null;
          foreach (Area area in areaSrc.AdjacentAreas) {
            if (area.Region.Index != _regionToConquerIndex) {
              continue;
            }
            if (!area.IsNeutral) {
              continue;
            }
            // cheat: knows the neutral army!
            if (areaDst == null) {
              armyMinfound = (int) area.Army;
              areaDst = area;
            }
            else {
              if (area.Army < armyMinfound) {
                armyMinfound = (int) area.Army;
                areaDst = area;
              }
            }
          }
          if (areaDst != null) {
            MoveArmy (areaSrc, areaDst, ((int) areaSrc.Army) - armyMinToLeft, false);
            AiMoveEmptyNavyToStrongest (areaSrc, navyMinToLeft);
            goNextArea = true;
          }
        }
        // il attaque la region a conquerir par navy meme si ami
        // alors qu'il ne le fait pas par army, donc si inner, il n'attaque jamais sans etre en guerre

        // after max attack by army the conquer region
        // do navy attack
        if (goNextArea) {
          continue;
        }
        for (; ; ) {
          if (goNextArea) {
            break;
          }

          if (Major.NavyMoves.Count >= 10) {
            break;
          }

          // not enough navy
          if (areaSrc.Navy <= navyMinToLeft) {
            AiMoveArmyToRandomFriendlyAdjacent (areaSrc, armyMinToLeft, navyMinToLeft);
            goNextArea = true;
          }
          else {
            neutralAreaFound = false;
            foreach (Area area in Board.Regions [_regionToConquerIndex].Areas) {
              if (area.IsNeutral) {
                neutralAreaFound = true;
                break;
              }
            }

            // no more neutral in the conquer region
            Area areaDst = null;
            if (!neutralAreaFound) {
              List<Area> areas = new List<Area> ();
              foreach (Area area in Board.Areas) {
                // bug ST
                if (area == areaSrc) {
                  continue;
                }
                if (area.IsOuter) {
                  if (area.Region.Index == _regionToConquerIndex) {
                    // ST only
                    if (area.Index != _mainCapitalAreaIndex) {
                      areas.Add (area);
                    }
                  }
                }
              }
              if (areas.Count != 0) {
                areaDst = areas [Random.Next ("", 0, areas.Count - 1)];
              }
            }

            else {
              List<Area> areas = new List<Area> ();
              foreach (Area area in Board.Areas) {
                if (area.IsNeutral) {
                  if (area.IsOuter) {
                    // cheat: knows the area army
                    if (area.Army <= _nbArmyMaxNeutralToAttack) {
                      areas.Add (area);
                    }
                  }
                }
              }
              if (areas.Count != 0) {
                areaDst = areas [Random.Next ("", 0, areas.Count - 1)];
              }
              else {
                areas.Clear ();
                foreach (Area area in Board.Areas) {
                  if (area.IsOuter) {
                    if (area.Owner == Major) {
                      if (area != areaSrc) {
                        if (area.Index != _mainCapitalAreaIndex) {
                          areas.Add (area);
                        }
                      }
                    }
                  }
                }
                if (areas.Count != 0) {
                  areaDst = areas [Random.Next ("", 0, areas.Count - 1)];
                }
                else {
                  // choose another region to conquer
                  for (; ; ) {
                    int regionIndex = Major.GetRandomRegion (Random).Index;
                    if (regionIndex != _regionToConquerIndex) {
                      _regionToConquerIndex = regionIndex;
                      break;
                    }
                  }
                  AiMoveArmyToRandomFriendlyAdjacent (areaSrc, armyMinToLeft, navyMinToLeft);
                  goNextArea = true;
                }
              }
            }

            int hundredsNotUsed = 0;
            if (!goNextArea) {

              if (areaDst == null) {
                throw new Exception ();
              }

              if (areaSrc.Army <= areaSrc.Navy) {
                int hundredsInArea = ((int) areaSrc.Army) / 100;
                int hundredsToLeft = armyMinToLeft / 100;
                int hundredsUsed = Major.HundredsToSend + hundredsToLeft;
                int nbToMove;
                if (hundredsInArea > hundredsUsed) {
                  hundredsNotUsed = hundredsInArea - hundredsUsed;
                  nbToMove = (((int) areaSrc.Army) % 100) + (hundredsUsed * 100) - armyMinToLeft;
                }
                else {
                  nbToMove = ((int) areaSrc.Army) - armyMinToLeft;
                }
                MoveNavy (areaSrc, areaDst, nbToMove, nbToMove, false);
              }
              else { // if (areaSrc.Army > areaSrc.Navy) {
                if (areaSrc.Army > armyMinToLeft) {
                  int hundredsInArea = ((int) areaSrc.Navy) / 100;
                  //int hundredsUsed = Major.HundredsToSend - navyMinToLeft;
                  int hundredsUsed = Major.HundredsToSend;
                  int nbToMove;
                  if (hundredsInArea > hundredsUsed) {
                    hundredsNotUsed = hundredsInArea - hundredsUsed;
                    // bug ST? pas de % mais un / car pas de swap
                    //nbToMove = hundredsInArea + (hundredsInArea * 100) - navyMinToLeft;
                    nbToMove = (((int) areaSrc.Navy) % 100) + (hundredsUsed * 100) - navyMinToLeft;
                  }
                  else {
                    nbToMove = ((int) areaSrc.Navy) - navyMinToLeft;
                  }
                  MoveNavy (areaSrc, areaDst, nbToMove, nbToMove, false);
                }
                else {
                  goNextArea = true;
                }
              }
            }

            if (!goNextArea) {
              // handle distance
            }

            if (hundredsNotUsed == 0) {
              goNextArea = true;
            }
          }

        } // end while navyMove

      } // end foreach area

    }


    private void AiMoveEmptyNavyToStrongest (Area areaSrc, int navyMinToLeft) {
      // Move the empty navy to the strongest frienfly area in the same region
      if (areaSrc.Navy <= navyMinToLeft) {
        return;
      }
      if (Major.NavyMoves.Count >= 10) {
        return;
      }
      int armyMax = 0;
      Area areaDst = null;
      foreach (Area area in Board.Areas) {
        if (area.Owner != Major) {
          continue;
        }
        if (area == areaSrc) {
          continue;
        }
        if (area.Army < armyMax) {
          continue;
        }
        // XL not in the loop
        if (area.IsInner) {
          continue;
        }
        // ST only
        if (area.Index == _mainCapitalAreaIndex) {
          continue;
        }
        // new: take into account the capital
        //int max = armyMax;
        //if (area.Index == _mainCapitalAreaIndex) {
        //  max -= armyMinToLeftAddedInCapital;
        //}
        armyMax = (int) area.Army;
        areaDst = area;
      }
      if (areaDst != null) {
        MoveNavy (areaSrc, areaDst, 0, ((int) areaSrc.Navy) - navyMinToLeft, false);
      }
    }

    private void AiMoveArmyToRandomFriendlyAdjacent (Area areaSrc, int armyMinToLeft, int navyMinToLeft) {
      if (areaSrc.Army <= armyMinToLeft) {
        goto Skip;
      }
      if (areaSrc.IsIsland) {
        goto Skip;
      }
      if (Major.ArmyMoves.Count >= 20) {
        goto Skip;
      }
      List<Area> areas = new List<Area> ();
      foreach (Area area in areaSrc.AdjacentAreas) {
        if (area.Owner != Major) {
          continue;
        }
        // ST only
        if (area.Index == _mainCapitalAreaIndex) {
          continue;
        }
        areas.Add (area);
      }
      if (areas.Count != 0) {
        Area areaDst = areas [Random.Next ("", 0, areas.Count - 1)];
        MoveArmy (areaSrc, areaDst, ((int) areaSrc.Army) - armyMinToLeft, false);
      }

Skip:
      AiMoveEmptyNavyToStrongest (areaSrc, navyMinToLeft);
    }

    private void DefendMainCapital () {
      int minArmy = 1;
      int minNavy = 0;

      Area areaDst = Board.Areas [_mainCapitalAreaIndex];
      foreach (Area areaSrc in areaDst.AdjacentAreas) {
        if (Major.ArmyMoves.Count >= 20) {
          break;
        }
        if (areaSrc.Owner == Major) {
          if (areaSrc.Army > minArmy) {
            int nbToMove = ((int) areaSrc.Army) - minArmy;
            MoveArmy (areaSrc, areaDst, nbToMove, false);
          }
        }
      }
      foreach (Area areaSrc in Board.Areas) {
        if (Major.NavyMoves.Count >= 10) {
          break;
        }
        if (areaSrc.Owner == Major) {
          if (areaSrc.Army > minArmy) {
            if (areaSrc.Navy > minNavy) {
              if (areaSrc.IsOuter) {
                int nbToMove = Math.Min (((int) areaSrc.Army) - minArmy, ((int) areaSrc.Navy) - minNavy);
                // Atari ST cheat: distance = 0
                MoveNavy (areaSrc, areaDst, nbToMove, nbToMove, false);
              }
            }
          }
        }
      }
    }

    private void AI_PhaseComputerWar (Major majorToAttack, int armyMinToLeftInitial, int armyToLeftFunctionOfLevel, int navyMinToLeft) {
      // attack enemy area by army
      foreach (Area areaSrc in ShuffleList<Area> (Board.Areas)) {
        if (Major.ArmyMoves.Count >= 20) {
          break;
        }
        int armyMinToLeft = armyMinToLeftInitial;
        if (areaSrc.Index == _mainCapitalAreaIndex) {
          armyMinToLeft += armyToLeftFunctionOfLevel;
        }
        if (areaSrc.Owner == Major) {
          if (Random.Next ("", 0, 3) != 0) {
            if (areaSrc.Army > armyMinToLeft) {
              if (!areaSrc.IsIsland) {
                List<Area> areas = new List<Area> ();
                foreach (Area adjacentArea in areaSrc.AdjacentAreas) {
                  if (adjacentArea.Owner == majorToAttack) {
                    if (adjacentArea.Index != _mainCapitalAreaIndex) {
                      areas.Add (adjacentArea);
                    }
                  }
                }
                if (areas.Count != 0) {
                  Area areaDst = areas [Random.Next ("", 0, areas.Count - 1)];
                  MoveArmy (areaSrc, areaDst, ((int) areaSrc.Army) - armyMinToLeft, false);
                }
              }
            }
          }
        }
      }

      // navy
      // cheater: distance = 0 !
      int nbNavyMoves = 0;
      foreach (Area areaSrc in ShuffleList<Area> (Board.Areas)) {
        if (Major.NavyMoves.Count >= 10) {
          break;
        }
        if (nbNavyMoves >= 5) {
          break;
        }
        if (areaSrc.Owner == Major) {
          if (Random.Next ("", 0, 7) != 0) {
            if (areaSrc.Navy > navyMinToLeft) {
              int armyMinToLeft = armyMinToLeftInitial;
              if (areaSrc.Index == _mainCapitalAreaIndex) {
                armyMinToLeft += armyToLeftFunctionOfLevel;
              }
              // Strike enemy navy
              if (areaSrc.Army <= armyMinToLeft) {
                List<Area> areas = new List<Area> ();
                foreach (Area areaToAttack in Board.Areas) {
                  if (areaToAttack.Owner == majorToAttack) {
                    if (areaToAttack.Navy != 0) {
                      if (areaToAttack.IsOuter) {
                        if (areaToAttack.Index != _mainCapitalAreaIndex) {
                          areas.Add (areaToAttack);
                        }
                      }
                    }
                  }
                }
                if (areas.Count != 0) {
                  Area areaDst = areas [Random.Next ("", 0, areas.Count - 1)];
                  MoveNavy (areaSrc, areaDst, 0, ((int) areaSrc.Navy) - navyMinToLeft, false);
                  nbNavyMoves++;
                }
              }
              // if (areaSrc.Army > armyMinToLeft) {
              else {
                // defend capitals
                Area areaDst = null;
                foreach (Area capital in ShuffleList<Area> (Major.Capitals)) {
                  if (capital.Owner != Major) {
                    if (capital.IsOuter) {
                      areaDst = capital;
                      break;
                    }
                  }
                }
                // attack enemy area by navy
                if (areaDst == null) {
                  List<Area> areas = new List<Area> ();
                  foreach (Area areaToAttack in Board.Areas) {
                    if (areaToAttack.Owner == majorToAttack) {
                      if (areaToAttack.IsOuter) {
                        if (areaToAttack.Index != _mainCapitalAreaIndex) {
                          areas.Add (areaToAttack);
                        }
                      }
                    }
                  }
                  if (areas.Count != 0) {
                    areaDst = areas [Random.Next ("", 0, areas.Count - 1)];
                  }
                }
                if (areaDst != null) {
                  int nbToMove = Math.Min (((int) areaSrc.Army) - armyMinToLeft, ((int) areaSrc.Navy) - navyMinToLeft);
                  MoveNavy (areaSrc, areaDst, nbToMove, nbToMove, false);
                  nbNavyMoves++;
                }
              }
            }
          }
        }
      }
    }

    public override string Dump () {
      StringBuilder sb = new StringBuilder ();
      sb.Append (base.Dump ());
      sb.AppendLine ("_mainCapitalAreaIndex: " + _mainCapitalAreaIndex);
      sb.AppendLine ("_regionToConquerIndex: " + _regionToConquerIndex);
      sb.AppendLine ("_nbYearsLeftAttackingRegion: " + _nbYearsLeftAttackingRegion);
      sb.AppendLine ("_nbArmyMaxNeutralToAttack: " + _nbArmyMaxNeutralToAttack);
      return (sb.ToString ());
    }

  }
}
