using CsQuery.ExtensionMethods;
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using System.Threading;

namespace VCommon.Logging.Viewer.Utility
{
    public sealed class FileSizeWatcher
    {
        private Timer _timer;
        private int IntervalMs { get; }
        private ConcurrentDictionary<FileInfo, long> Watchings { get; } = new ConcurrentDictionary<FileInfo, long>();

        public event Action<FileSizeWatcherEventArgs> Changed;

        public FileSizeWatcher(FileInfo[] watchFileInfos, int intervalMs = 1500)
        {
            if (watchFileInfos == null || watchFileInfos.Length == 0)
            {
                throw new ArgumentException(nameof(watchFileInfos));
            }
            watchFileInfos.ForEach(AddFileWatch);
            IntervalMs = intervalMs;
            EnableWatch();
        }

        public void AddFileWatch(FileInfo fileInfo) => Watchings.TryAdd(fileInfo, fileInfo.Length);

        public void AddFileWatch(string fullPath) => AddFileWatch(new FileInfo(fullPath));

        public void RemoveFileWatch(string fullPath) => Watchings.TryRemove(Watchings.FirstOrDefault(c => c.Key.FullName == fullPath).Key, out var _);

        public void RemoveAllFileWatch() => Watchings.Clear();

        private void EnableWatch()
        {
            _timer = new Timer(state =>
            {
                _timer.Change(Timeout.Infinite, Timeout.Infinite);
                foreach (var fileInfo in Watchings.ToArray())
                {
                    var fullName = fileInfo.Key.FullName;
                    IntPtr handle = NativeMethods.CreateFile(fullName, 0x80000000, 3, 0, 3, 0x00000001, 0);
                    NativeMethods.GetFileSizeEx(handle, out var fileSize);
                    if (fileSize == fileInfo.Value) continue;
                    var fileWatchSizeEventArgs = new FileSizeWatcherEventArgs
                    {
                        FullPath = fullName,
                        OriginalLength = fileInfo.Value,
                        ChangeSizeType = fileSize > fileInfo.Value ? ChangeSizeType.Increase : ChangeSizeType.Reduce
                    };
                    Watchings[fileInfo.Key] = fileSize;
                    Changed?.Invoke(fileWatchSizeEventArgs);
                }
                _timer.Change(IntervalMs, IntervalMs);
            }, null, IntervalMs, IntervalMs);
        }
    }

    public class FileSizeWatcherEventArgs : EventArgs
    {
        public string FullPath { get; set; }
        public long OriginalLength { get; set; }
        public ChangeSizeType ChangeSizeType { get; set; }
    }

    public enum ChangeSizeType : byte
    {
        /// <summary>
        /// 增加
        /// </summary>
        Increase,

        /// <summary>
        /// 减少
        /// </summary>
        Reduce
    }
}