using BackupEssentials.Backup.History;
using BackupEssentials.Sys;
using BackupEssentials.Utils;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.IO;
using System.Linq;

namespace BackupEssentials.Backup.Data{
    class DataStorage{
        public enum Type{
            Locations, History
        }

        private static ScheduledUpdate SaveTimer;
        private static bool IsSetupForSaving = false;

        private static Dictionary<Type,bool> LoadedData = new Dictionary<Type,bool>();

        public static readonly ObservableCollection<BackupLocation> BackupLocationList = new ObservableCollection<BackupLocation>(new List<BackupLocation>(8));
        public static readonly ChangeTracker BackupLocationListTracker = new ChangeTracker();
        public static readonly ObservableCollection<HistoryEntry> HistoryEntryList = new ObservableCollection<HistoryEntry>(new LinkedList<HistoryEntry>());
        public static readonly ChangeTracker HistoryEntryListTracker = new ChangeTracker();

        public class ChangeTracker{
            public bool Changed;
        }

        static NotifyCollectionChangedEventHandler Tracker(ChangeTracker tracker, bool scheduled){
            return new NotifyCollectionChangedEventHandler((sender, args) => { tracker.Changed = true; if (scheduled)Save(); });
        }

        /// <summary>
        /// Run this to allow data saving (loading is available automatically).
        /// </summary>
        public static void SetupForSaving(bool scheduled){
            IsSetupForSaving = true;

            BackupLocationList.CollectionChanged += Tracker(BackupLocationListTracker,scheduled);
            HistoryEntryList.CollectionChanged += Tracker(HistoryEntryListTracker,scheduled);

            foreach(Type type in Enum.GetValues(typeof(Type))){
                LoadedData[type] = false;
            }
            
            if (scheduled){
                SaveTimer = ScheduledUpdate.Forever(10,() => {
                    Save(true);
                });

                SaveTimer.Start();
            }
        }

        static bool ShouldLoad(Type[] types, Type type){
            return types.Length == 0 || types.Contains(type);
        }

        public static void Load(params Type[] types){
            if (ShouldLoad(types,Type.Locations)){
                LoadedData[Type.Locations] = true;

                FileUtils.ReadFile("DS.Locations.dat",FileMode.Open,(line) => {
                    if (line.Length == 0)return;
                    BackupLocation loc = new BackupLocation();
                    StringDictionarySerializer.FromString(loc,line);
                    BackupLocationList.Add(loc);
                });
            }

            if (ShouldLoad(types,Type.History)){
                LoadedData[Type.History] = true;

                FileUtils.ReadFile("DS.History.dat",FileMode.Open,(line) => {
                    if (line.Length == 0)return;
                    HistoryEntry entry = new HistoryEntry();
                    StringDictionarySerializer.FromString(entry,line);
                    HistoryEntryList.Add(entry);
                });
            }
        }

        public static void Save(){
            Save(false);
        }

        public static void Save(bool force){
            if (!IsSetupForSaving)throw new NotSupportedException(Settings.Default.Language["General.Storage.ErrorSaving"]);
            else if (SaveTimer == null && !force)throw new NotSupportedException(Settings.Default.Language["General.Storage.ErrorScheduledSaving"]);

            if (!force){
                SaveTimer.NeedsUpdate = true;
                return;
            }

            if (BackupLocationListTracker.Changed && LoadedData[Type.Locations]){
                BackupLocationListTracker.Changed = false;

                FileUtils.WriteFile("DS.Locations.dat",FileMode.Create,(writer) => {
                    foreach(BackupLocation loc in BackupLocationList)writer.WriteLine(StringDictionarySerializer.ToString(loc));
                });
            }

            if (HistoryEntryListTracker.Changed && LoadedData[Type.History]){
                HistoryEntryListTracker.Changed = false;

                FileUtils.WriteFile("DS.History.dat",FileMode.Create,(writer) => {
                    foreach(HistoryEntry entry in HistoryEntryList)writer.WriteLine(StringDictionarySerializer.ToString(entry));
                });
            }
        }
    }
}