using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO;
using System.Xml.Serialization;
using System.Runtime.Serialization;
using System.Diagnostics;
using System.Runtime.Serialization.Formatters.Binary;

namespace WinnyProtocolAnalyzer.Data
{
    class Data
    {
        public List<Node> NodeList { get; private set; }
        public List<Key> KeyList { get; private set; }

        ReaderWriterLock NodeListLock = new ReaderWriterLock();
        ReaderWriterLock KeyListLock = new ReaderWriterLock();

        public Data(bool readData, string dataPath)
        {
            if (DataDeserialize(readData, dataPath) != 0)
            {
                Debug.WriteLine("Data Deserialize Error");
            }

            //Timer UpdateTimer = new Timer(new TimerCallback(Update), null, 60000, Property.UpdateTiming);
        }

        /// <summary>
        /// データをデシリアライズします。
        /// </summary>
        /// <returns>デシリアライズ結果</returns>
        private int DataDeserialize(bool readData, string dataPath)
        {
            int iResult = 0;

            try
            {
                #region NodeList
                if (!File.Exists(dataPath + Property.NodeListSerializeFileName))
                {
                    NodeList = new List<Node>();
                }
                else
                {
                    using (FileStream Stream = new FileStream(dataPath + Property.NodeListSerializeFileName, FileMode.Open, FileAccess.Read))
                    {
                        try
                        {
                            NodeListLock.AcquireWriterLock(Timeout.Infinite);
                            XmlSerializer Serializer = new XmlSerializer(typeof(List<Node>));
                            NodeList = (List<Node>)Serializer.Deserialize(Stream);
                        }
                        catch (SerializationException ex)
                        {
                            Debug.WriteLine("DeserializationException caught - NodeList");
                            Debug.WriteLine(string.Format("Source: {0}", ex.Source));
                            Debug.WriteLine(string.Format("Message: {0}", ex.Message));
                            Debug.WriteLine(string.Format("StackTrace: {0}", ex.StackTrace));
                            NodeList = new List<Node>();
                            iResult--;
                        }
                        finally
                        {
                            NodeListLock.ReleaseWriterLock();
                        }
                    }
                }
                #endregion

                #region KeyList
                if (!File.Exists(dataPath + Property.KeyListSerializaFileName) || !readData)
                {
                    KeyList = new List<Key>();
                }
                else
                {
                    using (FileStream Stream = new FileStream(dataPath + Property.KeyListSerializaFileName, FileMode.Open, FileAccess.Read))
                    {
                        try
                        {
                            KeyListLock.AcquireWriterLock(Timeout.Infinite);
                            XmlSerializer Serializer = new XmlSerializer(typeof(List<Key>));
                            KeyList = (List<Key>)Serializer.Deserialize(Stream);
                        }
                        catch (SerializationException ex)
                        {
                            Debug.WriteLine("DeserializationException caught - KeyList");
                            Debug.WriteLine(string.Format("Source: {0}", ex.Source));
                            Debug.WriteLine(string.Format("Message: {0}", ex.Message));
                            Debug.WriteLine(string.Format("StackTrace: {0}", ex.StackTrace));
                            KeyList = new List<Key>();
                            iResult--;
                        }
                        finally
                        {
                            KeyListLock.ReleaseWriterLock();
                        }
                    }
                }
                #endregion
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Exception caught (Data - DataDeserialize)");
                Debug.WriteLine(string.Format("Source: {0}", ex.Source));
                Debug.WriteLine(string.Format("Message: {0}", ex.Message));
                Debug.WriteLine(string.Format("StackTrace: {0}", ex.StackTrace));
                if (NodeList == null) NodeList = new List<Node>();
                if (KeyList == null) KeyList = new List<Key>();
                return -3;
            }

            return iResult;
        }

