using System;

using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace Wayne.Lib
{
    /// <summary>
    /// Class representing a version number.
    /// </summary>
    public class VersionNumber : IComparable<VersionNumber>, IEquatable<VersionNumber>
    {
        #region Fields

        private const char Dot = '.';
        private static readonly Regex re = new Regex(@"^\d+(\.\d+)*$");
        private readonly string versionString;

        #endregion

        #region Construction

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="versionString">Version string that is on a proper version number format.</param>
        /// <exception cref="ArgumentException">If versionString is not on correct Version number format.</exception>
        public VersionNumber(string versionString)
        {
            if (!re.IsMatch(versionString))
            {
                throw new ArgumentException(versionString + " is not a valid version string");
            }

            this.versionString = versionString;
        }

        #endregion

        #region Methods

        /// <summary>
        /// CompareTo method
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        public int CompareTo(VersionNumber other)
        {
            Stack<int> ps = Convert(this.ToString());
            Stack<int> qs = Convert(other.ToString());

            // Continue while they are equal
            while (ps.Count > 0 && qs.Count > 0)
            {
                int p = ps.Pop();
                int q = qs.Pop();

                if (p < q)
                {
                    return -1;
                }

                if (p > q)
                {
                    return 1;
                }
            }

            // Check who ran out
            if (ps.Count == 0 && qs.Count == 0)
            {
                // both ran out, they are equal
                return 0;
            }

            if (ps.Count > 0 && qs.Count == 0)
            {
                // right ran out, so left must be bigger
                return 1;
            }

            if (ps.Count == 0 && qs.Count > 0)
            {
                // left ran out, so right must be bigger
                return -1;
            }

            // Should never end up here...
            throw new ArgumentException("this cannot happen");
        }

        private static Stack<int> Convert(string fullString)
        {
            List<int> list = new List<int>();

            foreach (string number in fullString.Split(Dot))
            {
                list.Add(int.Parse(number));
            }

            list.Reverse();
            return new Stack<int>(list);
        }

        /// <summary>
        /// ToString method
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return versionString;
        }

        /// <summary>
        /// Equals method
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(object obj)
        {
            VersionNumber otherVersionNumber = obj as VersionNumber;

            if (otherVersionNumber != null)
                return this.Equals(otherVersionNumber);
            else
                return false;
        }

        /// <summary>
        /// Equals method
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        public bool Equals(VersionNumber other)
        {
            return this.CompareTo(other) == 0;
        }

        /// <summary>
        /// Hash code generation
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return versionString.GetHashCode();
        }
        #endregion 

    }
}