using Apewer.Internals;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace Apewer
{

    /// <summary></summary>
    public class BytesSet
    {

        private Dictionary<string, byte[]> _dict = new Dictionary<string, byte[]>();

        private volatile Func<byte[], byte[]> _inbound = null;
        private volatile Func<byte[], byte[]> _outbound = null;

        /// <summary></summary>
        public byte[] this[string key]
        {
            get { return GetValue(key); }
            set { SetValue(key, value); }
        }

        /// <summary>设置或获取值入站的函数,设置时字典必须为空。设置为 Null 将忽略入站函数。</summary>
        public Func<byte[], byte[]> Inbound
        {
            get { lock (_dict) return _inbound; }
            set { lock (_dict) _inbound = value; }
        }

        /// <summary>设置或获取值出站的函数。设置为 Null 将忽略出站函数。</summary>
        public Func<byte[], byte[]> Outbound
        {
            get { lock (_dict) return _outbound; }
            set { lock (_dict) _outbound = value; }
        }

        /// <summary></summary>
        public int Count
        {
            get
            {
                var count = 0;
                lock (_dict) { count = _dict.Count; }
                return count;
            }
        }

        /// <summary></summary>
        public List<string> Keys
        {
            get
            {
                var list = new List<string>();
                lock (_dict)
                {
                    list.AddRange(_dict.Keys);
                }
                return list;
            }
        }

        /// <summary></summary>
        public BytesSet() { }

        /// <summary></summary>
        public BytesSet(Func<byte[], byte[]> inbound, Func<byte[], byte[]> outbound)
        {
            _inbound = inbound;
            _outbound = outbound;
        }

        /// <summary></summary>
        public void Clear()
        {
            lock (_dict)
            {
                foreach (var key in _dict.Keys)
                {
                    _dict[key] = null;
                }
                _dict.Clear();
            }
        }

        /// <summary></summary>
        public bool Import(byte[] argData)
        {
            var memory = new MemoryStream();
            lock (argData)
            {
                if (argData != null) memory.Write(argData, 0, argData.Length);
                BinaryUtility.ResetPosition(memory);
            }
            if (memory.Length < 4) return false;
            var count = 0;
            var first = true;
            while (true)
            {
                if (!CanRead(memory, 4)) break;
                if (first)
                {
                    var buffer = new byte[4];
                    memory.Read(buffer, 0, 4);
                    count = GetInt32(buffer);

                    first = false;
                }
                else
                {

                    // Read Key Length
                    var keylength = 0;
                    {
                        if (!CanRead(memory, 4)) break;
                        var buffer = new byte[4];
                        memory.Read(buffer, 0, 4);
                        keylength = GetInt32(buffer);
                    }

                    // Read Key Data
                    var key = Constant.EmptyString;
                    if (keylength > 1)
                    {
                        if (!CanRead(memory, keylength)) break;
                        var buffer = new byte[keylength];
                        memory.Read(buffer, 0, keylength);
                        key = BinaryUtility.ToText(buffer);
                    }

                    // Read Value Length
                    var valuelength = 0;
                    {
                        if (!CanRead(memory, 4)) break;
                        var buffer = new byte[4];
                        memory.Read(buffer, 0, 4);
                        valuelength = GetInt32(buffer);
                    }

                    // Read Key Data
                    var value = Constant.EmptyBytes;
                    if (valuelength > 1)
                    {
                        if (!CanRead(memory, valuelength)) break;
                        var buffer = new byte[valuelength];
                        memory.Read(buffer, 0, valuelength);
                        value = BinaryUtility.Clone(buffer);
                    }

                    if (_dict.ContainsKey(key)) continue;
                    _dict.Add(key, value);
                    if (_dict.Count >= count) break;
                }
            }
            return count == _dict.Count;
        }

        /// <summary></summary>
        public byte[] Export()
        {
            var memory = new MemoryStream();
            lock (_dict)
            {
                var count = _dict.Count;
                var countbytes = GetBytes(count);
                memory.Write(countbytes, 0, countbytes.Length);
                foreach (var pair in _dict)
                {
                    var keydata = BinaryUtility.FromText(pair.Key);
                    var keycount = GetBytes(keydata.Length);
                    memory.Write(keycount, 0, keycount.Length);
                    if (keydata.Length > 0) memory.Write(keydata, 0, keydata.Length);

                    var valuedata = pair.Value ?? Constant.EmptyBytes;
                    var valuecount = GetBytes(valuedata.Length);
                    memory.Write(valuecount, 0, valuecount.Length);
                    if (valuedata.Length > 0) memory.Write(valuedata, 0, valuedata.Length);
                }
            }
            var data = memory.ToArray();
            memory.Dispose();
            return data;
        }

        /// <summary></summary>
        public bool Contains(string argKey)
        {
            if (argKey == null) return false;
            var contains = false;
            lock (_dict)
            {
                contains = _dict.ContainsKey(argKey);
            }
            return contains;
        }

        /// <summary></summary>
        public byte[] GetValue(string argKey)
        {
            var key = argKey;
            var value = Constant.EmptyBytes;
            if (key == null) return value;
            lock (_dict)
            {
                if (_dict.ContainsKey(key)) value = _dict[key];
            }
            if (_outbound != null)
            {
                value = _outbound(value);
                if (value == null) value = Constant.EmptyBytes;
            }
            return value;
        }

        /// <summary></summary>
        public bool SetValue(string argKey, byte[] argValue)
        {
            var key = argKey;
            var value = Constant.EmptyBytes;
            if (key == null) return false;
            lock (argValue)
            {
                if (argValue != null) value = BinaryUtility.Clone(argValue);
            }
            if (_inbound != null)
            {
                value = _inbound(value);
                if (value == null) value = Constant.EmptyBytes;
            }
            lock (_dict)
            {
                if (_dict.ContainsKey(key)) _dict.Remove(key);
                _dict.Add(key, value);
            }
            return true;
        }

        private static bool CanRead(Stream argStream, int argLength)
        {
            if (argLength < 0) return false;
            if (argStream == null) return false;
            if (argStream.CanRead == false) return false;
            if (argStream.Position + argLength > argStream.Length) return false;
            return true;
        }

        /// <summary>Int32 -> Byte[]</summary>
        private static byte[] GetBytes(int argValue)
        {
            const int t3 = 256 * 256 * 256;
            const int t2 = 256 * 256;
            const int t1 = 256;

            byte[] vbs = { 0, 0, 0, 0 };

            if (argValue >= 0)
            {
                int vint = argValue;

                vbs[0] = (byte)(vint / t3);
                vint = vint % t3;

                vbs[1] = (byte)(vint / t2);
                vint = vint % t2;

                vbs[2] = (byte)(vint / t1);
                vint = vint % t1;

                vbs[3] = (byte)(vint);
            }
            else
            {
                int vminusint = Math.Abs(argValue + 1);
                var vminusbs = GetBytes(vminusint);
                vbs[0] = (byte)(255 - vminusbs[0]);
                vbs[1] = (byte)(255 - vminusbs[1]);
                vbs[2] = (byte)(255 - vminusbs[2]);
                vbs[3] = (byte)(255 - vminusbs[3]);
            }

            return vbs;
        }

        /// <summary>Byte[] -> Int32</summary>
        private static Int32 GetInt32(byte[] argValue)
        {
            if (argValue.Length == 4)
            {
                const int t3 = 256 * 256 * 256;
                const int t2 = 256 * 256;
                const int t1 = 256;

                if (argValue[0] <= 127)
                {
                    int[] vis = { 0, 0, 0, 0 };
                    vis[0] = argValue[0] * t3;
                    vis[1] = argValue[1] * t2;
                    vis[2] = argValue[2] * t1;
                    vis[3] = argValue[3];
                    int vr = vis[0] + vis[1] + vis[2] + vis[3];
                    return vr;
                }
                else
                {
                    if ((argValue[0] == 128) && (argValue[1] == 0) && (argValue[2] == 0) && (argValue[3] == 0))
                    {
                        return int.MinValue;
                    }
                    else
                    {
                        var vbs = new byte[4];
                        vbs[0] = (byte)(255 - argValue[0]);
                        vbs[1] = (byte)(255 - argValue[1]);
                        vbs[2] = (byte)(255 - argValue[2]);
                        vbs[3] = (byte)(255 - argValue[3]);
                        int vminusint = 0 - 1 - GetInt32(vbs);
                        return vminusint;
                    }
                }
            }
            return 0;
        }

    }

}