        /// <summary>
        /// データをシリアライズします。
        /// </summary>
        /// <returns>シリアライズ結果</returns>
        private int DataSerialize(string dataPath)
        {
            int iResult = 0;

            try
            {
                #region NodeList
                if (NodeList.Count > 0)
                {
                    using (FileStream Stream = new FileStream(dataPath + Property.NodeListSerializeFileName, FileMode.Create, FileAccess.Write))
                    {
                        try
                        {
                            NodeListLock.AcquireReaderLock(Timeout.Infinite);
                            XmlSerializer Serializer = new XmlSerializer(typeof(List<Node>));
                            Serializer.Serialize(Stream, NodeList);
                        }
                        catch (SerializationException ex)
                        {
                            Debug.WriteLine("SerializationException caught - NodeList");
                            Debug.WriteLine(string.Format("Source: {0}", ex.Source));
                            Debug.WriteLine(string.Format("Message: {0}", ex.Message));
                            Debug.WriteLine(string.Format("StackTrace: {0}", ex.StackTrace));
                            iResult--;
                        }
                        finally
                        {
                            NodeListLock.ReleaseReaderLock();
                        }
                    }
                }

                //if (NodeList.Count > Property.BinaryFormatThreshold / 2)
                //{
                //    using (FileStream Stream = new FileStream(Property.NodeListSerializeFileName_dat, FileMode.Create, FileAccess.Write))
                //    {
                //        try
                //        {
                //            NodeListLock.AcquireReaderLock(Timeout.Infinite);
                //            BinaryFormatter Formatter = new BinaryFormatter();
                //            Formatter.Serialize(Stream, NodeList);
                //            //XmlSerializer Serializer = new XmlSerializer(typeof(List<Node>));
                //            //Serializer.Serialize(Stream, NodeList);
                //        }
                //        catch (SerializationException ex)
                //        {
                //            Debug.WriteLine("SerializationException caught - NodeList");
                //            Debug.WriteLine(string.Format("Source: {0}", ex.Source));
                //            Debug.WriteLine(string.Format("Message: {0}", ex.Message));
                //            Debug.WriteLine(string.Format("StackTrace: {0}", ex.StackTrace));
                //            iResult--;
                //        }
                //        finally
                //        {
                //            NodeListLock.ReleaseReaderLock();
                //        }
                //    }
                //}
                #endregion

                #region KeyList
                if (KeyList.Count > 0)
                {
                    using (FileStream Stream = new FileStream(dataPath + Property.KeyListSerializaFileName, FileMode.Create, FileAccess.Write))
                    {
                        try
                        {
                            KeyListLock.AcquireReaderLock(Timeout.Infinite);
                            XmlSerializer Serializer = new XmlSerializer(typeof(List<Key>));
                            Serializer.Serialize(Stream, KeyList);
                        }
                        catch (SerializationException ex)
                        {
                            Debug.WriteLine("SerializationException caught - KeyList");
                            Debug.WriteLine(string.Format("Source: {0}", ex.Source));
                            Debug.WriteLine(string.Format("Message: {0}", ex.Message));
                            Debug.WriteLine(string.Format("StackTrace: {0}", ex.StackTrace));
                            iResult--;
                        }
                        finally
                        {
                            KeyListLock.ReleaseReaderLock();
                        }
                    }
                }

                //if (KeyList.Count > Property.BinaryFormatThreshold)
                //{
                //    using (FileStream Stream = new FileStream(Property.KeyListSerializaFileName_dat, FileMode.Create, FileAccess.Write))
                //    {
                //        try
                //        {
                //            KeyListLock.AcquireReaderLock(Timeout.Infinite);
                //            BinaryFormatter Formatter = new BinaryFormatter();
                //            Formatter.Serialize(Stream, KeyList);
                //            //XmlSerializer Serializer = new XmlSerializer(typeof(List<Key>));
                //            //Serializer.Serialize(Stream, KeyList);
                //        }
                //        catch (SerializationException ex)
                //        {
                //            Debug.WriteLine("SerializationException caught - KeyList");
                //            Debug.WriteLine(string.Format("Source: {0}", ex.Source));
                //            Debug.WriteLine(string.Format("Message: {0}", ex.Message));
                //            Debug.WriteLine(string.Format("StackTrace: {0}", ex.StackTrace));
                //            iResult--;
                //        }
                //        finally
                //        {
                //            KeyListLock.ReleaseReaderLock();
                //        }
                //    }
                //}
                #endregion
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Exception caught (Data - DataSerialize)");
                Debug.WriteLine(string.Format("Source: {0}", ex.Source));
                Debug.WriteLine(string.Format("Message: {0}", ex.Message));
                Debug.WriteLine(string.Format("StackTrace: {0}", ex.StackTrace));
            }

            return iResult;
        }

        /// <summary>
        /// Dataをシリアライズし、アップデートします。
        /// </summary>
        /// <param name="obj"></param>
        public void Update(string dataPath)
        {
            if (DataSerialize(dataPath) != 0)
            {
                Debug.WriteLine("Data Serialize Error");
            }
        }

        #region Data
        #region NodeList
        /// <summary>
        /// ノードリストの整合性を確認する。
        /// </summary>
        /// <param name="Node">ノード</param>
        /// <returns>整合性確認結果</returns>
        private bool NodeListConsistency(Node Node)
        {
            Node Remove = null;
            try
            {
                NodeListLock.AcquireReaderLock(Timeout.Infinite);
                foreach (Node node in NodeList)
                {
                    if (Node.EndPoint.Domain == node.EndPoint.Domain &&
                        Node.EndPoint.Port == node.EndPoint.Port &&
                        node.LineSpeed == 0)
                    {
                        Remove = node;
                        break;
                    }
                }
            }
            finally
            {
                NodeListLock.ReleaseReaderLock();
            }

            if (Remove != null)
            {
                try
                {
                    NodeListLock.AcquireWriterLock(Timeout.Infinite);
                    NodeList.Remove(Remove);
                }
                finally
                {
                    NodeListLock.ReleaseWriterLock();
                }
            }
            return true;
        }

