/* **********************************************************************************
 *
 * Copyright (c) Microsoft Corporation. All rights reserved.
 *
 * This source code is subject to terms and conditions of the Shared Source License
 * for IronPython. A copy of the license can be found in the License.html file
 * at the root of this distribution. If you can not locate the Shared Source License
 * for IronPython, please send an email to ironpy@microsoft.com.
 * By using this source code in any fashion, you are agreeing to be bound by
 * the terms of the Shared Source License for IronPython.
 *
 * You must not remove this notice, or any other, from this software.
 *
 * **********************************************************************************/

using System;
using System.Text;
using System.Collections;

using IronMath;

namespace IronPython.Runtime {
    public class ExtensibleComplex  {
        public Complex64 value;
        public ExtensibleComplex() { this.value = new Complex64(); }
        public ExtensibleComplex(double real) { value = new Complex64(real);  }
        public ExtensibleComplex(double real, double imag) {
            value = new Complex64(real, imag);
        }

        public override string ToString() {
            return value.ToString();
        }

        public override bool Equals(object obj) {
            return value.Equals(obj);
        }

        public override int GetHashCode() {
            return value.GetHashCode();
        }
    }

    public static partial class ComplexOps {
        public static ReflectedType MakeDynamicType() {
            return new OpsReflectedType("complex", typeof(Complex64), typeof(ComplexOps), typeof(ExtensibleComplex)); 
        }
        [PythonName("__new__")]
        public static object Make(PythonType cls) {
            if(cls == Ops.GetDynamicTypeFromType(typeof(Complex64))){
                return new Complex64();
            } else {
                return cls.ctor.Call(cls);
            }
        }

        [PythonName("__new__")]
        public static object Make(PythonType cls, object o) {
            if (o is string) return LiteralParser.ParseImaginary((string)o);
            if (o is Complex64) return o;

            Conversion conv;
            Complex64 res = Converter.TryConvertToComplex64(o, out conv);
            if (conv < Conversion.Truncation) return res;

            if (cls == Ops.GetDynamicTypeFromType(typeof(Complex64))) {
                return Complex64.MakeReal(Converter.ConvertToDouble(o));
            } else {
                return cls.ctor.Call(cls, Converter.ConvertToDouble(o));
            }
        }

        [PythonName("__new__")]
        public static object Make(PythonType cls, double r, double i) {
            if (cls == Ops.GetDynamicTypeFromType(typeof(Complex64))) {
                return new Complex64(r, i);
            } else {
                return cls.ctor.Call(cls, r, i);
            }
        }

        private static object TrueDivide(Complex64 x, Complex64 y) {
            return x / y;
        }

        public static object Abs(Complex64 x) {
            return x.Abs();
        }

        // #Power
        private static object Power(Complex64 x, Complex64 y) {
            if (x.IsZero && (y.Real < 0.0 || y.Imag != 0.0))
                throw Ops.ZeroDivisionError("0.0 to a negative or complex power");
            return x.Power(y);
        }

        public static object Divide(Complex64 x, object other) {
            if (other is Complex64) {
                return TrueDivide(x, (Complex64)other);
            } else {
                return TrueDivide(x, other);
            }
        }

        // floordiv for complex numbers is deprecated in the Python 2.4
        // specification; this function implements the observable
        // functionality in CPython 2.4: 
        //   Let x, y be complex.
        //   Re(x//y) := floor(Re(x/y))
        //   Im(x//y) := 0
        [PythonName("__floordiv__")]
        public static object FloorDivide(Complex64 x, object other) {
            object rawQuotient = Ops.TrueDivide(x, other);
            if (rawQuotient is Complex64) {
                Complex64 rawQComplex = (Complex64)rawQuotient;
                return new Complex64(Modules.PythonMath.Floor(rawQComplex.Real),
                    0);
            } else // quotient was not complex
                return new Complex64(Converter.ConvertToDouble(rawQuotient), 0);
        }

        // mod for complex numbers is also deprecated. IronPython
        // implements the CPython semantics, that is:
        // x % y = x - (y * (x//y)).
        [PythonName("__mod__")]
        public static object Mod(Complex64 x, object other) {
            Complex64 rawQuotient = (Complex64)ComplexOps.FloorDivide(x, other);
            Complex64 product = (Complex64)ComplexOps.Multiply(rawQuotient, other);
            return ComplexOps.Subtract(x, product);
        }

