﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace HashTag.Versioning
{
    public class VersionBase : ICloneable, IComparable, IComparable<VersionBase>, IEquatable<VersionBase>, IComparer<VersionBase>
    {
        protected VersionBase.StringAction OnBeforeParse;
        protected VersionBase.QualifyStringDelegate OnParseStringIntoQualifiers;
        protected VersionBase.SegmentStringDelegate OnParseQualifierIntoSegments;
        protected VersionBase.VersionActionDelegate OnAfterParse;
        protected VersionBase.ValidateQualifiersDelegate OnValidateQualifiers;
        protected VersionBase.ValidateSegmentsDelegate OnValidateSegments;
        private string _versionText;

        public string VersionText
        {
            get
            {
                if (string.IsNullOrWhiteSpace(this._versionText))
                    return this.ToString();
                return this._versionText;
            }
            private set
            {
                this._versionText = value;
            }
        }

        public List<Qualifier> Qualifiers { get; set; }

        internal VersionBase()
        {
            this.Qualifiers = new List<Qualifier>();
            this.SetHandlers();
        }

        public VersionBase(string fullVersionString)
          : this()
        {
            this.Parse(fullVersionString);
        }

        public static implicit operator VersionBase(Version version)
        {
            return (VersionBase) new NetVersion(version);
        }

        public static implicit operator Version(VersionBase version)
        {
            return new Version(version.ToString());
        }

        public static bool operator ==(VersionBase v1, VersionBase v2)
        {
            return VersionBase.compareForStaticOperators(v1, v2) == 0;
        }

        public static bool operator !=(VersionBase v1, VersionBase v2)
        {
            return VersionBase.compareForStaticOperators(v1, v2) != 0;
        }

        public static bool operator <(VersionBase v1, VersionBase v2)
        {
            return VersionBase.compareForStaticOperators(v1, v2) < 0;
        }

        public static bool operator >(VersionBase v1, VersionBase v2)
        {
            return VersionBase.compareForStaticOperators(v1, v2) > 0;
        }

        public static bool operator <=(VersionBase v1, VersionBase v2)
        {
            return VersionBase.compareForStaticOperators(v1, v2) <= 0;
        }

        public static bool operator >=(VersionBase v1, VersionBase v2)
        {
            return VersionBase.compareForStaticOperators(v1, v2) >= 0;
        }

        protected virtual void SetHandlers()
        {
            this.SetParsingHandlers();
            this.SetValidationHandlers();
        }

        protected virtual void SetParsingHandlers()
        {
            this.OnParseStringIntoQualifiers += new VersionBase.QualifyStringDelegate(this.SplitVersionStringIntoQualifiers);
            this.OnParseQualifierIntoSegments += new VersionBase.SegmentStringDelegate(this.SplitQualifierTextIntoSegments);
            this.OnAfterParse += new VersionBase.VersionActionDelegate(this.Validate);
        }

        protected virtual void SetValidationHandlers()
        {
            this.OnValidateSegments += new VersionBase.ValidateSegmentsDelegate(this.ValidateSegmentsForQualifier);
            this.OnValidateQualifiers += new VersionBase.ValidateQualifiersDelegate(this.ValidateQualifiers);
        }

        public virtual int CompareTo(VersionBase other)
        {
            return this.Compare(this, other);
        }

        public virtual int Compare(VersionBase v1, VersionBase v2)
        {
            if (object.ReferenceEquals((object) v1, (object) v2))
                return 0;
            if ((object) v1 == null && (object) v2 != null)
                return -1;
            if ((object) v1 != null && (object) v2 == null)
                return 1;
            if (string.Compare(v1.VersionText, v2.VersionText) == 0)
                return 0;
            int num1 = Math.Min(v1.Qualifiers.Count, v2.Qualifiers.Count);
            for (int index = 0; index < num1; ++index)
            {
                if (this.ShouldCompareQualifiers(v1.Qualifiers[index], v2.Qualifiers[index]))
                {
                    int num2 = this.QualifierCompare(v1.Qualifiers[index], v2.Qualifiers[index]);
                    if (num2 != 0)
                        return num2;
                }
            }
            return 0;
        }

        protected virtual bool ShouldCompareQualifiers(Qualifier qualifier1, Qualifier qualifier2)
        {
            return (int) qualifier1.StartDelimiter == (int) qualifier2.StartDelimiter;
        }

        protected virtual int QualifierCompare(Qualifier left, Qualifier right)
        {
            if (object.ReferenceEquals((object) left, (object) right))
                return 0;
            if (left == null && right != null)
                return -1;
            if (left != null && right == null)
                return 1;
            if (string.Compare(left.QualifierText, right.QualifierText) == 0)
                return 0;
            int num1 = Math.Min(left.Segments.Count, right.Segments.Count);
            for (int index = 0; index < num1; ++index)
            {
                int num2 = this.segmentCompare(left.Segments[index], right.Segments[index]);
                if (num2 != 0)
                    return num2;
            }
            if (left.Segments.Count < right.Segments.Count)
                return -1;
            return left.Segments.Count > right.Segments.Count ? 1 : 0;
        }

        private int segmentCompare(QualifierSegment left, QualifierSegment right)
        {
            if (object.ReferenceEquals((object) left, (object) right))
                return 0;
            if (left == null && right != null)
                return -1;
            if (left != null && right == null)
                return 1;
            if (string.Compare(left.SegmentText, right.SegmentText) == 0)
                return 0;
            if (left.IsNumeric && !right.IsNumeric)
                return -1;
            if (!left.IsNumeric && right.IsNumeric)
                return 1;
            if (left.IsNumeric && right.IsNumeric)
                return left.NumericValue.CompareTo(right.NumericValue);
            return string.Compare(left.SegmentText, right.SegmentText);
        }

        protected virtual VersionBase ObjectToVersion(object version)
        {
            if (version == null)
                return (VersionBase) null;
            if (version is string)
                return (VersionBase) Activator.CreateInstance(this.GetType(), new object[1]
                {
          (object) (string) version
                });
            if (version is VersionBase)
                return (VersionBase) version;
            throw new ArgumentException(string.Format("Unable to convert '{0}' to '{1}'", (object) version.GetType().FullName, (object) this.GetType().FullName));
        }

        protected static int compareForStaticOperators(VersionBase v1, VersionBase v2)
        {
            if (object.ReferenceEquals((object) v1, (object) v2))
                return 0;
            if ((object) v1 == null && (object) v2 != null)
                return -1;
            if ((object) v1 != null && (object) v2 == null)
                return 1;
            return v1.CompareTo(v2);
        }

        public override int GetHashCode()
        {
            return this.VersionText.GetHashCode();
        }

        public virtual bool Equals(VersionBase other)
        {
            return this.CompareTo(other) == 0;
        }

        public virtual bool Equals(string other)
        {
            return string.Compare(this.VersionText, other) == 0;
        }

        private static int staticStringCompare(VersionBase v1, string v2)
        {
            return VersionBase.compareForStaticOperators(v1, (VersionBase) new SemanticVersion(v2));
        }

        public virtual int CompareTo(object obj)
        {
            if (obj == null)
                return 1;
            VersionBase version = this.ObjectToVersion(obj);
            if (version == (VersionBase) null)
                throw new ArgumentException(string.Format("Unable to convert type '{0}' to '{1}'", (object) obj.GetType().Name, (object) this.GetType().Name));
            return this.CompareTo(version);
        }

        public object Clone()
        {
            return Activator.CreateInstance(this.GetType(), new object[1]
            {
        (object) this.VersionText
            });
        }

        public override string ToString()
        {
            return string.Join("", this.Qualifiers.Select<Qualifier, string>((Func<Qualifier, string>) (q => q.ToString())).ToArray<string>());
        }

        public virtual void Parse(string versionString)
        {
            if (this.OnBeforeParse != null)
                versionString = this.OnBeforeParse(versionString);
            this.VersionText = versionString;
            this.validateStringContent(versionString);
            if (this.OnParseStringIntoQualifiers != null)
                this.Qualifiers = this.OnParseStringIntoQualifiers(versionString);
            if (this.OnParseQualifierIntoSegments != null)
            {
                for (int index = 0; index < this.Qualifiers.Count; ++index)
                {
                    Qualifier qualifier = this.Qualifiers[index];
                    List<QualifierSegment> qualifierSegmentList = this.OnParseQualifierIntoSegments(qualifier);
                    qualifier.Segments.AddRange((IEnumerable<QualifierSegment>) qualifierSegmentList);
                }
            }
            if (this.OnAfterParse == null)
                return;
            this.OnAfterParse();
        }

        public virtual List<Qualifier> SplitVersionStringIntoQualifiers(string fullVersionString)
        {
            List<Qualifier> qualifierList = new List<Qualifier>();
            Qualifier currentQualifier = new Qualifier();
            currentQualifier.QualifierTextStartPosition = 0;
            for (int index = 0; index < fullVersionString.Length; ++index)
            {
                if (this.IsQualifierStartDelimiter(fullVersionString[index], this.Qualifiers, currentQualifier))
                {
                    qualifierList.Add(currentQualifier);
                    currentQualifier = new Qualifier()
                    {
                        StartDelimiter = fullVersionString[index]
                    };
                    if (index < fullVersionString.Length - 1)
                        currentQualifier.QualifierTextStartPosition = index + 1;
                }
                else
                    currentQualifier.QualifierText += (string) (object) fullVersionString[index];
            }
            qualifierList.Add(currentQualifier);
            return qualifierList;
        }

        protected virtual bool IsQualifierStartDelimiter(char currentCharacter, List<Qualifier> parsedQualifiers, Qualifier currentQualifier)
        {
            if ((int) currentCharacter != 45)
                return (int) currentCharacter == 43;
            return true;
        }

        private void validateStringContent(string versionString)
        {
            if (string.IsNullOrWhiteSpace(versionString))
                throw new ArgumentException("Version must not be null or empty");
            versionString = versionString.Trim();
            versionString.All<char>((Func<char, bool>) (c => this.IsValidVersionStringCharacter(c)));
        }

        protected virtual bool IsValidVersionStringCharacter(char testCharacter)
        {
            bool flag = char.IsLetterOrDigit(testCharacter) || (int) testCharacter == 46 || (int) testCharacter == 43 || (int) testCharacter == 45;
            if (!flag)
                throw new ArgumentException("Version must contain only the following characters: A-Z a-z 0-9 (alpha-numerics), . (period), + (plus),  - (hyphen)");
            return flag;
        }

        protected List<QualifierSegment> SplitQualifierTextIntoSegments(Qualifier qualifier)
        {
            List<QualifierSegment> qualifierSegmentList = new List<QualifierSegment>();
            if (string.IsNullOrWhiteSpace(qualifier.QualifierText))
                throw new ArgumentException("Qualifier is not formatted correctly");
            string qualifierText = qualifier.QualifierText;
            char[] chArray = new char[1] { '.' };
            foreach (string str in qualifierText.Split(chArray))
            {
                QualifierSegment qualifierSegment = new QualifierSegment(str);
                qualifierSegmentList.Add(qualifierSegment);
                int result = 0;
                qualifierSegment.IsNumeric = int.TryParse(str, out result);
                if (qualifierSegment.IsNumeric)
                    qualifierSegment.NumericValue = result;
            }
            return qualifierSegmentList;
        }

        public virtual void Validate()
        {
            if (this.OnValidateQualifiers != null)
                this.OnValidateQualifiers(this.Qualifiers);
            if (this.OnValidateSegments == null)
                return;
            for (int currentIndex = 0; currentIndex < this.Qualifiers.Count; ++currentIndex)
            {
                Qualifier qualifier = this.Qualifiers[currentIndex];
                this.OnValidateSegments(currentIndex, this.Qualifiers);
            }
        }

        protected void ValidateSegmentsForQualifier(int currentIndex, List<Qualifier> Qualifiers)
        {
            Qualifier qualifier = Qualifiers[currentIndex];
            if (string.IsNullOrWhiteSpace(qualifier.QualifierText) || qualifier.Segments.Count == 0)
                throw new ArgumentException("Qualifiers must not be empty");
            if (qualifier.Segments.Count<QualifierSegment>((Func<QualifierSegment, bool>) (s => string.IsNullOrWhiteSpace(s.SegmentText))) > 0)
                throw new ArgumentException("Qualifier segments must not be empty");
            if (qualifier.Segments.Count<QualifierSegment>((Func<QualifierSegment, bool>) (s =>
            {
                if (!s.IsNumeric)
                    return false;
                if (!s.SegmentText.StartsWith("0") || s.NumericValue == 0)
                    return s.NumericValue < 0;
                return true;
            })) != 0)
                throw new ArgumentException("Numeric segments must non-negative integers not prefixed with leading zeros");
        }

        protected void ValidateQualifiers(List<Qualifier> qualifiers)
        {
            if (qualifiers.Count == 0)
                throw new ArgumentException("Version must have at least one qualifier");
            if (qualifiers.Count<Qualifier>((Func<Qualifier, bool>) (c => string.IsNullOrWhiteSpace(c.QualifierText))) != 0)
                throw new ArgumentException("Qualifiers must not be empty or null");
            if ((int) qualifiers[0].StartDelimiter != 0)
                throw new ArgumentException("First qualifier must NOT have a delimiter");
        }

        protected delegate string StringAction(string versionString);

        protected delegate List<Qualifier> QualifyStringDelegate(string versionString);

        protected delegate List<QualifierSegment> SegmentStringDelegate(Qualifier qualifier);

        protected delegate void VersionActionDelegate();

        protected delegate void ValidateQualifiersDelegate(List<Qualifier> qualifiers);

        protected delegate void ValidateSegmentsDelegate(int currentIndex, List<Qualifier> Qualifiers);
    }
}
