using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using EHF;
using Valg.Eva.Skanning.Scan.Presentation.ScanInProgress;
using Valg.EVA.Skanning.Scan.ReadSoftContracts.Events;
using Prism.Events;
using Valg.EVA.Skanning.Configuration;
using Valg.EVA.Skanning.Diagnostics;
using Valg.EVA.Skanning.UserInterfaceElements.ApplicationTermination;

namespace Valg.EVA.Skanning.Scan.Api
{
    public class ScanApplication
    {
        private readonly IEHF_IScanApp scanApp;
        private readonly IEventAggregator eventAggregator;
        private readonly ScannedFormMapper scannedFormMapper;
        private ApplicationTerminatingEvent _applicationTerminatingEvent;
        private ScanJobRequestedStoppedEvent _scanJobRequestedStoppedEvent;
        private ScanJobRequestedStartedEvent _scanJobRequestedStartedEvent;
        private readonly IApplicationConfigurationReader applicationConfigurationReader;
        
        

        public ScanApplication(IEHF_IScanApp scanApp, IEventAggregator eventAggregator, ScannedFormMapper scannedFormMapper, IApplicationConfigurationReader applicationConfigurationReader)
        {
            this.applicationConfigurationReader = applicationConfigurationReader;
            this.scanApp = scanApp;
            this.eventAggregator = eventAggregator;
            this.scannedFormMapper = scannedFormMapper;

            var terminationEvent = eventAggregator.GetEvent<ApplicationTerminatingEvent>();
            terminationEvent.Subscribe(args => CloseApplication());

            eventAggregator.GetEvent<SaveDataFailedEvent>().Subscribe(args => StopJob());
        }

        public void CloseApplication()
        {
            scanApp.CloseApp();
        }

        public void StartJob(ScanJobRequestedStartedEventArgs scanJobRequestedStartedEventArgs)
        {
            var fullPathToJobFile = GetTemplatesDirectory();

            if (scanApp.LoadJob(fullPathToJobFile) != 0)
            {
                throw new Exception($"Job file could not be loaded: {fullPathToJobFile}");
            }
            scanApp.StartJob();
        }

        public void StopJob()
        {
            var jobStoppedReturnCode = -1;
            var sleepIntervalBetweenRetryAttempt = 200;
            var totalAllotedTimeForStopAttempts = 10000;
            var accumulatedStopAttemptTime = 0;
            while (jobStoppedReturnCode != 0 && accumulatedStopAttemptTime < totalAllotedTimeForStopAttempts)
            {
                jobStoppedReturnCode = scanApp.StopJob();
                if (jobStoppedReturnCode != 0)
                {
                    EvaSkanningEventSource.Log.ReadsoftJobAttemptedStopWithFailure(jobStoppedReturnCode);
                    accumulatedStopAttemptTime += sleepIntervalBetweenRetryAttempt;
                    System.Threading.Thread.Sleep(sleepIntervalBetweenRetryAttempt);
                }
            }
            

        }

        public void SubscribeToReadsoftEvents(IReadSoftApi readsoftEventConsumer)
        {
            if (scanApp.Subscribe(readsoftEventConsumer, "ScanBatchDialogShow", "OnScanBatchDialogShow") == -1)
                throw new Exception("ScanBatchDialogShow was not subscribed");
            if (scanApp.Subscribe(readsoftEventConsumer, "ScanBeforeSaveDlg", nameof(IReadSoftApi.OnScanBeforeSaveDlg)) == -1)
                throw new Exception("ScanBeforeSaveDlg was not subscribed");
            if (scanApp.Subscribe(readsoftEventConsumer, "ScanNotifyUser", nameof(IReadSoftApi.OnScanNotifyUser)) == -1)
                throw new Exception("ScanNotifyUser was not subscribed");
            if (scanApp.Subscribe(readsoftEventConsumer, "FormScanEnd", nameof(IReadSoftApi.OnFormScanEnd)) == -1)
                throw new Exception("FormScanEnd was not subscribed");
            if (scanApp.Subscribe(readsoftEventConsumer, "AppError", nameof(IReadSoftApi.OnAppError)) == -1)
                throw new Exception("AppError was not subscribed");
            if (scanApp.Subscribe(readsoftEventConsumer, "JobStarted", nameof(IReadSoftApi.OnJobStarted)) == -1)
                throw new Exception("JobStarted was not subscribed");
            if (scanApp.Subscribe(readsoftEventConsumer, "JobStopped", nameof(IReadSoftApi.OnJobStopped)) == -1)
                throw new Exception("JobStopped was not subscribed");
        }

        public void SubscribeToApplicationEvents()
        {
            
            _applicationTerminatingEvent = eventAggregator.GetEvent<ApplicationTerminatingEvent>();
            _applicationTerminatingEvent.Subscribe(args => CloseApplication(), ThreadOption.PublisherThread, true);

            _scanJobRequestedStoppedEvent = eventAggregator.GetEvent<ScanJobRequestedStoppedEvent>();
            _scanJobRequestedStoppedEvent.Subscribe(args => StopJob(), ThreadOption.PublisherThread, true);

            _scanJobRequestedStartedEvent = eventAggregator.GetEvent<ScanJobRequestedStartedEvent>();
            _scanJobRequestedStartedEvent.Subscribe(StartJob, ThreadOption.PublisherThread, true);

            
            
        }

        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        public void SetBatchSize()
        {
            scanApp.BatchDef.Size = 10000;
            scanApp.SetReturnValue(0, "1");
        }

        public void SetReturnValueToNo()
        {
            scanApp.SetReturnValue(0, "No");
        }

        public void MapAndSaveCurrentForm()
        {
            IEHF_IForm form = null;

            try
            {
                form = scanApp.Form;

                var eventArgs = new ScanFormFinishedEventArgs();
                eventArgs.Form = scannedFormMapper.MapScannedForm(form);
                var scanFormFinishedEvent = eventAggregator.GetEvent<ScanFormFinishedEvent>();
                scanFormFinishedEvent.Publish(eventArgs);
            }
            finally
            {
                if (form != null)
                {
                    form.Delete(); 
                    if (form.GetType().IsCOMObject)
                    {
                        Marshal.ReleaseComObject(form);
                    }
                    form = null;
                }
            }
        }

        private string GetTemplatesDirectory()
        {
            var location = new Uri(Assembly.GetEntryAssembly().GetName().CodeBase);
            var inf = new FileInfo(location.LocalPath);
            return Path.Combine(inf.Directory.FullName, "Templates", applicationConfigurationReader.GetAppSetting("JobPackageName"), $"{applicationConfigurationReader.GetAppSetting("JobPackageName")}.JOB");
        }
    }
}
