using System;
using System.Collections.Generic;
using System.Globalization;

namespace System.Reactive
{
	internal abstract class Either<TLeft, TRight>
	{
		private Either()
		{
		}

		public static Either<TLeft, TRight> CreateLeft(TLeft value)
		{
			return new Either<TLeft, TRight>.Left(value);
		}

		public static Either<TLeft, TRight> CreateRight(TRight value)
		{
			return new Either<TLeft, TRight>.Right(value);
		}

		public abstract TResult Switch<TResult>(Func<TLeft, TResult> caseLeft, Func<TRight, TResult> caseRight);

		public abstract void Switch(Action<TLeft> caseLeft, Action<TRight> caseRight);

		public sealed class Left : Either<TLeft, TRight>, IEquatable<Either<TLeft, TRight>.Left>
		{
			public Left(TLeft value)
			{
				this.Value = value;
			}

			public TLeft Value { get; }

			public override TResult Switch<TResult>(Func<TLeft, TResult> caseLeft, Func<TRight, TResult> caseRight)
			{
				return caseLeft(this.Value);
			}

			public override void Switch(Action<TLeft> caseLeft, Action<TRight> caseRight)
			{
				caseLeft(this.Value);
			}

			public bool Equals(Either<TLeft, TRight>.Left other)
			{
				return other == this || (other != null && EqualityComparer<TLeft>.Default.Equals(this.Value, other.Value));
			}

			public override bool Equals(object obj)
			{
				return this.Equals(obj as Either<TLeft, TRight>.Left);
			}

			public override int GetHashCode()
			{
				return EqualityComparer<TLeft>.Default.GetHashCode(this.Value);
			}

			public override string ToString()
			{
				return string.Format(CultureInfo.CurrentCulture, "Left({0})", this.Value);
			}
		}

		public sealed class Right : Either<TLeft, TRight>, IEquatable<Either<TLeft, TRight>.Right>
		{
			public Right(TRight value)
			{
				this.Value = value;
			}

			public TRight Value { get; }

			public override TResult Switch<TResult>(Func<TLeft, TResult> caseLeft, Func<TRight, TResult> caseRight)
			{
				return caseRight(this.Value);
			}

			public override void Switch(Action<TLeft> caseLeft, Action<TRight> caseRight)
			{
				caseRight(this.Value);
			}

			public bool Equals(Either<TLeft, TRight>.Right other)
			{
				return other == this || (other != null && EqualityComparer<TRight>.Default.Equals(this.Value, other.Value));
			}

			public override bool Equals(object obj)
			{
				return this.Equals(obj as Either<TLeft, TRight>.Right);
			}

			public override int GetHashCode()
			{
				return EqualityComparer<TRight>.Default.GetHashCode(this.Value);
			}

			public override string ToString()
			{
				return string.Format(CultureInfo.CurrentCulture, "Right({0})", this.Value);
			}
		}
	}
}
