using System;
using System.Collections.Generic;
using System.Diagnostics.SymbolStore;
using System.Linq;
using System.Net.Sockets;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace TELTest {
    public class QuizCreator {
        public QuizCreator() {
            DraftID = null;
            Password = Utilities.RandomString(15);
        }
        bool savedChanges = false;
        protected QuizContainer GetContainer() {
            QuizContainer qc = new QuizContainer();
            qc.Title = Title;
            qc.Author = Author;
            qc.Created = DateTime.Now;
            qc.Guid = Guid.NewGuid();
            qc.Password = Password;
            qc.Questions = Questions.ToArray();
            qc.Visibility = Visibility;
            return qc;
        }
        public void Save() {
            var qc = GetContainer();
            bool doNotSave = true;
            doNotSave &= string.IsNullOrWhiteSpace(Title);
            doNotSave &= string.IsNullOrWhiteSpace(Author);
            foreach (var item in Questions) {
                doNotSave &= string.IsNullOrWhiteSpace(item.Content);
                foreach (var item2 in item.Answers) {
                    doNotSave &= string.IsNullOrWhiteSpace(item2.Content);
                }
            }
            if (doNotSave) {
                if (!string.IsNullOrWhiteSpace(DraftID)) QuizDraftManager.DeleteDraft(DraftID);
                return;
            }
            if (DraftID == null) {
                DraftID = QuizDraftManager.SaveNewDraft(qc);
            } else {
                QuizDraftManager.SaveDraft(qc, DraftID);
                if (isPublished) {
                    QuizListing.UpdateEntry(DraftID, Title, Visibility);
                }
            }
            savedChanges = true;
        }
        public async Task Handle(ConnectionHandler handler, NetworkStream stream) {
            Handler = handler;
            Stream = stream;
            string[][] menu = new string[2][];
            int xsel = 0;
            int ysel = 0;
            Questions.Add(new QuizQuestion() { Answers = new[] { new QuizQuestionAnswer() } });
            while (true) {
                menu[0] = new[] {
                    /* 0 */ "ID Kopii Roboczej/Quizu: " + DraftID,
                    /* 1 */ "Tytul quizu: " + Title,
                    /* 2 */ "Autor quizu: " + Author,
                    /* 3 */ "Liczba pytan: " + Questions.Count,
                    /* 4 */ "Widocznosc: " + Visibility,
                    /* 5 */ "Haslo: " + Password,
                    /* 6 */ "Wyprobuj....",
                    /* 7 */ "Publikuj/udostepnij...",
                    "Powrot..."
                };
                if (menu.Length != Questions.Count + 1)
                    Array.Resize(ref menu, Questions.Count + 1);
                
                for (int i = 0; i < Questions.Count; i++) {
                    var q = Questions[i];
                    menu[1 + i] = new string[4 + q.Answers.Length];
                    menu[1 + i][0] = "Usun...";
                    menu[1 + i][1] = "Dodaj kolejne...";
                    menu[1 + i][2] = "Pytanie: " + q.Content;
                    for (int j = 0; j < q.Answers.Length; j++) {
                        menu[1 + i][3 + j] = (q.Answers[j].Correct? "[P] " : "") + "Odpowiedz " + (j + 1) + ": " + q.Answers[j].Content;
                    }
                    menu[1 + i][menu[1 + i].Length - 1] = "Dodaj odpowiedz...";
                    
                }
                List<string> labels = new List<string>();
                labels.Add("Menu");
                for (int i = 0; i < Questions.Count; i++) {
                    labels.Add("Pytanie " + (i + 1));
                }
                var sel = await GUIUtilities.Create2DMenu(handler, stream, menu, labels.ToArray(), defaultx: xsel, defaulty: ysel);
                xsel = sel.Item1;
                ysel = sel.Item2;
                if (xsel == 0) {
                    switch (ysel) {
                        case 0:
                            await OpenDraft();
                            break;
                        case 1:
                            Title = await Promptfull("Tytul quizu");
                            break;
                        case 2:
                            Author = await Promptfull("Autor quizu");
                            break;
                        case 4:
                            Visibility += 1;
                            if (Visibility > Visibility.Public) Visibility = 0;
                            break;
                        case 6: { // Try
                                QuizContainer qc = GetContainer();
                                BaseQuizContext bc = new BaseQuizContext(Handler, Stream, qc);
                                await bc.Handle();
                                break;
                            }
                        case 7:
                            await Publish();
                            break;
                        default:
                            break;
                    }
                    Save();
                    if (ysel == menu[0].Length - 1) break;
                } else {
                    int qn = xsel - 1;
                    switch (ysel) {
                        case 0:
                            bool del = await AreYouSure("Na pewno usunac?");
                            if (del) {
                                if (Questions.Count == 1) {
                                    Questions[0] = new QuizQuestion() { Answers = new QuizQuestionAnswer[] { new QuizQuestionAnswer() } };
                                } else {
                                    Questions.RemoveAt(qn);
                                    if (xsel <= qn + 1) xsel--;
                                }
                                Save();
                            }

                            break;
                        case 1:
                            Questions.Insert(qn + 1, new QuizQuestion() { Answers = new QuizQuestionAnswer[] { new QuizQuestionAnswer() } });
                            Save();
                            xsel = qn + 2;
                            break;
                        case 2: {
                                var tmp = Questions[qn];
                                tmp.Content = await Promptfull("Pytanie", tmp.Content, 128);
                                Questions[qn] = tmp;
                                Save();
                                break;
                            }
                        default: {
                                if (ysel == menu[xsel].Length - 1) {
                                    var arr = Questions[qn].Answers;
                                    Array.Resize(ref arr, arr.Length + 1);
                                    arr[arr.Length - 1] = new QuizQuestionAnswer();
                                    var tmpq = Questions[qn];
                                    tmpq.Answers = arr;
                                    Questions[qn] = tmpq;
                                    ysel++;
                                    Save();
                                } else {
                                    var tmp = Questions[qn].Answers[ysel - 3];
                                    var ans = await ProcAnswer(tmp);
                                    if (ans == null) {
                                        var l = Questions[qn].Answers.ToList();
                                        l.RemoveAt(ysel - 3);
                                        var tmpq = Questions[qn];
                                        tmpq.Answers = l.ToArray();
                                        Questions[qn] = tmpq;
                                        break;
                                    }
                                    tmp = ans.Value;
                                    Questions[qn].Answers[ysel - 3] = tmp;
                                    Save();
                                }
                                break; 
                            }
                    }

                }

            }
            await Task.Delay(1);
        }
        public async Task Publish() {
            if (string.IsNullOrWhiteSpace(DraftID)) {
                await Handler.TryClear(Stream);
                await Handler.WriteASCII(Stream, "Musisz najpierw cokolwiek dodac.\r\n\r\nNacisnij klawisz aby powrocic do edytora quizow...");
                await Handler.ReadChar(Stream);
                Handler.Flush(Stream);
                return;
            }
            await Handler.TryClear(Stream);
            await Handler.WriteASCII(Stream, "Po opublikowaniu quizu bedziesz potrzebowac zarowno jego ID jak hasla aby go edytowac.\r\n\r\n");
            await Handler.WriteASCII(Stream, "Widocznosc tego quizu to: \"" + Visibility+ "\".\r\n");
            switch (Visibility) {
                case Visibility.Private:
                    await Handler.WriteASCII(Stream, "Oznacza to, ze bedzie wymagane jego ID aby w niego zagrac.\r\n");
                    break;
                case Visibility.Public:
                    await Handler.WriteASCII(Stream, "Oznacza to, ze bedzie on widoczny w eksploratorze quizow dla wszystkich.\r\n");
                    break;
                default:
                    await Handler.WriteASCII(Stream, "Blad: Nieznana widocznosc.\r\n");
                    break;
            }
            bool yes = await AreYouSure("Czy na pewno chcesz kontynuowac?");
            if (yes) QuizListing.Insert(DraftID, Title, Visibility);
        }
        protected ConnectionHandler Handler { get; set; }
        protected NetworkStream Stream { get; set; }
        public async Task<QuizQuestionAnswer?> ProcAnswer(QuizQuestionAnswer current) {
            int ysel = 0;
            while (true) {
                string[][] menu = new string[1][];
                menu[0] = new[] {
                    "Tresc: " + current.Content,
                    "Poprawna? " + (current.Correct? "Tak" : "Nie"),
                    "Powrot...",
                    "Usun..."
                };

                var res = await GUIUtilities.Create2DMenu(Handler, Stream, menu, new[] { "Pytanie" }, defaulty: ysel);
                ysel = res.Item2;
                switch (ysel) {
                    case 0:
                        current.Content = await Promptfull("Tresc");
                        break;
                    case 1:
                        current.Correct = !current.Correct;
                        break;
                    case 2:
                        goto BreakLoop;
                    case 3:
                        if (await AreYouSure("Usunac odpowiedz?")) {
                            return null;
                        }
                        break;
                    default:
                        break;
                }
            }
        BreakLoop:
            return current;
        }
        public async Task<bool> AreYouSure(string prompt, bool nodef = false, bool deffalse = true) {
            var c = await Handler.Choice(Stream, new[] { 'Y', 'N' }, prompt, def: (nodef ? (char?)null : (deffalse ? 'N' : 'Y')));
            Handler.Flush(Stream);
            return c == 'Y';
        }
        bool isPublished = false;
        public async Task<string> OpenDraft() {
            isPublished = false;
            await Handler.TryClear(Stream);
            await Handler.WriteASCII(Stream, "Wpisz ID kopii roboczej lub quizu aby do nigo powrocic. Obecny quiz jest rowniez zapisany jako kopia robocza. Jezeli quiz jest opublikowany lub udostepniony bedziesz potrzebowac hasla aby go edytowac.");
            string ask = await Promptfull("ID", noClear: true);

            var d = QuizDraftManager.GetDraft(ask);
            if (d != null) {
                var lst = QuizListing.Contains(ask);
                if (lst) {
                    while (true) {
                        await Handler.TryClear(Stream);
                        await Handler.WriteASCII(Stream, "Wpisz haslo: ");
                        ReadConfiguration rc = ReadConfiguration.Default;
                        rc.EchoPasswordChars = true;
                        string pwd = await Handler.Read(Stream, rc);
                        if (d.Password == pwd) break;
                        if (string.IsNullOrWhiteSpace(pwd)) return null;
                    }
                    isPublished = true;
                }
                Title = d.Title;
                Author = d.Author;
                Password = d.Password;
                DraftID = ask;
                Questions = d.Questions.ToList();
                Visibility = d.Visibility;
            }
            if (string.IsNullOrWhiteSpace(ask)) return DraftID;

            return ask;
        }
        public async Task<string> Promptfull(string prompt, string @default = null, int nMax = 32, bool noClear = false) {
            if (!noClear)
                await Handler.TryClear(Stream);

            await Handler.WriteASCII(Stream, "\r\n" + prompt + ": ");
            var res = await Handler.Read(Stream, new ReadConfiguration() {
                Echo = true,
                MaxLength = nMax,
                FlushAfterReading = true,
                Terminator = '\r',
                Whitelist = Utilities.ALPHANUMERIC + " ?,.{}[]()/\\:;'\"'\b"
            });
            return string.IsNullOrWhiteSpace(res) ? @default : res;
        }
        public List<QuizQuestion> Questions { get; set; } = new List<QuizQuestion>();
        public string DraftID { get; set; }
        public string Title { get; set; }
        public string Author { get; set; }
        public string Password { get; set; }
        public Visibility Visibility { get; set; }
    }
    public enum Visibility {
        Private,
        Public
    }
}
