using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using PhilExampleCrawler.Common.Models;
using PhilExampleCrawler.Common.TCP.Enums;
using PhilExampleCrawler.Common.TCP.Interface;
using PhilExampleCrawler.Common.TCP.Packets;

namespace PhilExampleCrawler.Common.TCP.Utils
{
    public static class PacketSerializer
    {
        public static byte[] Serialize(this BasePacket packet)
        {
            using (MemoryStream m = new MemoryStream())
            {
                using (BinaryWriter writer = new BinaryWriter(m))
                {
                    writer.Write((int)packet.LoadData.Type);
                    switch (packet.LoadData.Type)
                    {
                        //User - GETs
                        case LoadType.RegisterUserSessionLoad:
                            WriteRegisterUserLoad(writer, (RegisterUserSessionLoad)packet.LoadData);
                            break;
                        case LoadType.RegisterUserSessionOKLoad:
                            WriteRegisterUserOKLoad(writer, (RegisterUserSessionOKLoad)packet.LoadData);
                            break;

                        //Insertion
                        case LoadType.NewInsertionLoad:
                            WriteNewInsertionLoad(writer, (NewInsertionLoad)packet.LoadData);
                            break;
                        default:
                            throw new FormatException("PacketSerializer/Serialize Exception: packet LoadType not found.");
                    }
                }
                return m.ToArray();
            }
        }

        public static BasePacket Deserialize(this byte[] packet)
        {
            using (MemoryStream m = new MemoryStream(packet))
            {
                using (BinaryReader reader = new BinaryReader(m))
                {
                    var bp = new BasePacket();
                    LoadType loadType = (LoadType)reader.ReadInt32();
                    switch (loadType)
                    {
                        //User
                        case LoadType.RegisterUserSessionLoad:
                            bp.LoadData = ReadRegisterUserLoad(reader);
                            break;
                        case LoadType.RegisterUserSessionOKLoad:
                            bp.LoadData = ReadRegisterUserOKLoad(reader);
                            break;

                        //Insertion
                        case LoadType.NewInsertionLoad:
                            bp.LoadData = ReadNewInsertionLoad(reader);
                            break;
                        default:
                            throw new FormatException("packet LoadType not found.");
                    }
                    return bp;
                }
            }
            throw new InvalidOperationException("Deserialize could not properly create a stream.");
        }



        #region Write
        private static void WriteRegisterUserLoad(BinaryWriter writer, RegisterUserSessionLoad loadData)
        {
            writer.Write(loadData.AuthCode);
        }

        private static void WriteRegisterUserOKLoad(BinaryWriter writer, RegisterUserSessionOKLoad loadData)
        {
            writer.Write(loadData.ForAuthCode);
            writer.Write(ToDTString(loadData.ValidUntil));
        }

        private static void WriteNewInsertionLoad(BinaryWriter writer, NewInsertionLoad loadData)
        {
            writer.Write(loadData.Href);
        }

        /*
        private static void WriteGetUserLoad(BinaryWriter writer, GetUserLoad loadData)
        {
            writer.Write(loadData.AuthCode);
        }

        private static void WriteReturnUserLoad(BinaryWriter writer, ReturnUserLoad loadData)
        {
            if (loadData.User != null)
            {
                writer.Write(loadData.User.ID);
                writer.Write(loadData.User.AuthCode);
                writer.Write(ToDTString(loadData.User.CreateDate));
            }
        }

        private static void WriteRegisterCrawlSessionLoad(BinaryWriter writer, RegisterCrawlSessionLoad loadData)
        {
            writer.Write(loadData.UserID);
            writer.Write(loadData.CrawlSearchParams.KeyWords);
            writer.Write(loadData.CrawlSearchParams.LocationStr);
            writer.Write(loadData.CrawlSearchParams.CategoryID);
            writer.Write(loadData.CrawlSearchParams.Radius);
        }

        private static void WriteGetCrawlSessionsLoad(BinaryWriter writer, GetCrawlSessionsLoad loadData)
        {
            writer.Write(loadData.UserID);
        }
        */
        #endregion


        #region Read
        private static RegisterUserSessionLoad ReadRegisterUserLoad(BinaryReader reader)
        {
            return new RegisterUserSessionLoad()
            {
                AuthCode = reader.ReadString()
                
            };
        }

        private static RegisterUserSessionOKLoad ReadRegisterUserOKLoad(BinaryReader reader)
        {
            return new RegisterUserSessionOKLoad()
            {
                ForAuthCode = reader.ReadString(),
                ValidUntil = ToDT(reader.ReadString())
            };
        }

        private static NewInsertionLoad ReadNewInsertionLoad(BinaryReader reader)
        {
            return new NewInsertionLoad()
            {
                Href = reader.ReadString()
            };
        }

        /*
        private static GetUserLoad ReadGetUserLoad(BinaryReader reader)
        {
            return new GetUserLoad()
            {
                AuthCode = reader.ReadString()
            };
        }

        private static ReturnUserLoad ReadReturnUserLoad(BinaryReader reader)
        {
            return new ReturnUserLoad()
            {
                User = new User(reader.ReadInt32(),
                                reader.ReadString(),
                                ToDT(reader.ReadString()))
            };
        }

        private static RegisterCrawlSessionLoad ReadRegisterCrawlSessionLoad(BinaryReader reader)
        {
            return new RegisterCrawlSessionLoad()
            {
                UserID = reader.ReadInt32(),
                CrawlSearchParams = new CrawlSearchParams(keywords: reader.ReadString(),
                                                          locationStr: reader.ReadString(),
                                                          categoryID: reader.ReadInt32(),
                                                          radius: reader.ReadInt32())
            };
        }

        private static GetCrawlSessionsLoad ReadGetCrawlSessionsLoad(BinaryReader reader)
        {
            return new GetCrawlSessionsLoad()
            {
                UserID = reader.ReadInt32()
            };
        }
        */
        #endregion


        private static string ToDTString(DateTime dt) => dt.ToString("ddMMyyyyHHmmss");
        private static DateTime ToDT(string dt) => new DateTime(year: int.Parse(dt.Substring(4, 4)),
                                                                month: int.Parse(dt.Substring(2, 2)),
                                                                day: int.Parse(dt.Substring(0, 2)),
                                                                hour: int.Parse(dt.Substring(8, 2)),
                                                                minute: int.Parse(dt.Substring(10, 2)),
                                                                second: int.Parse(dt.Substring(12, 2)));
    }
}
