using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace CommonLang.NIO
{
    public enum ByteOrder
    {
        LITTLE_ENDIAN,
        BIG_ENDIAN,
    }

    public abstract class Buffer
    {
        // Invariants: mark <= position <= limit <= capacity
        private int mark = -1;
        private int position = 0;
        private int limit;
        private int capacity;

        // Used only by direct buffers
        // NOTE: hoisted here for speed in JNI GetDirectBufferAddress
        IntPtr address;

        // Creates a new buffer with the given mark, position, limit, and capacity,
        // after checking invariants.
        //
        internal Buffer(int mark, int pos, int lim, int cap)
        {
            if (cap < 0)
                throw new Exception("Negative capacity: " + cap);
            this.capacity = cap;
            Limit(lim);
            Position(pos);
            if (mark >= 0)
            {
                if (mark > pos)
                    throw new Exception("mark > position: ("
                                                       + mark + " > " + pos + ")");
                this.mark = mark;
            }
        }

        public int Capacity()
        {
            return capacity;
        }

        public int Position()
        {
            return position;
        }

        public Buffer Position(int newPosition)
        {
            if ((newPosition > limit) || (newPosition < 0))
                throw new Exception("IllegalArgumentException");
            position = newPosition;
            if (mark > position) mark = -1;
            return this;
        }

        public int Limit()
        {
            return limit;
        }

        public Buffer Limit(int newLimit)
        {
            if ((newLimit > capacity) || (newLimit < 0))
                throw new Exception("IllegalArgumentException");
            limit = newLimit;
            if (position > limit) position = limit;
            if (mark > limit) mark = -1;
            return this;
        }

        public Buffer Mark()
        {
            mark = position;
            return this;
        }

        public Buffer Reset()
        {
            int m = mark;
            if (m < 0)
                throw new Exception("InvalidMarkException");
            position = m;
            return this;
        }

        public Buffer Clear()
        {
            position = 0;
            limit = capacity;
            mark = -1;
            return this;
        }

        public Buffer Flip()
        {
            limit = position;
            position = 0;
            mark = -1;
            return this;
        }

        public Buffer Rewind()
        {
            position = 0;
            mark = -1;
            return this;
        }

        public int Remaining()
        {
            return limit - position;
        }

        public bool HasRemaining()
        {
            return position < limit;
        }

        public abstract bool IsReadOnly();

        public abstract bool HasArray();

        public abstract Array Array();

        public abstract int ArrayOffset();

        internal int NextGetIndex()
        {
            if (position >= limit)
                throw new Exception("BufferUnderflowException");
            return position++;
        }

        internal int NextGetIndex(int nb)
        {
            if (limit - position < nb)
                throw new Exception("BufferUnderflowException");
            int p = position;
            position += nb;
            return p;
        }

        internal int NextPutIndex()
        {
            if (position >= limit)
                throw new Exception("BufferOverflowException");
            return position++;
        }

        internal int NextPutIndex(int nb)
        {
            if (limit - position < nb)
                throw new Exception("BufferOverflowException");
            int p = position;
            position += nb;
            return p;
        }

        internal int CheckIndex(int i)
        {
            if ((i < 0) || (i >= limit))
                throw new IndexOutOfRangeException();
            return i;
        }

        internal int CheckIndex(int i, int nb)
        {
            if ((i < 0) || (nb > limit - i))
                throw new IndexOutOfRangeException();
            return i;
        }

        internal int MarkValue()
        {
            return mark;
        }

        internal void Truncate()
        {
            mark = -1;
            position = 0;
            limit = 0;
            capacity = 0;
        }

        internal void DiscardMark()
        {
            mark = -1;
        }

        internal static void CheckBounds(int off, int len, int size)
        {
            if ((off | len | (off + len) | (size - (off + len))) < 0)
                throw new IndexOutOfRangeException();
        }

    }



    public abstract class ByteBuffer : Buffer
    {
        readonly internal byte[] hb;                  
        readonly internal int offset;          

        internal ByteBuffer(int mark, int pos, int lim, int cap,
                     byte[] hb, int offset)
            : base(mark, pos, lim, cap)
        {
            this.hb = hb;
            this.offset = offset;
        }

        internal ByteBuffer(int mark, int pos, int lim, int cap)
            : this(mark, pos, lim, cap, null, 0)
        {

        }

        public static ByteBuffer Allocate(int capacity)
        {
            if (capacity < 0)
                throw new Exception("IllegalArgumentException");
            return new HeapByteBuffer(capacity, capacity);
        }

        public static ByteBuffer Wrap(byte[] array,                                        int offset, int length)
        {
            try
            {
                return new HeapByteBuffer(array, offset, length);
            }
            catch (Exception err)
            {
				Console.WriteLine("Wrap 1: " + array + ", catch: " + err);
				throw new IndexOutOfRangeException();
            }
        }

        public static ByteBuffer Wrap(byte[] array)
        {
            return Wrap(array, 0, array.Length);
        }

        public abstract ByteBuffer Slice();

        public abstract ByteBuffer Duplicate();

        public abstract byte Get();

        public abstract ByteBuffer Put(byte b);

        public abstract byte Get(int index);

        public abstract ByteBuffer Put(int index, byte b);

        public virtual ByteBuffer Get(byte[] dst, int offset, int length)
        {
            CheckBounds(offset, length, dst.Length);
            if (length > Remaining())
                throw new Exception("BufferUnderflowException");
            int end = offset + length;
            for (int i = offset; i < end; i++)
                dst[i] = Get();
            return this;
        }

        public virtual ByteBuffer Get(byte[] dst)
        {
            return Get(dst, 0, dst.Length);
        }

        public virtual ByteBuffer Put(ByteBuffer src)
        {
            if (src == this)
                throw new Exception("IllegalArgumentException");
            int n = src.Remaining();
            if (n > Remaining())
                throw new Exception("BufferOverflowException");
            for (int i = 0; i < n; i++)
                Put(src.Get());
            return this;
        }

        public virtual ByteBuffer Put(byte[] src, int offset, int length)
        {
            CheckBounds(offset, length, src.Length);
            if (length > Remaining())
                throw new Exception("BufferOverflowException");
            int end = offset + length;
            for (int i = offset; i < end; i++)
                this.Put(src[i]);
            return this;
        }
        public virtual ByteBuffer Put(byte[] src)
        {
            return Put(src, 0, src.Length);
        }

        public override bool HasArray()
        {
            return (hb != null);
        }
        public override Array Array()
        {
            if (hb == null)
                throw new Exception("UnsupportedOperationException");
            return hb;
        }
        public override int ArrayOffset()
        {
            if (hb == null)
                throw new Exception("UnsupportedOperationException");
            return offset;
        }
        public abstract ByteBuffer Compact();

        public override String ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(GetType().Name);
            sb.Append("[pos=");
            sb.Append(Position());
            sb.Append(" lim=");
            sb.Append(Limit());
            sb.Append(" cap=");
            sb.Append(Capacity());
            sb.Append("]");
            return sb.ToString();
        }
        public override int GetHashCode()
        {
            int h = 1;
            int p = Position();
            for (int i = Limit() - 1; i >= p; i--)
                h = 31 * h + (int)Get(i);
            return h;
        }

        public override bool Equals(Object ob)
        {
            if (this == ob)
                return true;
            if (!(ob is ByteBuffer))
                return false;
            ByteBuffer that = (ByteBuffer)ob;
            if (this.Remaining() != that.Remaining())
                return false;
            int p = this.Position();
            for (int i = this.Limit() - 1, j = that.Limit() - 1; i >= p; i--, j--)
                if ((this.Get(i) != that.Get(j)))
                    return false;
            return true;
        }

    }




    public class HeapByteBuffer : ByteBuffer
    {

        internal HeapByteBuffer(int cap, int lim)
            : base(-1, 0, lim, cap, new byte[cap], 0)
        { }

        internal HeapByteBuffer(byte[] buf, int off, int len)
            : base(-1, off, off + len, buf.Length, buf, 0)
        { }

        protected HeapByteBuffer(byte[] buf, int mark, int pos, int lim, int cap, int off)
            : base(mark, pos, lim, cap, buf, off)
        { }

        public override ByteBuffer Slice()
        {
            return new HeapByteBuffer(hb,
                                            -1,
                                            0,
                                            this.Remaining(),
                                            this.Remaining(),
                                            this.Position() + offset);
        }
        public override ByteBuffer Duplicate()
        {
            return new HeapByteBuffer(hb,
                                            this.MarkValue(),
                                            this.Position(),
                                            this.Limit(),
                                            this.Capacity(),
                                            offset);
        }



        protected int ix(int i)
        {
            return i + offset;
        }

        public override byte Get()
        {
            return hb[ix(NextGetIndex())];
        }

        public override byte Get(int i)
        {
            return hb[ix(CheckIndex(i))];
        }

        public override ByteBuffer Get(byte[] dst, int offset, int length)
        {
            CheckBounds(offset, length, dst.Length);
            if (length > Remaining())
                throw new Exception("BufferUnderflowException");
            System.Buffer.BlockCopy(hb, ix(Position()), dst, offset, length);
            Position(Position() + length);
            return this;
        }




        public override bool IsReadOnly()
        {
            return false;
        }

        public override ByteBuffer Put(byte x)
        {
            hb[ix(NextPutIndex())] = x;
            return this;
        }

        public override ByteBuffer Put(int i, byte x)
        {
            hb[ix(CheckIndex(i))] = x;
            return this;
        }

        public override ByteBuffer Put(byte[] src, int offset, int length)
        {
            CheckBounds(offset, length, src.Length);
            if (length > Remaining())
                throw new Exception("BufferOverflowException");
            System.Buffer.BlockCopy(src, offset, hb, ix(Position()), length);
            Position(Position() + length);
            return this;
        }

        public override ByteBuffer Put(ByteBuffer src)
        {
            if (src is HeapByteBuffer)
            {
                if (src == this)
                    throw new Exception("IllegalArgumentException");
                HeapByteBuffer sb = (HeapByteBuffer)src;
                int n = sb.Remaining();
                if (n > Remaining())
                    throw new Exception("BufferOverflowException");
                System.Buffer.BlockCopy(sb.hb, sb.ix(sb.Position()),
                                 hb, ix(Position()), n);
                sb.Position(sb.Position() + n);
                Position(Position() + n);
            }
            else
            {
                base.Put(src);
            }
            return this;
        }

        public override ByteBuffer Compact()
        {
            System.Buffer.BlockCopy(hb, ix(Position()), hb, ix(0), Remaining());
            Position(Remaining());
            Limit(Capacity());
            DiscardMark();
            return this;
        }


    }


}