        [PythonName("__eq__")]
        public static object Equals(Complex64 x, object other) {
            if (other is int) return Ops.Bool2Object(x == (int)other);
            else if (other is double) return Ops.Bool2Object(x == (double)other);
            else if (other is BigInteger) return Ops.Bool2Object(x == (BigInteger)other);
            else if (other is Complex64) return Ops.Bool2Object(x == (Complex64)other);
            else if (other is bool) return Ops.Bool2Object((bool)other ? x == 1 : x == 0);

            Conversion conversion;
            Complex64 y = Converter.TryConvertToComplex64(other, out conversion);
            if (conversion != Conversion.None) return Ops.Bool2Object(x == y);

            return Ops.NotImplemented;
        }


        [PythonName("__gt__")]
        public static object GreaterThan(Complex64 self, object other) {
            return Compare(self, other) > 0;
        }

        [PythonName("__lt__")]
        public static object LessThan(Complex64 self, object other) {
            return Compare(self, other) < 0;
        }

        [PythonName("__ge__")]
        public static object GreaterThanEquals(Complex64 self, object other) {
            return Compare(self, other) >= 0;
        }

        [PythonName("__le__")]
        public static object LessThanEquals(Complex64 self, object other) {
            return Compare(self, other) <= 0;
        }

        public static bool EqualsRetBool(Complex64 x, object other) {
            if (other is int) return x == (int)other;
            else if (other is double) return x == (double)other;
            else if (other is BigInteger) return x == (BigInteger)other;
            else if (other is Complex64) return x == (Complex64)other;
            else if (other is bool) return (bool)other ? x == 1 : x == 0;

            Conversion conversion;
            Complex64 y = Converter.TryConvertToComplex64(other, out conversion);
            if (conversion != Conversion.None) return x == y;

            return Ops.DynamicEqualRetBool(x, other);
        }

        /// <summary>
        /// Used when user calls cmp(x,y) versus x > y, if the values are the same we return 0.
        /// </summary>
        public static int TrueCompare(object x, object y) {
            // Complex vs. null is 1 (when complex is on the lhs)
            // Complex vs. another type is -1 (when complex is on the lhs)
            // If two complex values are equal we return 0
            // Otherwise we throw because it's an un-ordered comparison
            if (x is Complex64) {
                Complex64 us = (Complex64)x;

                // Complex vs null, 1
                if (y == null) return 1;

                // Compex vs Complex, if they're equal we return 0, otherwize we throw
                Complex64 them = new Complex64();
                bool haveOther = false;
                if (y is Complex64) {
                    them = (Complex64)y;
                    haveOther = true;
                } else if (y is ExtensibleComplex) {
                    them = ((ExtensibleComplex)y).value;
                    haveOther = true;
                }

                if (haveOther) {
                    if (us.imag == them.imag && us.real == them.real) return 0;
                    throw Ops.TypeError("complex is not an ordered type");
                }

                // Complex vs user type, check what the user type says
                object ret = Ops.GetDynamicType(y).CompareTo(y, x);
                if (ret != Ops.NotImplemented) {
                    return ((int)ret) * -1;
                }

                // Otherwise all types are less than complex
                return -1;
            } else {               
                System.Diagnostics.Debug.Assert(y is Complex64);
                return -1 * TrueCompare(y, x);
            }            
        }

        /// <summary>
        /// Used for >, <, >=, <= where we don't allow ordered comparisons.
        /// </summary>
        private static int Compare(Complex64 x, object y) {
            // Complex vs. another type is -1 (when complex is on the lhs)
            // Complex vs. null is 1 (when complex is on the lhs)
            // If two complex values are equal we return 0
            // Otherwise we throw because it's an un-ordered comparison            

            if (y == null) return 1;
            if (y is Complex64 || y is ExtensibleComplex) throw Ops.TypeError("complex is not an ordered type");

            object ret = Ops.GetDynamicType(y).CompareTo(y, x);
            if (ret != Ops.NotImplemented) {
                return ((int)ret)*-1;
            }

            return -1;                       
        }

        public static object Negate(Complex64 x) {
            return -x;
        }

    }
}
