using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using TweetLib.Core.Serialization;
using TweetLib.Core.Serialization.Converters;

namespace TweetDuck.Management.Analytics {
	[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Local")]
	sealed class AnalyticsFile {
		private static readonly FileSerializer<AnalyticsFile> Serializer = new FileSerializer<AnalyticsFile>();

		static AnalyticsFile() {
			Serializer.RegisterTypeConverter(typeof(DateTime), new SingleTypeConverter<DateTime> {
				ConvertToString = value => value.ToBinary().ToString(),
				ConvertToObject = value => DateTime.FromBinary(long.Parse(value))
			});

			Serializer.RegisterTypeConverter(typeof(Counter), new SingleTypeConverter<Counter> {
				ConvertToString = value => value.Value.ToString(),
				ConvertToObject = value => int.Parse(value)
			});
		}

		public static readonly AnalyticsFile Dummy = new AnalyticsFile();

		// STATE PROPERTIES

		public DateTime LastDataCollection  { get; set; } = DateTime.MinValue;
		public string LastCollectionVersion { get; set; } = null;
		public string LastCollectionMessage { get; set; } = null;

		// USAGE DATA

		public Counter OpenOptions { get; private set; } = 0;
		public Counter OpenPlugins { get; private set; } = 0;
		public Counter OpenAbout   { get; private set; } = 0;
		public Counter OpenGuide   { get; private set; } = 0;

		public Counter DesktopNotifications { get; private set; } = 0;
		public Counter SoundNotifications   { get; private set; } = 0;
		public Counter NotificationMutes    { get; private set; } = 0;

		public Counter BrowserContextMenus           { get; private set; } = 0;
		public Counter BrowserExtraMouseButtons      { get; private set; } = 0;
		public Counter NotificationContextMenus      { get; private set; } = 0;
		public Counter NotificationExtraMouseButtons { get; private set; } = 0;
		public Counter NotificationKeyboardShortcuts { get; private set; } = 0;

		public Counter BrowserReloads   { get; private set; } = 0;
		public Counter CopiedUsernames  { get; private set; } = 0;
		public Counter ViewedImages     { get; private set; } = 0;
		public Counter DownloadedImages { get; private set; } = 0;
		public Counter DownloadedVideos { get; private set; } = 0;
		public Counter UsedROT13        { get; private set; } = 0;

		public Counter TweetScreenshots { get; private set; } = 0;
		public Counter TweetDetails     { get; private set; } = 0;
		public Counter VideoPlays       { get; private set; } = 0;

		// END OF DATA

		public event EventHandler PropertyChanged;

		private readonly string file;

		private AnalyticsFile(string file) {
			this.file = file;
		}

		private AnalyticsFile() {
			this.file = null;
		}

		private void SetupProperties() {
			foreach (Counter counter in GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(prop => prop.PropertyType == typeof(Counter)).Select(prop => (Counter) prop.GetValue(this))) {
				counter.SetOwner(this);
			}
		}

		private void OnPropertyChanged() {
			PropertyChanged?.Invoke(this, EventArgs.Empty);
		}

		public void Save() {
			if (file == null) {
				return;
			}

			try {
				Serializer.Write(file, this);
			} catch (Exception e) {
				Program.Reporter.HandleException("Analytics File Error", "Could not save the analytics file.", true, e);
			}
		}

		public static AnalyticsFile Load(string file) {
			AnalyticsFile config = new AnalyticsFile(file);

			try {
				Serializer.ReadIfExists(file, config);
			} catch (Exception e) {
				Program.Reporter.HandleException("Analytics File Error", "Could not open the analytics file.", true, e);
			}

			config.SetupProperties();
			return config;
		}

		public interface IProvider {
			AnalyticsFile AnalyticsFile { get; }
		}

		public sealed class Counter {
			public int Value { get; private set; }

			private AnalyticsFile owner;

			private Counter(int value) {
				this.Value = value;
			}

			public void SetOwner(AnalyticsFile owner) {
				this.owner = owner;
			}

			public void Trigger() {
				++Value;
				owner?.OnPropertyChanged();
			}

			public static implicit operator int(Counter counter) => counter.Value;
			public static implicit operator Counter(int value) => new Counter(value);
		}
	}
}