using System;
using System.Collections.Generic;
using System.Linq;

namespace CoordinateSharp {



  /// <summary>
  /// Used for UTM/MGRS Conversions
  /// </summary>
  [Serializable]
  internal class LatZones {
    public static List<String> longZongLetters = new List<String>(new String[]{"C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T",
        "U", "V", "W", "X"});
  }
  /// <summary>
  /// Used for handling diagraph determination
  /// </summary>
  [Serializable]
  internal class Digraphs {
    private readonly List<Digraph> digraph1;
    private readonly List<Digraph> digraph2;

    private readonly String[] digraph1Array = { "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };

    private readonly String[] digraph2Array = { "V", "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V" };

    public Digraphs() {
      this.digraph1 = new List<Digraph>();
      this.digraph2 = new List<Digraph>();

      this.digraph1.Add(new Digraph() { Zone = 1, Letter = "A" });
      this.digraph1.Add(new Digraph() { Zone = 2, Letter = "B" });
      this.digraph1.Add(new Digraph() { Zone = 3, Letter = "C" });
      this.digraph1.Add(new Digraph() { Zone = 4, Letter = "D" });
      this.digraph1.Add(new Digraph() { Zone = 5, Letter = "E" });
      this.digraph1.Add(new Digraph() { Zone = 6, Letter = "F" });
      this.digraph1.Add(new Digraph() { Zone = 7, Letter = "G" });
      this.digraph1.Add(new Digraph() { Zone = 8, Letter = "H" });
      this.digraph1.Add(new Digraph() { Zone = 9, Letter = "J" });
      this.digraph1.Add(new Digraph() { Zone = 10, Letter = "K" });
      this.digraph1.Add(new Digraph() { Zone = 11, Letter = "L" });
      this.digraph1.Add(new Digraph() { Zone = 12, Letter = "M" });
      this.digraph1.Add(new Digraph() { Zone = 13, Letter = "N" });
      this.digraph1.Add(new Digraph() { Zone = 14, Letter = "P" });
      this.digraph1.Add(new Digraph() { Zone = 15, Letter = "Q" });
      this.digraph1.Add(new Digraph() { Zone = 16, Letter = "R" });
      this.digraph1.Add(new Digraph() { Zone = 17, Letter = "S" });
      this.digraph1.Add(new Digraph() { Zone = 18, Letter = "T" });
      this.digraph1.Add(new Digraph() { Zone = 19, Letter = "U" });
      this.digraph1.Add(new Digraph() { Zone = 20, Letter = "V" });
      this.digraph1.Add(new Digraph() { Zone = 21, Letter = "W" });
      this.digraph1.Add(new Digraph() { Zone = 22, Letter = "X" });
      this.digraph1.Add(new Digraph() { Zone = 23, Letter = "Y" });
      this.digraph1.Add(new Digraph() { Zone = 24, Letter = "Z" });
      this.digraph1.Add(new Digraph() { Zone = 1, Letter = "A" });

      this.digraph2.Add(new Digraph() { Zone = 0, Letter = "V" });
      this.digraph2.Add(new Digraph() { Zone = 1, Letter = "A" });
      this.digraph2.Add(new Digraph() { Zone = 2, Letter = "B" });
      this.digraph2.Add(new Digraph() { Zone = 3, Letter = "C" });
      this.digraph2.Add(new Digraph() { Zone = 4, Letter = "D" });
      this.digraph2.Add(new Digraph() { Zone = 5, Letter = "E" });
      this.digraph2.Add(new Digraph() { Zone = 6, Letter = "F" });
      this.digraph2.Add(new Digraph() { Zone = 7, Letter = "G" });
      this.digraph2.Add(new Digraph() { Zone = 8, Letter = "H" });
      this.digraph2.Add(new Digraph() { Zone = 9, Letter = "J" });
      this.digraph2.Add(new Digraph() { Zone = 10, Letter = "K" });
      this.digraph2.Add(new Digraph() { Zone = 11, Letter = "L" });
      this.digraph2.Add(new Digraph() { Zone = 12, Letter = "M" });
      this.digraph2.Add(new Digraph() { Zone = 13, Letter = "N" });
      this.digraph2.Add(new Digraph() { Zone = 14, Letter = "P" });
      this.digraph2.Add(new Digraph() { Zone = 15, Letter = "Q" });
      this.digraph2.Add(new Digraph() { Zone = 16, Letter = "R" });
      this.digraph2.Add(new Digraph() { Zone = 17, Letter = "S" });
      this.digraph2.Add(new Digraph() { Zone = 18, Letter = "T" });
      this.digraph2.Add(new Digraph() { Zone = 19, Letter = "U" });
      this.digraph2.Add(new Digraph() { Zone = 20, Letter = "V" });
    }

    internal Int32 GetDigraph1Index(String letter) {
      for (Int32 i = 0; i < this.digraph1Array.Length; i++) {
        if (this.digraph1Array[i].Equals(letter)) {
          return i + 1;
        }
      }

      return -1;
    }

    internal Int32 GetDigraph2Index(String letter) {
      for (Int32 i = 0; i < this.digraph2Array.Length; i++) {
        if (this.digraph2Array[i].Equals(letter)) {
          return i;
        }
      }

      return -1;
    }

    internal String GetDigraph1(Int32 longZone, Double easting) {
      Int32 a1 = longZone;
      Double a2 = 8 * ((a1 - 1) % 3) + 1;

      Double a3 = easting;
      Double a4 = a2 + (Int32)(a3 / 100000) - 1;
      return this.digraph1.Where(x => x.Zone == Math.Floor(a4)).FirstOrDefault().Letter;
    }

    internal String GetDigraph2(Int32 longZone, Double northing) {
      Int32 a1 = longZone;
      Double a2 = 1 + 5 * ((a1 - 1) % 2);
      Double a3 = northing;
      Double a4 = a2 + (Int32)(a3 / 100000);
      a4 = (a2 + (Int32)(a3 / 100000.0)) % 20;
      a4 = Math.Floor(a4);
      if (a4 < 0) {
        a4 += 19;
      }
      return this.digraph2.Where(x => x.Zone == Math.Floor(a4)).FirstOrDefault().Letter;
    }

  }
  /// <summary>
  /// Diagraph model
  /// </summary>
  [Serializable]
  internal class Digraph {
    public Int32 Zone { get; set; }
    public String Letter { get; set; }
  }
  /// <summary>
  /// Used for setting whether a coordinate part is latitudinal or longitudinal.
  /// </summary>
  [Serializable]
  public enum CoordinateType {
    /// <summary>
    /// Latitude
    /// </summary>
    Lat,
    /// <summary>
    /// Longitude
    /// </summary>
    Long
  }
  /// <summary>
  /// Used to set a coordinate part position.
  /// </summary>
  [Serializable]
  public enum CoordinatesPosition : Int32 {
    /// <summary>
    /// North
    /// </summary>
    N,
    /// <summary>
    /// East
    /// </summary>
    E,
    /// <summary>
    /// South
    /// </summary>
    S,
    /// <summary>
    /// West
    /// </summary>
    W
  }


