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

using IronMath;

namespace IronPython.Runtime {

    public static partial class Int64Ops {
        public static ReflectedType MakeDynamicType(ReflectedType integerType) {
            OpsReflectedType ret = new OpsReflectedType("int64", typeof(long), typeof(Int64Ops), null);
            ret.effectivePythonType = integerType;
            return ret;
        }

        //		public static int __cmp__(int self, object obj) {
        //			int oi = Converter.ConvertToInt32(obj);
        //			return self == oi ? 0 : (self < oi ? -1 : +1);
        //		}
        //
        //		public static object Make(string s, int radix) {
        //			return LiteralParser.ParseInteger( s, radix);
        //		}		
        //
        //		public static object Make(object o) {
        //			if (o is string) return LiteralParser.ParseInteger( (string) o, 10);
        //			if (o is double) return (int) (double)o;
        //
        //			return Converter.ConvertToInt32(o);
        //		}

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

        [PythonName("__truediv__")]
        private static object TrueDivide(long x, long y) {
            return (double)x / (double)y;
        }

        [PythonName("__powmod__")]
        public static int PowerMod(long x, long power, long mod) {
            if (power < 0) {
                throw Ops.TypeError("power", power, "power must be >= 0");
            }
            if (mod == 0) {
                throw Ops.ZeroDivisionError();
            }

            if (power == 0) return 1;
            long factor = x;
            long result = 1;

            while (power != 0) {
                if ((power & 1) != 0) result = result * factor % mod;
                factor = factor * factor % mod; //???
                power >>= 1;
            }
            return (int)result;
        }

        [PythonName("__powmod__")]
        public static object PowerMod(long x, object y, object z) {
            return PowerMod(x, Converter.ConvertToInt64(y), Converter.ConvertToInt64(z));
        }

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

        [PythonName("__div__")]
        private static long Divide(long x, int y) {
            long q = checked(x / y);

            if (x >= 0) {
                if (y > 0) return q;
                else if (x % y == 0) return q;
                else return q - 1;
            } else {
                if (y > 0) {
                    if (x % y == 0) return q;
                    else return q - 1;
                } else return q;
            }
        }

        [PythonName("__div__")]
        private static long Divide(long x, long y) {
            long q = checked(x / y);

            if (x >= 0) {
                if (y > 0) return q;
                else if (x % y == 0) return q;
                else return q - 1;
            } else {
                if (y > 0) {
                    if (x % y == 0) return q;
                    else return q - 1;
                } else return q;
            }
        }

        [PythonName("__mod__")]
        private static long Mod(long x, int y) {
            long r = checked(x % y);

            if (x >= 0) {
                if (y > 0) return r;
                else if (r == 0) return 0;
                else return r + y;
            } else {
                if (y > 0) {
                    if (r == 0) return r;
                    else return r + y;
                } else return r;
            }
        }

        [PythonName("__mod__")]
        private static long Mod(long x, long y) {
            long r = checked(x % y);

            if (x >= 0) {
                if (y > 0) return r;
                else if (r == 0) return 0;
                else return r + y;
            } else {
                if (y > 0) {
                    if (r == 0) return r;
                    else return r + y;
                } else return r;
            }
        }

        private static int CompareWorker(long x, long y) {
            return x > y ? 1 : x < y ? -1 : 0;
        }

        private static int CompareWorker(long x, double y) {
            return x > y ? 1 : x < y ? -1 : 0;
        }

        private static int CompareWorker(long x, decimal y) {
            return x > y ? 1 : x < y ? -1 : 0;
        }

        private static int CompareWorker(ulong x, ulong y) {
            return x > y ? 1 : x < y ? -1 : 0;
        }

        private static int CompareWorker(ulong x, double y) {
            return x > y ? 1 : x < y ? -1 : 0;
        }

        private static int CompareWorker(ulong x, decimal y) {
            return x > y ? 1 : x < y ? -1 : 0;
        }

        private static int CompareWorker(long x, ulong y) {
            if (x < 0) return -1;
            if (y > Int64.MaxValue) return 1;

            return CompareWorker(x, (long)y);
        }

