using Fuel.Payment.Core;
using Fuel.Payment.Core.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Fuel.Payment.Service.Factory
{
    public class AsyncPaymentProcessorFactory : AbstractPaymentProcessorFactory<Task<GenericProcessResponse>>
    {
        private readonly ReaderWriterLockSlim locker = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
        private readonly List<Tuple<Func<ElectronicOrderModel, bool>, IPaymentProcessor<ElectronicOrderModel, Task<GenericProcessResponse>>>> cache
            = new List<Tuple<Func<ElectronicOrderModel, bool>, IPaymentProcessor<ElectronicOrderModel, Task<GenericProcessResponse>>>>();
        private static readonly AsyncPaymentProcessorFactory instance = new AsyncPaymentProcessorFactory();
        public static AsyncPaymentProcessorFactory Default { get { return instance; } }
        public override void Regist(Func<ElectronicOrderModel, bool> predict, IPaymentProcessor<ElectronicOrderModel, Task<GenericProcessResponse>> processor)
        {
            this.locker.EnterWriteLock();
            try
            {
                this.cache.Add(
                    new Tuple<Func<ElectronicOrderModel, bool>, IPaymentProcessor<ElectronicOrderModel, Task<GenericProcessResponse>>>(predict, processor));
            }
            finally
            {
                this.locker.ExitWriteLock();
            }
        }

        public override IEnumerator<IPaymentProcessor<ElectronicOrderModel, Task<GenericProcessResponse>>> GetEnumerator()
        {
            this.locker.EnterReadLock();
            try
            {
                return this.cache.Select(c => c.Item2).GetEnumerator();
            }
            finally
            {
                this.locker.ExitReadLock();
            }
        }

        public override IPaymentProcessor<ElectronicOrderModel, Task<GenericProcessResponse>> Get(ElectronicOrderModel order)
        {
            this.locker.EnterWriteLock();
            try
            {
                return this.cache.Last(p => p.Item1(order)).Item2;
            }
            finally
            {
                this.locker.ExitWriteLock();
            }
        }
    }
}
