package games.strategy.triplea.delegate;

import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;

import games.strategy.engine.data.GameData;
import games.strategy.engine.data.TerritoryEffect;
import games.strategy.engine.data.Unit;
import games.strategy.engine.data.UnitType;
import games.strategy.triplea.Properties;
import games.strategy.triplea.attachments.UnitAttachment;
import games.strategy.util.IntegerMap;

public class UnitBattleComparator implements Comparator<Unit> {
  private final boolean m_defending;
  private final IntegerMap<UnitType> m_costs;
  private final GameData m_data;
  private final boolean m_bonus;
  private final boolean m_ignorePrimaryPower;
  private final Collection<TerritoryEffect> m_territoryEffects;
  private final Collection<UnitType> m_multiHitpointCanRepair = new HashSet<>();

  public UnitBattleComparator(final boolean defending, final IntegerMap<UnitType> costs,
      final Collection<TerritoryEffect> territoryEffects, final GameData data, final boolean bonus,
      final boolean ignorePrimaryPower) {
    m_defending = defending;
    m_costs = costs;
    m_data = data;
    m_bonus = bonus;
    m_ignorePrimaryPower = ignorePrimaryPower;
    m_territoryEffects = territoryEffects;
    if (Properties.getBattleshipsRepairAtEndOfRound(data)
        || Properties.getBattleshipsRepairAtBeginningOfRound(data)) {
      for (final UnitType ut : data.getUnitTypeList()) {
        if (Matches.unitTypeHasMoreThanOneHitPointTotal().match(ut)) {
          m_multiHitpointCanRepair.add(ut);
        }
      }
      // TODO: check if there are units in the game that can repair this unit
    }
  }

  @Override
  public int compare(final Unit u1, final Unit u2) {
    if (u1.equals(u2)) {
      return 0;
    }
    final boolean transporting1 = Matches.transportIsTransporting().match(u1);
    final boolean transporting2 = Matches.transportIsTransporting().match(u2);
    final UnitAttachment ua1 = UnitAttachment.get(u1.getType());
    final UnitAttachment ua2 = UnitAttachment.get(u2.getType());
    if (ua1 == ua2 && u1.getOwner().equals(u2.getOwner())) {
      if (transporting1 && !transporting2) {
        return 1;
      }
      if (!transporting1 && transporting2) {
        return -1;
      }
      return 0;
    }
    final boolean airOrCarrierOrTransport1 = Matches.unitIsAir().match(u1) || Matches.unitIsCarrier().match(u1)
        || (!transporting1 && Matches.unitIsTransport().match(u1));
    final boolean airOrCarrierOrTransport2 = Matches.unitIsAir().match(u2) || Matches.unitIsCarrier().match(u2)
        || (!transporting2 && Matches.unitIsTransport().match(u2));
    final boolean subDestroyer1 = Matches.unitIsSub().match(u1) || Matches.unitIsDestroyer().match(u1);
    final boolean subDestroyer2 = Matches.unitIsSub().match(u2) || Matches.unitIsDestroyer().match(u2);
    final boolean multiHpCanRepair1 = m_multiHitpointCanRepair.contains(u1.getType());
    final boolean multiHpCanRepair2 = m_multiHitpointCanRepair.contains(u2.getType());
    if (!m_ignorePrimaryPower) {
      int power1 = 8 * BattleCalculator.getUnitPowerForSorting(u1, m_defending, m_data, m_territoryEffects);
      int power2 = 8 * BattleCalculator.getUnitPowerForSorting(u2, m_defending, m_data, m_territoryEffects);
      if (m_bonus) {
        if (subDestroyer1 && !subDestroyer2) {
          power1 += 4;
        } else if (!subDestroyer1 && subDestroyer2) {
          power2 += 4;
        }
        if (multiHpCanRepair1 && !multiHpCanRepair2) {
          power1++;
        } else if (!multiHpCanRepair1 && multiHpCanRepair2) {
          power2++;
        }
        if (transporting1 && !transporting2) {
          power1++;
        } else if (!transporting1 && transporting2) {
          power2++;
        }
        if (airOrCarrierOrTransport1 && !airOrCarrierOrTransport2) {
          power1++;
        } else if (!airOrCarrierOrTransport1 && airOrCarrierOrTransport2) {
          power2++;
        }
      }
      if (power1 != power2) {
        return power1 - power2;
      }
    }
    {
      final int cost1 = m_costs.getInt(u1.getType());
      final int cost2 = m_costs.getInt(u2.getType());
      if (cost1 != cost2) {
        return cost1 - cost2;
      }
    }
    {
      int power1reverse = 8 * BattleCalculator.getUnitPowerForSorting(u1, !m_defending, m_data, m_territoryEffects);
      int power2reverse = 8 * BattleCalculator.getUnitPowerForSorting(u2, !m_defending, m_data, m_territoryEffects);
      if (m_bonus) {
        if (subDestroyer1 && !subDestroyer2) {
          power1reverse += 4;
        } else if (!subDestroyer1 && subDestroyer2) {
          power2reverse += 4;
        }
        if (multiHpCanRepair1 && !multiHpCanRepair2) {
          power1reverse++;
        } else if (!multiHpCanRepair1 && multiHpCanRepair2) {
          power2reverse++;
        }
        if (transporting1 && !transporting2) {
          power1reverse++;
        } else if (!transporting1 && transporting2) {
          power2reverse++;
        }
        if (airOrCarrierOrTransport1 && !airOrCarrierOrTransport2) {
          power1reverse++;
        } else if (!airOrCarrierOrTransport1 && airOrCarrierOrTransport2) {
          power2reverse++;
        }
      }
      if (power1reverse != power2reverse) {
        return power1reverse - power2reverse;
      }
    }
    if (subDestroyer1 && !subDestroyer2) {
      return 1;
    } else if (!subDestroyer1 && subDestroyer2) {
      return -1;
    }
    if (multiHpCanRepair1 && !multiHpCanRepair2) {
      return 1;
    } else if (!multiHpCanRepair1 && multiHpCanRepair2) {
      return -1;
    }
    if (transporting1 && !transporting2) {
      return 1;
    } else if (!transporting1 && transporting2) {
      return -1;
    }
    if (airOrCarrierOrTransport1 && !airOrCarrierOrTransport2) {
      return 1;
    } else if (!airOrCarrierOrTransport1 && airOrCarrierOrTransport2) {
      return -1;
    }
    return ua1.getMovement(u1.getOwner()) - ua2.getMovement(u2.getOwner());
  }
}
