using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TELTest.Gamemodes {
    internal class Comments : QuizGamemode {
        static Comments() {
            bk.MaximumMemoryPressure = 512 * 1024 * 1024; // 512MB
            UpdateCDB();
        }
        static void UpdateCDB() {
            tmr.Interval = 15000;
            tmr.Elapsed += Tmr_Elapsed;
            tmr.AutoReset = false;
        }
        private static void Retime() {
            tmr.Stop();
            tmr.Start();
        }
        public Comments() {
            
        }
        public Comments(QuizContainer container, BaseQuizContext context) : base(container, context) {
            
        }
        private static System.Timers.Timer tmr = new System.Timers.Timer();
        private static void Tmr_Elapsed(Object sender, System.Timers.ElapsedEventArgs e) {
            Program.WriteWithTime("Begin write comments...");
            bk.SavePendingChanges();
            Program.WriteWithTime("Done. {0} tuples out, ~{1}B memory in use.", bk.LastTuplesOut, bk.CurrentMemoryPressure);
        }
        static BasicKeyValue bk = new BasicKeyValue("Comments");

        public override String Name => "Komentarze";

        public override String SystemName => "Comments";

        public override String Description => "Zostaw komentarz pod quizem.";

        public override QuizGamemode CreateInstance(QuizContainer quiz, BaseQuizContext context) => new Comments(quiz, context);
        public override async Task Begin() {
            int score = 0;
            await WriteLine("Tytul quizu: " + Quiz.Title);
            await WriteLine("Autor quizu: " + Quiz.Author);
            await WriteLine();
            var g = Quiz.Guid.ToString();

            List<string> cms = new List<string>();
            recreate = true;
            int lastx = 0;
            int lasty = 2;
            while (true) {
                if (recreate) {
                    cms.Clear();
                    DeserializeComs();
                    cms.Add("Tytul quizu: " + Quiz.Title);
                    cms.Add("Autor quizu: " + Quiz.Author);
                    cms.Add("Powrot...");
                    cms.Add("Dodaj...");
                    for (int i = 0; i < comments.Count; i++) {
                        var item = comments[i];
                        cms.Add("[" + item.Date + "] " + item.Author + ": " + item.Content);
                    }
                    recreate = false;
                }
                var outp = await GUIUtilities.Create2DMenu(Context.Handler, Context.Stream, new string[][] { cms.ToArray() }, new[] { "Komentarze" }, null, lastx, lasty);
                if (outp.Item2 == 2) break;

                if (outp.Item2 == 3) {
                    await GUIUtilities.AskUsernameIfNeeded(Context.Handler, Context.Stream);
                    await WriteComment();
                }
                lastx = outp.Item1;
                lasty = outp.Item2;
            }
        }
        private bool recreate = false;
        protected void DeserializeComs() {
            comments.Clear();
            Byte[] com = null;
            lock (bk) com = bk[Quiz.Guid.ToString()];
            DeserializeCommentsInner(com);
            comments.Reverse();
        }
        protected void DeserializeCommentsInner(byte[] data) {
            if (data == null || data.Length < 4) return;
            using (MemoryStream ms = new MemoryStream(data)) {
                using (BinaryReader br = new BinaryReader(ms)) {
                    while (true) {
                        var al = br.ReadInt32();
                        if (al == 0x7FFFFFFF) break;
                        Comment com = new Comment();
                        var bt = br.ReadBytes(al);
                        com.Author = DecStr(bt);
                        al = br.ReadInt32();
                        bt = br.ReadBytes(al);
                        com.Content = DecStr(bt);
                        int y = br.ReadInt32();
                        int m = br.ReadInt32();
                        int d = br.ReadInt32();
                        int h = br.ReadInt32();
                        int mn = br.ReadInt32();
                        int s = br.ReadInt32();
                        com.Date = new DateTime(y, m, d, h, mn, s);
                        comments.Add(com);
                    }
                }
            }
        }
        List<Comment> comments = new List<Comment>();
        protected byte[] SerializeComments() {
            MemoryStream ms = new MemoryStream();
            BinaryWriter bw = new BinaryWriter(ms);
            for (int i = 0; i < comments.Count; i++) {
                var item = comments[i];
                var aut = EncStr(item.Author);
                bw.Write(aut.Length);
                bw.Write(aut);
                var cont = EncStr(item.Content);
                bw.Write(cont.Length);
                bw.Write(cont);
                bw.Write(item.Date.Year);
                bw.Write(item.Date.Month);
                bw.Write(item.Date.Day);
                bw.Write(item.Date.Hour);
                bw.Write(item.Date.Minute);
                bw.Write(item.Date.Second);
            }
            bw.Write(0x7FFFFFFF);
            return ms.ToArray();
        }
        protected static byte[] EncStr(string str) {
            return System.Text.Encoding.UTF8.GetBytes(str);
        }
        protected static string DecStr(byte[] str) {
            return System.Text.Encoding.UTF8.GetString(str);
        }
        async Task WriteComment() {
            var res = await GUIUtilities.Promptfull(Context.Handler, Context.Stream, "Komentarz", null, 96);
            if (string.IsNullOrWhiteSpace(res)) return;
            comments.Add(new Comment() {
                Author = Context.Handler.PlayerName,
                Content = res,
                Date = DateTime.Now,
            });
            var b = SerializeComments();
            lock (bk)
                bk[Quiz.Guid.ToString()] = b;
            Retime();
            recreate = true;
        }
        struct Comment {
            public DateTime Date;
            public string Author;
            public string Content;
        }
    }
}
