namespace AddisonWesley.Michaelis.EssentialCSharp.Chapter10.Listing10_05
{
    using System;

    public class Program
    {
        public static void Main()
        {
            //...

            Coordinate coordinate1 =
                new Coordinate(new Longitude(48, 52),
                                new Latitude(-2, -20));

            // Typy bezpośrednie nigdy nie są równe ze względu na referencje.
            if(Coordinate.ReferenceEquals(coordinate1,
                coordinate1))
            {
                throw new Exception(
                    "coordinate1 jest równa ze względu na referencje z coordinate1");
            }

            Console.WriteLine(
                "coordinate1 NIE jest równa ze względu na referencje z samą sobą");
        }
    }

    public struct Coordinate : IEquatable<Coordinate>
    {
        public Coordinate(Longitude longitude, Latitude latitude)
        {
            Longitude = longitude;
            Latitude = latitude;
        }

        public Longitude Longitude { get; }
        public Latitude Latitude { get; }

        public override bool Equals(object obj)
        {
            // KROK 1. Sprawdzenie, czy obiekt jest różny od null.
            if (obj == null)
            {
                return false;
            }
            // KROK 3. Sprawdzenie, czy typy pasują do siebie.
            // Można pominąć ten krok, jeśli typ jest zamknięty.
            if (this.GetType() != obj.GetType())
            {
                return false;
            }
            return Equals((Coordinate)obj);
        }
        public bool Equals(Coordinate obj)
        {
            // KROK 1. Sprawdzenie (w przypadku typów referencyjnych), 
            // czy obiekt jest różny od null.
            // if (obj == null)
            // {
            //   return false;
            // }

            // KROK 2. Sprawdzenie wyniku wywołania metody ReferenceEquals 
            // (gdy kod dotyczy typu referencyjnego).
            // if (ReferenceEquals(this, obj))
            // {
            //  return true;
            // }

            // KROK 4. Opcjonalne sprawdzenie, czy skróty są identyczne.
            // if (this.GetHashCode() != obj.GetHashCode())
            // {
            //   return false;
            // } 

            // KROK 5. Sprawdzenie wyniku wywołania base.Equals, jeśli 
            // w klasie bazowej przesłonięta jest metoda Equals().
            // System.Diagnostics.Debug.Assert(
            //   base.GetType() != typeof(object) );
            // if (!base.Equals(obj) )
            // {
            //   return false;
            // } 

            // KROK 6. Sprawdzenie, czy pola identyfikujące mają równą wartość.
            // Tu używana jest wersja metody Equals z typów Longitude i Latitude.
            return ((Longitude.Equals(obj.Longitude)) &&
              (Latitude.Equals(obj.Latitude)));
        }

        // KROK 7. Przesłonięcie metody GetHashCode.
        public override int GetHashCode()
        {
            int hashCode = Longitude.GetHashCode();
            hashCode ^= Latitude.GetHashCode(); // Operacja XOR.
            return hashCode;
        }
        public static bool operator ==(
            Coordinate leftHandSide,
            Coordinate rightHandSide)
        {
            return (leftHandSide.Equals(rightHandSide));
        }

        public static bool operator !=(
            Coordinate leftHandSide,
            Coordinate rightHandSide)
        {
            return !(leftHandSide.Equals(rightHandSide));
        }
    }

    public struct Longitude
    {
        public Longitude(int x, int y) { }
    }

    public struct Latitude
    {
        public Latitude(int x, int y) { }
    }
}