  /// <summary>
  /// Coordinate type datum specification
  /// </summary>
  [Serializable]
  [Flags]
  public enum Coordinate_Datum {
    /// <summary>
    /// Lat Long GeoDetic
    /// </summary>
    LAT_LONG = 1,
    /// <summary>
    /// UTM and MGRS
    /// </summary>
    UTM_MGRS = 2,
    /// <summary>
    /// ECEF
    /// </summary>
    ECEF = 4,
  }

  /// <summary>
  /// Cartesian Coordinate Type
  /// </summary>
  public enum CartesianType {
    /// <summary>
    /// Spherical Cartesian
    /// </summary>
    Cartesian,
    /// <summary>
    /// Earth Centered Earth Fixed
    /// </summary>
    ECEF,
  }
  /// <summary>
  /// Used for easy read math functions
  /// </summary>
  [Serializable]
  internal static class ModM {
    public static Double Mod(Double x, Double y) => x - y * Math.Floor(x / y);

    public static Double ModLon(Double x) => Mod(x + Math.PI, 2 * Math.PI) - Math.PI;

    public static Double ModCrs(Double x) => Mod(x, 2 * Math.PI);

    public static Double ModLat(Double x) => Mod(x + Math.PI / 2, 2 * Math.PI) - Math.PI / 2;
  }
  /// <summary>
  /// Earth Shape for Calculations.
  /// </summary>

  [Serializable]
  public enum Shape {
    /// <summary>
    /// Calculate as sphere (less accurate, more efficient).
    /// </summary>
    Sphere,
    /// <summary>
    /// Calculate as ellipsoid (more accurate, less efficient).
    /// </summary>
    Ellipsoid
  }


}