        [PythonName("__cmp__")]
        public static object Compare(ulong x, object other) {
            if (other is int) return -CompareWorker((long)(int)other, x);
            else if (other is long) return -CompareWorker((long)other, x);
            else if (other is double) return CompareWorker(x, (double)other);
            else if (other is BigInteger) return -(int)LongOps.Compare((BigInteger)other, x);
            else if (other is Complex64) return ComplexOps.TrueCompare(new Complex64((double)x, 0), (Complex64)other);
            else if (other is float) return CompareWorker(x, (double)(float)other);
            else if (other is bool) return CompareWorker(x, ((bool)other) ? 1UL : 0UL);
            else if (other is decimal) return CompareWorker(x, (decimal)other);
            else if (other is ulong) return CompareWorker(x, (ulong)other);

            Conversion conversion;
            double y = Converter.TryConvertToUInt64(other, out conversion);
            if (conversion != Conversion.None) CompareWorker(x, y);

            return Ops.NotImplemented;
        }

        [PythonName("__cmp__")]
        public static object Compare(long x, object other) {
            if (other is int) return CompareWorker(x, (long)(int)other);
            else if (other is long) return CompareWorker(x, (long)other);
            else if (other is double) return CompareWorker(x, (double)other);
            else if (other is BigInteger) return -(int)LongOps.Compare((BigInteger)other, x);
            else if (other is Complex64) return ComplexOps.TrueCompare(new Complex64((double)x, 0), (Complex64)other);
            else if (other is float) return CompareWorker(x, (double)(float)other);
            else if (other is bool) return CompareWorker(x, ((bool)other) ? 1L : 0L);
            else if (other is decimal) return CompareWorker(x, (decimal)other);
            else if (other is ulong) return CompareWorker(x, (ulong)other);

            Conversion conversion;
            double y = Converter.TryConvertToInt64(other, out conversion);
            if (conversion != Conversion.None) CompareWorker(x, y);

            return Ops.NotImplemented;
        }

        [PythonName("__eq__")]
        public static object Equals(long x, object other) {
            object ret = Compare(x, other);
            if (ret == Ops.NotImplemented) return ret;

            if ((int)ret == 0) return Ops.TRUE;
            return Ops.FALSE;
        }

        public static bool EqualsRetBool(long x, object other) {
            object ret = Compare(x, other);
            if (ret == Ops.NotImplemented) {
                return Ops.DynamicEqualRetBool(x, other);
            }

            return (int)ret == 0;
        }

        [PythonName("__neg__")]
        public static object Negate(long x) {
            try {
                return checked(-x);
            } catch (OverflowException) {
                return LongOps.Negate(x);
            }
        }

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

        private static object RightShift(long x, int y) {
            if (y < 0) {
                throw Ops.ValueError("negative shift count");
            }
            if (y > 63) {
                return x > 0 ? 0 : -1;
            }

            long q;

            if (x >= 0) {
                q = x >> y;
            } else {
                q = (x + ((1 << y) - 1)) >> y;
                long r = x - (q << y);
                if (r != 0) q--;
            }
            return Ops.Long2Object(q);
        }

        [PythonName("__lshift__")]
        public static object LeftShift(long x, object other) {
            if (other is int) {
                return LeftShift(x, (int)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(long x, int y) {
            if (y < 0) {
                throw Ops.ValueError("negative shift count");
            }
            if ((y > 63) ||
                (x > 0 && x > (Int64.MaxValue >> y)) ||
                (x < 0 && x < (Int64.MinValue >> y))) {
                return BigInteger.Create(x) << y;
            }

            long ret = x << y;
            return Ops.Long2Object(ret);
        }

        [PythonName("__pow__")]
        public static object Power(long x, long exp) {
            if (exp == 0) return 1;
            if (exp == 1) return x;
            if (exp < 0) {
                return FloatOps.Power(x, exp);
            }
            long saveexp = exp;
            long result = 1;
            long factor = x;
            try {
                checked {
                    while (exp != 0) {
                        if ((exp & 1) != 0) {
                            result = result * factor;
                        }
                        if (exp == 1) break;    // save possible overflow in the multiply
                        factor = factor * factor;
                        exp >>= 1;
                    }
                    return result;
                }
            } catch (OverflowException) {
                return LongOps.Power(BigInteger.Create(x), saveexp);
            }
        }

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

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