/* **********************************************************************************
 *
 * 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 System.Threading;

using IronMath;

namespace IronPython.Runtime {
    /// <summary>
    /// BigInteger doesn't have the right ctor's on it for us to call 
    /// the derived class from a base class, so we define ExtensibeLong
    /// and give it a new ctor.
    /// </summary>
    public class ExtensibleLong : BigInteger, IPythonComparable {
        public ExtensibleLong()
            : base(0, new uint[0]) {
        }

        public ExtensibleLong(BigInteger val)
            : base(val) {
        }

        #region IPythonComparable Members

        public new object CompareTo(object other) {
            return LongOps.Compare(this, other);
        }

        public object GreaterThan(object other) {
            object res = CompareTo(other);
            if (res is int) {
                return ((int)res) > 0;
            }
            return Ops.NotImplemented;
        }

        public object LessThan(object other) {
            object res = CompareTo(other);
            if (res is int) {
                return ((int)res) < 0;
            }
            return Ops.NotImplemented;
        }

        public object GreaterThanOrEqual(object other) {
            object res = CompareTo(other);
            if (res is int) {
                return ((int)res) >= 0;
            }
            return Ops.NotImplemented;
        }

        public object LessThanOrEqual(object other) {
            object res = CompareTo(other);
            if (res is int) {
                return ((int)res) <= 0;
            }
            return Ops.NotImplemented;
        }

        #endregion
    }

    public static partial class LongOps {
        static ReflectedType LongType;
        public static ReflectedType MakeDynamicType() {
            if (LongType == null) {
                ReflectedType res = new OpsReflectedType("long", typeof(BigInteger), typeof(LongOps), typeof(ExtensibleLong));

                if (Interlocked.CompareExchange<ReflectedType>(ref LongType, res, null) == null)
                    return res;
            }
            return LongType;
        }

        [PythonName("__new__")]
        public static object Make(PythonType cls, string s, int radix) {
            if (cls == LongType) {
                return ParseBigIntegerSign(s, radix);
            } else {
                BigInteger res = ParseBigIntegerSign(s, radix);
                return cls.ctor.Call(cls, res);
            }
        }

        private static BigInteger ParseBigIntegerSign(string s, int radix) {
            try {
                return LiteralParser.ParseBigIntegerSign(s, radix);
            } catch (ArgumentException e) {
                throw Ops.ValueError(e.Message);
            }
        }
        
        [PythonName("__new__")]
        public static object Make(PythonType cls, object o) {
            if (cls == LongType) {
                if (o is string) return ParseBigIntegerSign((string)o, 10);
                if (o is IronMath.BigInteger) return (IronMath.BigInteger)o;
                else if (o is int) return (long)(int)o;
                else if (o is double) return IronMath.BigInteger.Create((double)o);
                else if (o is long) return o;
                else {
                    Conversion conv;
                    BigInteger intVal;
                    intVal = Converter.TryConvertToBigInteger(o, out conv);
                    if (conv < Conversion.Truncation) return intVal;
                }
            } else {
                BigInteger intVal = null;
                if (o is string) intVal = ParseBigIntegerSign((string)o, 10);
                else if (o is IronMath.BigInteger) intVal = (IronMath.BigInteger)o;
                else if (o is int) intVal = (long)(int)o;
                else if (o is double) intVal = IronMath.BigInteger.Create((double)o);
                else if (o is long) intVal = (long)o;
                else {
                    Conversion conv;
                    intVal = Converter.TryConvertToBigInteger(o, out conv);
                    if (conv >= Conversion.Truncation) intVal = null;
                }

                if (!Object.Equals(intVal, null)) {
                    return cls.ctor.Call(cls, intVal);
                }
            }
            throw Ops.ValueError("invalid value");
        }

        [PythonName("__new__")]
        public static object Make(PythonType cls) {
            if (cls == LongType) {
                return BigInteger.Zero;
            } else {
                return cls.ctor.Call(cls, BigInteger.Zero);
            }
        }
        
        private static object TrueDivide(BigInteger x, int y) {
            throw new NotImplementedException();
        }

        [PythonName("__abs__")]
        public static object Abs(BigInteger x) {
            return x.Abs();
        }

        [PythonName("__powmod__")]
        public static object PowerMod(BigInteger x, object y, object z) {
            if (y is int) {
                return PowerMod(x, (int)y, z);
            } else if (y is long) {
                long longy = (long)y;
                if (longy < Int32.MinValue || longy > Int32.MaxValue) {
                    throw Ops.ValueError("value too big");
                }
                return PowerMod(x, (int)longy, z);
            } else if (y is BigInteger) {
                int inty;
                if (((BigInteger)y).AsInt32(out inty)) {
                    return PowerMod(x, inty, z);
                }
                throw Ops.ValueError("value too big");
            }
            return Ops.NotImplemented;
        }

        private static object PowerMod(BigInteger x, int y, object z) {
            if (z is int) {
                return PowerMod(x, y, BigInteger.Create((int)z));
            } else if (z is long) {
                return PowerMod(x, y, BigInteger.Create((long)z));
            } else if (z is BigInteger) {
                return PowerMod(x, y, (BigInteger)z);
            }
            return Ops.NotImplemented;
        }

        private static object PowerMod(BigInteger x, int y, BigInteger z) {
            if (y < 0) {
                throw Ops.TypeError("power", y, "power must be >= 0");
            }
            if (z == BigInteger.Zero) {
                throw Ops.ZeroDivisionError();
            }
            return x.ModPow(y, z);
        }

        [PythonName("__pow__")]
        public static object Power(BigInteger x, int y) {
            if (y < 0) {
                return FloatOps.Power(x.ToFloat64(), y);
            }
            return x.Power(y);
        }

        [PythonName("__float__")]
        public static object ToFloat(BigInteger self) {
            return self.ToFloat64();
        }

        [PythonName("__pow__")]
        public static object Power(BigInteger x, long y) {
            if ((int)y == y) {
                return Power(x, (int)y);
            } else {
                if (y < 0) {
                    return FloatOps.Power(x.ToFloat64(), y);
                }
                if (x == BigInteger.Zero) {
                    return BigInteger.Zero;
                } else if (x == BigInteger.One) {
                    return BigInteger.One;
                } else {
                    throw Ops.ValueError("Number too big");
                }
            }
        }

        [PythonName("__cmp__")]
        public static object Compare(BigInteger x, object y) {
            if (y == null) return 1;

            int intVal;
            if (y is int) {
                if (x.AsInt32(out intVal)) return IntOps.Compare(intVal, y);
            } else if (y is ExtensibleInt) {
                if (x.AsInt32(out intVal)) return IntOps.Compare(intVal, ((ExtensibleInt)y).value);
            } else if (y is double) {
                double dbl = x.ToFloat64();
                return FloatOps.Compare(dbl, y);
            } else if (y is ExtensibleFloat) {
                double dbl = x.ToFloat64();
                return FloatOps.Compare(dbl, ((ExtensibleFloat)y).value);
            } else if (y is bool) {
                if (x.AsInt32(out intVal)) return IntOps.Compare(intVal, ((bool)y) ? 1 : 0);
            } else if (y is decimal) {
                double dbl = x.ToFloat64();
                return FloatOps.Compare(dbl, y);
            }
            
            Conversion conv;
            BigInteger bi = Converter.TryConvertToBigInteger(y, out conv);
            if (conv == Conversion.None) return Ops.NotImplemented;
            BigInteger diff = x-bi;
            if (diff == 0) return 0;
            else if (diff < 0) return -1;
            else return 1;
        }

        [PythonName("__gt__")]
        public static object __gt__(BigInteger x, object y) {
            if (y == null) return true;

            Conversion conv;
            BigInteger bi = Converter.TryConvertToBigInteger(y, out conv);
            if (conv == Conversion.None) return Ops.NotImplemented;
            return x- bi > 0;
        }

        [PythonName("__lt__")]
        public static object __lt__(BigInteger x, object y) {
            if (y == null) return false;

            Conversion conv;
            BigInteger bi = Converter.TryConvertToBigInteger(y, out conv);
            if (conv == Conversion.None) return Ops.NotImplemented;
            return x - bi < 0;
        }

        [PythonName("__ge__")]
        public static object __ge__(BigInteger x, object y) {
            if (y == null) return true;

            Conversion conv;
            BigInteger bi = Converter.TryConvertToBigInteger(y, out conv);
            if (conv == Conversion.None) return Ops.NotImplemented;
            return x - bi >= 0;
        }

        [PythonName("__le__")]
        public static object __le__(BigInteger x, object y) {
            if (y == null) return false;

            Conversion conv;
            BigInteger bi = Converter.TryConvertToBigInteger(y, out conv);
            if (conv == Conversion.None) return Ops.NotImplemented;
            return x- bi <= 0;
        }

        
        [PythonName("__pow__")]
        public static object Power(BigInteger x, BigInteger y) {
            long yl;
            if (y.AsInt64(out yl)) {
                return Power(x, yl);
            } else {
                if (x == BigInteger.Zero) {
                    if (y.IsNegative())
                        throw Ops.ZeroDivisionError("0.0 cannot be raised to a negative power");
                    return BigInteger.Zero;
                } else if (x == BigInteger.One) {
                    return BigInteger.One;
                } else {
                    throw Ops.ValueError("Number too big");
                }
            }
        }

        [PythonName("__div__")]
        public static object Divide(BigInteger x, object other) {
            return FloorDivide(x, other);
        }

        private static BigInteger DivMod(BigInteger x, BigInteger y, out BigInteger r) {
            BigInteger rr;
            BigInteger qq;

            qq = BigInteger.DivRem(x, y, out rr);

            if (x >= BigInteger.Zero) {
                if (y > BigInteger.Zero) {
                    r = rr;
                    return qq;
                } else {
                    if (rr == BigInteger.Zero) {
                        r = rr;
                        return qq;
                    } else {
                        r = rr + y;
                        return qq - BigInteger.One;
                    }
                }
            } else {
                if (y > BigInteger.Zero) {
                    if (rr == BigInteger.Zero) {
                        r = rr;
                        return qq;
                    } else {
                        r = rr + y;
                        return qq - BigInteger.One;
                    }
                } else {
                    r = rr;
                    return qq;
                }
            }
        }

        [PythonName("__div__")]
        public static object Divide(BigInteger x, int y) {
            BigInteger r;
            return DivMod(x, y, out r);
        }
        [PythonName("__div__")]
        public static object Divide(BigInteger x, long y) {
            BigInteger r;
            return DivMod(x, y, out r);
        }
        [PythonName("__div__")]
        public static object Divide(BigInteger x, uint y) {
            BigInteger r;
            return DivMod(x, y, out r);
        }
        [PythonName("__div__")]
        public static object Divide(BigInteger x, ulong y) {
            BigInteger r;
            return DivMod(x, y, out r);
        }
        [PythonName("__div__")]
        public static object Divide(BigInteger x, BigInteger y) {
            BigInteger r;
            return DivMod(x, y, out r);
        }
        [PythonName("__mod__")]
        public static object Mod(BigInteger x, int y) {
            BigInteger r;
            DivMod(x, y, out r);
            return r;
        }
        [PythonName("__mod__")]
        public static object Mod(BigInteger x, long y) {
            BigInteger r;
            DivMod(x, y, out r);
            return r;
        }
        [PythonName("__mod__")]
        public static object Mod(BigInteger x, uint y) {
            BigInteger r;
            DivMod(x, y, out r);
            return r;
        }
        [PythonName("__mod__")]
        public static object Mod(BigInteger x, ulong y) {
            BigInteger r;
            DivMod(x, y, out r);
            return r;
        }
        [PythonName("__mod__")]
        public static object Mod(BigInteger x, BigInteger y) {
            BigInteger r;
            DivMod(x, y, out r);
            return r;
        }
        [PythonName("__eq__")]
        public static object Equals(BigInteger x, object other) {
            if (other is int) return Ops.Bool2Object(x == (BigInteger)(int)other);
            else if (other is long) return Ops.Bool2Object(x == (BigInteger)(long)other);
            else if (other is double) return Ops.Bool2Object(x == (double)other);
            else if (other is BigInteger) return Ops.Bool2Object((BigInteger)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);
            else if (other is ExtensibleFloat) return Ops.Bool2Object(x == ((ExtensibleFloat)other).value);
            else if (other is decimal) return Ops.Bool2Object(x == (double)(decimal)other);
            else if (other == null) return Ops.FALSE;

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

            return Ops.NotImplemented;
        }

        public static bool EqualsRetBool(BigInteger x, object other) {
            if (other is int) return x == (BigInteger)(int)other;
            else if (other is long) return x == (BigInteger)(long)other;
            else if (other is double) return x == (double)other;
            else if (other is BigInteger) return (BigInteger)x == (BigInteger)other;
            else if (other is Complex64) return x == (Complex64)other;
            else if (other is bool) return (bool)other ? x == 1 : x == 0;
            else if (other is ExtensibleFloat) return x == ((ExtensibleFloat)other).value;
            else if (other is decimal) return x == (double)(decimal)other;
            else if (other == null) return false;

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

            return Ops.DynamicEqualRetBool(x, other);
        }

        [PythonName("__ne__")]
        public static object NotEquals(BigInteger x, object y) {
            object res = Equals(x, y);
            if (res == Ops.NotImplemented) return res;
            else return Ops.Bool2Object(!((bool)res));
        }


        [PythonName("__neg__")]
        public static object Negate(BigInteger x) {
            return -x;
        }

        [PythonName("__lshift__")]
        public static object LeftShift(BigInteger x, object other) {
            if (other is int) {
                return LeftShift(x, (int)other);
            } else if (other is BigInteger) {
                return LeftShift(x, (BigInteger)other);
            } else if (other is long) {
                long y = (long)other;
                if (Int32.MinValue <= y && y <= Int32.MaxValue) {
                    return LeftShift(x, (int)y);
                }
            }
            return Ops.NotImplemented;
        }

        private static object LeftShift(BigInteger x, int y) {
            if (y < 0) {
                throw Ops.ValueError("negative shift count");
            }
            return x << y;
        }
        private static object LeftShift(BigInteger x, BigInteger y) {
            if (y < BigInteger.Zero) {
                throw Ops.ValueError("negative shift count");
            }
            int yint;
            if (y.AsInt32(out yint)) {
                return x << yint;
            }
            if (x == BigInteger.Zero) {
                return BigInteger.Zero;
            } else {
                throw Ops.OverflowError("number too big");
            }
        }

        [PythonName("__rshift__")]
        public static object RightShift(BigInteger x, object other) {
            if (other is int) {
                int y = (int)other;
                BigInteger q;
                if (x < BigInteger.Zero) {
                    q = x >> y;
                    BigInteger r = x - (q << y);
                    if (r != BigInteger.Zero) q -= BigInteger.One; ;
                } else {
                    q = x >> y;
                }
                return q;
            } else if (other is BigInteger) {
                BigInteger y = (BigInteger)other;
                if (y < BigInteger.Zero) {
                    throw Ops.ValueError("negative shift count");
                }
                int yint;
                if (y.AsInt32(out yint)) {
                    return x >> yint;
                }
            }
            return Ops.NotImplemented;
        }

        [PythonName("__oct__")]
        public static string Oct(BigInteger x) {
            //!!! horribly inefficient
            if (x == BigInteger.Zero) {
                return "0L";
            } else if (x > 0) {
                return "0" + x.ToString(8) + "L";
            } else {
                return "-0" + (-x).ToString(8) + "L";
            }
        }

        [PythonName("__hex__")]
        public static string Hex(BigInteger x) {
            if (x < 0) {
                return "-0x" + (-x).ToString(16) + "L";
            } else {
                return "0x" + x.ToString(16) + "L";
            }
        }
    }
}
