using System;
using System.Collections;
using System.Data;
using System.IO;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Text;

namespace ms.aq.cview.util {
    public abstract class DBNumber {
        public abstract long ToLong();
        public abstract double ToDouble();

        public static implicit operator double(DBNumber i) { return i.ToDouble(); }
        public static implicit operator long(DBNumber i) { return i.ToLong(); }
        
        public static implicit operator DBNumber(int i) { return DBNumberFactory.Object2DBNumber(i); }
        public static implicit operator DBNumber(double i) { return DBNumberFactory.Object2DBNumber(i); }

        protected static void ct(DBNumber a, DBNumber b) {
            if (a.GetType().Name != b.GetType().Name)
                throw new DBDifTypeException();
        }
        public static DBNumber Dif(DBNumber a, DBNumber b) {
            if (a is DBInt && b is DBInt)
                return new DBInt(Math.Abs((long)a - (long)b));
            else return new DBFloat(Math.Abs((double)a - (double)b));
        }
        public DBNumber Add(int a) { return Add((long)a); }
        public DBNumber Add(long a) {
            if (this is DBInt)
                return new DBInt((long)this + a);
            else if (this is DBFloat)
                return new DBFloat((double)this + a);
            else throw new NotImplementedException();
        }
        public DBNumber Add(double a) {
            if (this is DBInt)
                return new DBFloat((double)((long)this + a));
            else if (this is DBFloat) {
                return new DBFloat((double)this + a);
            }
            else throw new NotImplementedException();
        }
        public DBNumber Add(DBNumber a) {
            if (a is DBInt)
                return Add((long)a);
            else if (a is DBFloat)
                return Add((double)a);
            else
                throw new NotImplementedException();
        }
        public DBNumber Sub(int a) { return Sub((long)a); }
        public DBNumber Sub(long a) {
            if (this is DBInt)
                return new DBInt((long)this - a);
            else if (this is DBFloat)
                return new DBFloat((double)this - a);
            else throw new NotImplementedException();
        }
        public DBNumber Sub(double a) {
            if (this is DBInt)
                return new DBFloat((double)((long)this - a));
            else if (this is DBFloat) {
                return new DBFloat((double)this - a);
            }
            else throw new NotImplementedException();
        }
        public DBNumber Sub(DBNumber a) {
            if (a is DBInt)
                return Sub((long)a);
            else if (a is DBFloat)
                return Sub((double)a);
            else
                throw new NotImplementedException();
        }
        public DBNumber Mult(int a) {return Mult((long)a);}
        public DBNumber Mult(long a) {
            if (this is DBInt)
                return new DBInt((long)this * a);
            else if (this is DBFloat)
                return new DBFloat((double)this * a);
            else throw new NotImplementedException();
        }
        public DBNumber Div(int a) { return Div((long)a); }
        public DBNumber Div(long a) {
            if (this is DBInt)
                return new DBInt((long)this / a);
            else if (this is DBFloat)
                return new DBFloat((double)this / a);
            else throw new NotImplementedException();
        }
        public static double DivR(DBNumber a, DBNumber b) {
            if (a is DBInt && b is DBInt) {
                long rem;
                long r = Math.DivRem((long)a, (long)b, out rem);
                return r + ((double)rem / (long)b);
            }
            else if (a is DBFloat && b is DBFloat)
                return (double)a / (double)b;
            else
                throw new NotImplementedException();  
        }
        public bool GT(int a) {return GT((long)a);}
        public bool GT(long a) {
            if (this is DBInt)
                return (long)this > a;
            else if (this is DBFloat)
                return ((double)this) > a;
            else throw new NotImplementedException();
        }
        public bool GT(double a) {
            if (this is DBInt)
                return (long)this > a;
            else if (this is DBFloat)
                return ((double)this) > a;
            else throw new NotImplementedException();
        }
        public bool GT(DBNumber a) {
            if (a is DBInt)
                return GT((long)a);
            else if (a is DBFloat)
                return GT((double)a);
            else
                throw new NotImplementedException();
        }
        public bool LT(int a) { return LT((long)a); }
        public bool LT(long a) {
            if (this is DBInt)
                return (long)this < a;
            else if (this is DBFloat)
                return ((double)this) < a;
            else throw new NotImplementedException();
        }
        public bool LT(double a) {
            if (this is DBInt)
                return (long)this < a;
            else if (this is DBFloat)
                return ((double)this) < a;
            else throw new NotImplementedException();
        }
        public bool LT(DBNumber a) {
            if (a is DBInt)
                return LT((long)a);
            else if (a is DBFloat)
                return LT((double)a);
            else
                throw new NotImplementedException();
        }
        public int CompareTo(int a) { return CompareTo((long)a); }
        public int CompareTo(long a) {
            if (this is DBInt)
                return ((long)this).CompareTo(a);
            else if (this is DBFloat)
                return ((double)this).CompareTo(a);
            else throw new NotImplementedException();
        }
    }
    public class DBDifTypeException:Exception{}
    [Serializable]
    public class DBInt : DBNumber {
        long val;
        public DBInt(long i) { val = i; }
        public static implicit operator DBInt(long i) { return new DBInt(i); }
        public static implicit operator long(DBInt i) { return i.val; }
        public override string ToString() { return val.ToString(); }
        public override long ToLong() {
            return val;
        }
        public override double ToDouble() {
            return (double)val;
        }
    }
    [Serializable]
    public class DBFloat : DBNumber {
        double val;
        public DBFloat(double i) { val = i; }
        public static implicit operator DBFloat(double i) { return new DBFloat(i); }
        public static implicit operator double(DBFloat i) { return i.val; }
        public override string ToString() { return val.ToString(); }
        public override long ToLong() {
            if (val == Math.Floor(val))//should be safe, i think
                return (long)val;
            Console.WriteLine("this number, "+val+", is not a long");
            throw new OverflowException();
        }
        public override double ToDouble() {
            return val;
        }
    }

    public abstract class DBNumberFactory {
        public static DBNumber Object2DBNumber(object o) {
            Type t = Type2DBNumber(o.GetType());
            return (DBNumber)t.GetConstructor(new Type[] { o.GetType() }).Invoke(new object[] { o });
        }
        public static Type Type2DBNumber(Type type) {
            switch (type.Name) {
                case "Double":
                case "Float":
                case "Single":
                    return typeof(DBFloat);
                case "Byte":
                case "Int16":
                case "Int32":
                case "Int64":
                    return typeof(DBInt);
                default:
                    throw new Exception(type.Name + " has no corresponding DBNumber type");
            }
        }
    }
}