        /// <summary>
        /// ノードを追加する。
        /// </summary>
        /// <param name="Node">ノード</param>
        public int AddNode(Node Node)
        {
            if (NodeListConsistency(Node))
            {
                try
                {
                    NodeListLock.AcquireWriterLock(Timeout.Infinite);
                    NodeList.Add(Node);
                }
                finally
                {
                    NodeListLock.ReleaseWriterLock();
                }
                return 0;
            }
            return -1;
        }

        /// <summary>
        /// ノードを削除する。
        /// </summary>
        /// <param name="Node">ノード</param>
        public void RemoveNode(Node Node)
        {
            try
            {
                NodeListLock.AcquireWriterLock(Timeout.Infinite);
                NodeList.Remove(Node);
            }
            finally
            {
                NodeListLock.ReleaseWriterLock();
            }
        }
        #endregion

        #region KeyList
        /// <summary>
        /// キーリストの整合性を確認する。
        /// </summary>
        /// <param name="Key">キーリスト</param>
        /// <returns>整合性確認結果</returns>
        private bool KeyListConsistency(Key Key)
        {
            try
            {
                KeyListLock.AcquireReaderLock(Timeout.Infinite);
                if (!KeyList.Exists(delegate(Key key)
                {
                    if (Key.Hash == key.Hash)
                    {
                        return true;
                    }
                    return false;
                }))
                {
                    return true;
                }
            }
            finally
            {
                KeyListLock.ReleaseReaderLock();
            }
            return false;
        }

        /// <summary>
        /// キーを追加する。
        /// </summary>
        /// <param name="Key">キー</param>
        public int AddKey(Key Key)
        {
            if (KeyListConsistency(Key))
            {
                try
                {
                    KeyListLock.AcquireWriterLock(Timeout.Infinite);
                    KeyList.Add(Key);
                }
                finally
                {
                    KeyListLock.ReleaseWriterLock();
                }
                return 0;
            }
            return -1;
        }

        /// <summary>
        /// キーを追加する。
        /// </summary>
        /// <param name="Keys">キーリスト</param>
        public int AddKey(List<Key> Keys)
        {
            List<Key> List = new List<Key>();
            foreach (Key Key in Keys)
            {
                if (KeyListConsistency(Key))
                {
                    List.Add(Key);
                }
            }

            try
            {
                KeyListLock.AcquireWriterLock(Timeout.Infinite);
                KeyList.AddRange(List);
            }
            finally
            {
                KeyListLock.ReleaseWriterLock();
            }
            return 0;
        }

        /// <summary>
        /// キーを削除する。
        /// </summary>
        /// <param name="Key">キー</param>
        public void RemoveKey(Key Key)
        {
            try
            {
                KeyListLock.AcquireWriterLock(Timeout.Infinite);
                KeyList.Remove(Key);
            }
            finally
            {
                KeyListLock.ReleaseWriterLock();
            }
        }

        #endregion
        #endregion

        public List<Key> GetKey(int iCount)
        {
            List<Key> List = new List<Key>();

            try
            {
                KeyListLock.AcquireReaderLock(Timeout.Infinite);
                for (int i = 1; i <= KeyList.Count && i <= iCount; i++)
                {
                    List.Add(KeyList[KeyList.Count - i]);
                }
            }
            finally
            {
                KeyListLock.ReleaseReaderLock();
            }

            return List;
        }

        public List<Key> GetKey(string sKeywords, string sTrip)
        {
            List<Key> List = null;

            string[] aosToken;

            if (sKeywords.StartsWith("%"))
            {
                //string sHash = sKeywords.Substring(1);
                //try
                //{
                //    KeyListLock.AcquireReaderLock(Timeout.Infinite);
                //    List = KeyList.FindAll(delegate(Key key)
                //    {
                //        if (sHash == key.Hash)
                //        {
                //            return true;
                //        }
                //        return false;
                //    });
                //}
                //finally
                //{
                //    KeyListLock.ReleaseReaderLock();
                //}
                List = new List<Key>();
            }
            else
            {

                if (sKeywords.StartsWith("\"") && sKeywords.EndsWith("\""))
                {
                    aosToken = new string[] { sKeywords.Replace("\"", "") };
                }
                else
                {
                    aosToken = sKeywords.Split(new char[] { ' ', '　' }, StringSplitOptions.RemoveEmptyEntries);
                }

                try
                {
                    KeyListLock.AcquireReaderLock(Timeout.Infinite);
                    List = KeyList.FindAll(delegate(Key key)
                    {
                        for (int i = 0; i < aosToken.Length; i++)
                        {
                            if (aosToken[i].StartsWith("-"))
                            {
                                if (key.Name.Contains(aosToken[i]))
                                {
                                    return false;
                                }
                            }
                            else
                            {
                                if (!key.Name.Contains(aosToken[i]))
                                {
                                    return false;
                                }
                            }
                        }
                        return true;
                    });
                }
                finally
                {
                    KeyListLock.ReleaseReaderLock();
                }

                if (!string.IsNullOrEmpty(sTrip))
                {
                    List = List.FindAll(delegate(Key key)
                    {
                        if (sTrip == key.Trip)
                        {
                            return true;
                        }
                        return false;
                    });
                }
            }

            return List;
        }
    }
}
