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

namespace TELTest {
    public class Scoreboard {
        public Scoreboard(string fileName) {
            FileName = fileName;
            if (File.Exists(fileName)) {
                ReloadAllScores();
            }
            t.Interval = 15000;
            t.Elapsed += this.T_Elapsed;
            t.AutoReset = false;
        }

        private void T_Elapsed(Object sender, ElapsedEventArgs e) {
            Save();
        }

        public string FileName { get; }
        private readonly object _lock = new object();
        private void ReloadAllScores() {
            string[] lines = null;
            lock (_lock) lines = File.ReadAllLines(FileName);
            lock (m_Entries)
                foreach (var item in lines) {
                    var i = ScoreboardEntry.Deserialize(item);
                    if (i.Name != null) m_Entries.Add(i);
                }
        }
        public void Save() {
            lock (_lock) { 
                File.WriteAllLines(FileName, m_Entries.Select(x=>x.Serialize()));
            }
        }
        public void Add(ScoreboardEntry entry) {
            lock (m_Entries)
                m_Entries.Add(entry);
            t.Stop();
            t.Start();
        }
        public ScoreboardEntry[] Highscore(int count = 15) {
            lock(m_Entries)
                return m_Entries.OrderBy(x => -x.Value).ToList().GetRange(0, count < m_Entries.Count ? count : m_Entries.Count).ToArray();
        }
        public ScoreboardEntry[] Lowscore(int count = 15) {
            lock (m_Entries)
                return m_Entries.OrderBy(x => x.Value).ToList().GetRange(0, count < m_Entries.Count ? count : m_Entries.Count).ToArray();
        }
        System.Timers.Timer t = new System.Timers.Timer();

        private List<ScoreboardEntry> m_Entries = new List<ScoreboardEntry>();
        public IReadOnlyList<ScoreboardEntry> Entries { get { return m_Entries; } }
        public struct ScoreboardEntry {
            public ScoreboardEntry(string name, int value) {
                this.Date = DateTime.Now;
                this.Name = name;
                this.Value = value;
            }
            public DateTime Date;
            public string Name;
            public int Value;
            const char SEP_CHAR = '|';
            public string Serialize() {
                return Name + SEP_CHAR + Value + SEP_CHAR + Date.ToString("O", System.Globalization.CultureInfo.InvariantCulture);
            }
            public static ScoreboardEntry Deserialize(string str) {
                if (str.IndexOf(SEP_CHAR) == -1) return default;
                string[] s = str.Split(SEP_CHAR);
                string name = s[0];
                int value = int.MinValue;
                DateTime dt = DateTime.MinValue;
                if (int.TryParse(s[1], out int p)) {
                    value = p;
                }
                if (s.Length > 2) {
                    dt = DateTime.ParseExact(s[2], "O", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.RoundtripKind);
                }
                return new ScoreboardEntry(name, value) { Date = dt };
            }
        }
        public static Scoreboard Of(string name) {
            lock (Scoreboards) {
                if (!Directory.Exists("Hiscores"))
                    lock (_dirLock) 
                        Directory.CreateDirectory("Hiscores");
                if (Scoreboards.ContainsKey(name)) return Scoreboards[name];
                Scoreboard s = new Scoreboard(System.IO.Path.Combine("Hiscores", name + ".txt"));
                Scoreboards.Add(name, s);
                return s;
            }
        }
        private static object _dirLock = new object();
        public static Dictionary<string, Scoreboard> Scoreboards { get; set; } = new Dictionary<string, Scoreboard>();
    }
}
