using System;
using System.IO;
using ZeroLevel.Collections;
using ZeroLevel.Services.Cache;
using ZeroLevel.Services.FileSystem;
using ZeroLevel.Services.Memory;

namespace ZeroLevel.Services.PartitionStorage
{
    internal sealed class PhisicalFileAccessorCachee
        : IDisposable
    {
        private readonly TimerCachee<ParallelFileReader> _indexReadersCachee;
        private readonly TimerCachee<ParallelFileReader> _dataReadersCachee;

        private readonly ConcurrentHashSet<string> _lockedFiles = new ConcurrentHashSet<string>();

        public PhisicalFileAccessorCachee(TimeSpan dataExpirationPeriod, TimeSpan indexExpirationPeriod)
        {
            _dataReadersCachee = new TimerCachee<ParallelFileReader>(dataExpirationPeriod, s => new ParallelFileReader(s), i => i.Dispose(), 8192);
            _indexReadersCachee = new TimerCachee<ParallelFileReader>(indexExpirationPeriod, s => new ParallelFileReader(s), i => i.Dispose(), 8192);
        }

        #region DATA
        public void DropDataReader(string filePath)
        {
            _dataReadersCachee.Drop(filePath);
        }

        private ParallelFileReader GetDataReader(string filePath)
        {
            if (File.Exists(filePath) == false)
                throw new FileNotFoundException(filePath);
            return _dataReadersCachee.Get(filePath);
        }
        public IViewAccessor GetDataAccessor(string filePath, long offset)
        {
            if (false == _lockedFiles.Contains(filePath))
            {
                var reader = GetDataReader(filePath);
                try
                {
                    return reader.GetAccessor(offset);
                }
                catch (ObjectDisposedException)
                {
                    _dataReadersCachee.Drop(filePath);
                    reader = _dataReadersCachee.Get(filePath);
                }
                return reader.GetAccessor(offset);
            }
            return null!;
        }

        public IViewAccessor GetDataAccessor(string filePath, long offset, int length)
        {
            if (false == _lockedFiles.Contains(filePath))
            {
                var reader = GetDataReader(filePath);
                try
                {
                    return reader.GetAccessor(offset, length);
                }
                catch (ObjectDisposedException)
                {
                    _dataReadersCachee.Drop(filePath);
                    reader = _dataReadersCachee.Get(filePath);
                }
                return reader.GetAccessor(offset, length);
            }
            return null!;
        }
        public void DropAllDataReaders()
        {
            _dataReadersCachee.DropAll();
        }
        #endregion

        #region Indexes
        public void DropIndexReader(string filePath)
        {
            _indexReadersCachee.Drop(filePath);
        }

        private ParallelFileReader GetIndexReader(string filePath)
        {
            if (File.Exists(filePath) == false)
                throw new FileNotFoundException(filePath);
            return _indexReadersCachee.Get(filePath);
        }
        public IViewAccessor GetIndexAccessor(string filePath, long offset)
        {
            if (false == _lockedFiles.Contains(filePath))
            {
                var reader = GetIndexReader(filePath);
                try
                {
                    return reader.GetAccessor(offset);
                }
                catch (ObjectDisposedException)
                {
                    _indexReadersCachee.Drop(filePath);
                    reader = _indexReadersCachee.Get(filePath);
                }
                return reader.GetAccessor(offset);
            }
            return null!;
        }

        public IViewAccessor GetIndexAccessor(string filePath, long offset, int length)
        {
            if (false == _lockedFiles.Contains(filePath))
            {
                var reader = GetIndexReader(filePath);
                try
                {
                    return reader.GetAccessor(offset, length);
                }
                catch (ObjectDisposedException)
                {
                    _indexReadersCachee.Drop(filePath);
                    reader = _indexReadersCachee.Get(filePath);
                }
                return reader.GetAccessor(offset, length);
            }
            return null!;
        }
        public void DropAllIndexReaders()
        {
            _indexReadersCachee.DropAll();
        }
        #endregion

        public void LockFile(string filePath)
        {
            _lockedFiles.Add(filePath);
            DropDataReader(filePath);
            DropIndexReader(filePath);
        }

        public void UnlockFile(string filePath) 
        {
            _lockedFiles.TryRemove(filePath);
        }

        public void Dispose()
        {
            _lockedFiles.Clear();
            _dataReadersCachee.Dispose();
            _indexReadersCachee.Dispose();
        }
    }
}
