using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Globalization;

namespace NonaBGS.Journal
{
    public class JournalFile : IComparable<JournalFile>
    {
        private string fullpath = null;
        private string part = null;
        private int intpart = 0;
        private DateTime datetime;
        private DateTime normalised;
        private List<Entry> entries = new List<Entry>();

        private static Regex fileregex = new Regex("Journal\\.(\\d+)\\.(\\d+)\\.log");
        private static string iso8601 = "yyyyMMddTHHmmss";
        private static string iso8601_notime = "yyyyMMdd";

        private void SetFilename(string path) {
            string filename = Path.GetFileName(path);
            if (!File.Exists(path) || filename == null) {
                throw new JournalException(string.Format("Invalid journal file: {0}", filename));
            }

            var matches = fileregex.Matches(filename);
            if (matches.Count < 1) {
                throw new JournalException(string.Format("Invalid journal file: {0}", filename));
            }

            this.fullpath = path;
            this.part = matches[0].Groups[2].ToString();
            this.intpart = int.Parse(part);
            // The ISO is not quite correct on journal filenames, so build
            // a proper ISO 8601 date time stamp out of it.
            var date = new StringBuilder();
            date.Append("20"); // Add the missing century in front.
            date.Append(matches[0].Groups[1].ToString());
            date.Insert(8, "T"); // Add the "T" separating date and time
            this.datetime = DateTime.ParseExact(date.ToString(), iso8601, CultureInfo.InvariantCulture);
            this.normalised = DateTime.Parse(this.datetime.ToShortDateString());
        }

        public int CompareTo(JournalFile other) {
            if (datetime == null || other.datetime == null) {
                return 0;
            }
            var comp = DateTime.Compare(datetime, other.datetime);
            if (comp == 0) {
                /* If we have the exact same datetime we compare by parts.
                 */
                return intpart - other.intpart;
            } else {
                return comp;
            }
        }

        public JournalFile(string path) {
            SetFilename(path);
        }

        public DateTime Timestamp {
            get { return datetime; }
        }

        public DateTime NormalisedTimestamp {
            get { return normalised; }
        }

        public int Part {
            get { return intpart; }
        }

        public IEnumerable<Entry> Entries {
            get { 
                if (entries == null || entries.Count == 0) {
                    try {
                        LoadEntries();
                    } catch (IOException) {
                        entries = new List<Entry>();
                    }
                }
                return entries; 
            }
        }

        public void LoadEntries() {
            List<string> lines = new List<string>();

            /* This needs to be done this way, otherwise, if the game is still running the journal files cannot
             * be accessed. And it is very much convenient to generate logs while the game is still running.
             */
            using (var fs = new FileStream(this.fullpath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) {
                using (var sr = new StreamReader(fs, Encoding.UTF8)) {
                    string line = null;
                    while ((line = sr.ReadLine()) != null) {
                        lines.Add(line);
                    }
                }
            }

            entries.Clear();
            foreach(var line in lines) {
                Entry entry = Entry.Parse(line);
                entries.Add(entry);
            }
        }
    }
}