using MainShell.Common;
using MainShell.ProcessResult;
using MainShell.Recipe.Models;
using MW.WorkFlow;
using Stylet;
using System;

namespace MainShell.Process
{
    public class AutoProductionWorkflowBuilder
    {
        private readonly RecipeManager _recipeManager;
        private readonly ProcessResultManager _processResultManager;
        private readonly IWorkflowRuntimeTracker _workflowRuntimeTracker;
        private readonly IEventAggregator _eventAggregator;
        private readonly PreparationAreaService _preparationAreaService;
        private readonly CurrentChipStateService _currentChipStateService;
        private readonly AutoProductionRuntimeStateService _autoProductionRuntimeStateService;
        private readonly DieTransferMotionService _dieTransferMotionService;
        private readonly SubstratePositionMotionService _substratePositionMotionService;
        private readonly SubstrateHeightMeasureService _substrateHeightMeasureService;
        private readonly DiePositionService _diePositionService;
        private readonly DieRecheckService _dieRecheckService;

        public AutoProductionWorkflowBuilder(
            RecipeManager recipeManager,
            ProcessResultManager processResultManager,
            IWorkflowRuntimeTracker workflowRuntimeTracker,
            IEventAggregator eventAggregator,
            PreparationAreaService preparationAreaService,
            CurrentChipStateService currentChipStateService,
            AutoProductionRuntimeStateService autoProductionRuntimeStateService,
            DieTransferMotionService dieTransferMotionService,
            SubstratePositionMotionService substratePositionMotionService,
            SubstrateHeightMeasureService substrateHeightMeasureService,
            DiePositionService diePositionService,
            DieRecheckService dieRecheckService)
        {
            _recipeManager = recipeManager ?? throw new ArgumentNullException(nameof(recipeManager));
            _processResultManager = processResultManager ?? throw new ArgumentNullException(nameof(processResultManager));
            _workflowRuntimeTracker = workflowRuntimeTracker ?? throw new ArgumentNullException(nameof(workflowRuntimeTracker));
            _eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
            _preparationAreaService = preparationAreaService ?? throw new ArgumentNullException(nameof(preparationAreaService));
            _currentChipStateService = currentChipStateService ?? throw new ArgumentNullException(nameof(currentChipStateService));
            _autoProductionRuntimeStateService = autoProductionRuntimeStateService ?? throw new ArgumentNullException(nameof(autoProductionRuntimeStateService));
            _dieTransferMotionService = dieTransferMotionService ?? throw new ArgumentNullException(nameof(dieTransferMotionService));
            _substratePositionMotionService = substratePositionMotionService ?? throw new ArgumentNullException(nameof(substratePositionMotionService));
            _substrateHeightMeasureService = substrateHeightMeasureService ?? throw new ArgumentNullException(nameof(substrateHeightMeasureService));
            _diePositionService = diePositionService ?? throw new ArgumentNullException(nameof(diePositionService));
            _dieRecheckService = dieRecheckService ?? throw new ArgumentNullException(nameof(dieRecheckService));
        }

        public WorkflowStartRequest Create(string selectedFlowName)
        {
            WorkflowDefinition definition = CreateAutoProductionWorkflowDefinition();
            WorkflowContext context = CreateAutoWorkflowContext();
            string startStepId = ResolveRequestedStartStepId(selectedFlowName);
            return new WorkflowStartRequest(definition, context, startStepId);
        }

        private WorkflowContext CreateAutoWorkflowContext()
        {
            string resumeStepId;
            bool isResume = _processResultManager.TryGetResumeStepId(ProcessFlowName.AutoProduction, out resumeStepId);
            CurrentChipLifecycleState currentChipState = ResolveInitialChipState(_currentChipStateService.CurrentState, _preparationAreaService.CurrentStatus, resumeStepId, isResume);
            bool pendingChipLoad = ResolveInitialPendingChipLoad(currentChipState);
            SubstrateLifecycleState substrateProcessState = ResolveInitialSubstrateState(resumeStepId, isResume);

            _autoProductionRuntimeStateService.PrepareForRun(_recipeManager, currentChipState, isResume);

            WorkflowContext context = new WorkflowContext();
            context[WorkflowContextKeys.EventAggregator] = _eventAggregator;
            context[WorkflowContextKeys.RecipeManager] = _recipeManager;
            context[WorkflowContextKeys.ProcessResultManager] = _processResultManager;
            context[WorkflowContextKeys.WorkflowRuntimeTracker] = _workflowRuntimeTracker;
            context[WorkflowContextKeys.WorkflowName] = ProcessFlowName.AutoProduction;
            context[WorkflowContextKeys.PreparationAreaService] = _preparationAreaService;
            context[WorkflowContextKeys.CurrentChipStateService] = _currentChipStateService;
            context[WorkflowContextKeys.AutoProductionRuntimeStateService] = _autoProductionRuntimeStateService;
            context[WorkflowContextKeys.PreparationAreaStatus] = _preparationAreaService.CurrentStatus;
            context[WorkflowContextKeys.PendingChipLoad] = pendingChipLoad;
            context[WorkflowContextKeys.CurrentChipState] = currentChipState;
            context[WorkflowContextKeys.SubstrateProcessState] = substrateProcessState;
            context[WorkflowContextKeys.CurrentSubstratePendingCount] = _autoProductionRuntimeStateService.CurrentSubstratePendingCount;
            context[WorkflowContextKeys.CurrentSubstrateProcessedCount] = _autoProductionRuntimeStateService.CurrentSubstrateProcessedCount;
            context[WorkflowContextKeys.CurrentChipRemainingCount] = _autoProductionRuntimeStateService.CurrentChipRemainingCount;
            context[WorkflowContextKeys.TransferNeedsRecheck] = _autoProductionRuntimeStateService.TransferNeedsRecheck;
            context[WorkflowContextKeys.TransferChipExhausted] = _autoProductionRuntimeStateService.TransferChipExhausted;
            return context;
        }

        private static CurrentChipLifecycleState ResolveInitialChipState(CurrentChipLifecycleState currentChipState, ChipPreparationStatus preparationStatus, string resumeStepId, bool isResume)
        {
            if (!isResume)
            {
                return currentChipState;
            }

            if (currentChipState != CurrentChipLifecycleState.Empty)
            {
                return currentChipState;
            }

            if (preparationStatus == ChipPreparationStatus.Loading)
            {
                return CurrentChipLifecycleState.Loading;
            }

            if (preparationStatus == ChipPreparationStatus.Loaded && IsPreTransferOrSyncStep(resumeStepId))
            {
                return CurrentChipLifecycleState.LoadedPendingTransfer;
            }

            return currentChipState;
        }

        private static bool ResolveInitialPendingChipLoad(CurrentChipLifecycleState currentChipState)
        {
            return currentChipState == CurrentChipLifecycleState.Empty ||
                   currentChipState == CurrentChipLifecycleState.Loading;
        }

        private static SubstrateLifecycleState ResolveInitialSubstrateState(string resumeStepId, bool isResume)
        {
            if (!isResume || string.IsNullOrWhiteSpace(resumeStepId))
            {
                return SubstrateLifecycleState.NotLoaded;
            }

            if (string.Equals(resumeStepId, WorkflowStepIds.AutoProduction.SubstratePositionEntry, StringComparison.OrdinalIgnoreCase))
            {
                return SubstrateLifecycleState.Loaded;
            }

            if (string.Equals(resumeStepId, WorkflowStepIds.AutoProduction.SubstrateHeightMeasureEntry, StringComparison.OrdinalIgnoreCase))
            {
                return SubstrateLifecycleState.Positioned;
            }

            if (IsPreTransferOrSyncStep(resumeStepId))
            {
                return SubstrateLifecycleState.HeightMeasured;
            }

            if (string.Equals(resumeStepId, WorkflowStepIds.AutoProduction.ChipUnloadEntry, StringComparison.OrdinalIgnoreCase))
            {
                return SubstrateLifecycleState.InProcess;
            }

            if (string.Equals(resumeStepId, WorkflowStepIds.AutoProduction.SubstrateUnloadEntry, StringComparison.OrdinalIgnoreCase))
            {
                return SubstrateLifecycleState.Complete;
            }

            return SubstrateLifecycleState.NotLoaded;
        }

        private static bool IsPreTransferOrSyncStep(string resumeStepId)
        {
            return string.Equals(resumeStepId, WorkflowStepIds.AutoProduction.ChipPreparationSyncEntry, StringComparison.OrdinalIgnoreCase) ||
                   string.Equals(resumeStepId, WorkflowStepIds.AutoProduction.PreTransferValidationEntry, StringComparison.OrdinalIgnoreCase) ||
                   string.Equals(resumeStepId, WorkflowStepIds.AutoProduction.ChipStraighteningEntry, StringComparison.OrdinalIgnoreCase) ||
                   string.Equals(resumeStepId, WorkflowStepIds.AutoProduction.DiePositionEntry, StringComparison.OrdinalIgnoreCase) ||
                   string.Equals(resumeStepId, WorkflowStepIds.AutoProduction.DieTransferEntry, StringComparison.OrdinalIgnoreCase) ||
                   string.Equals(resumeStepId, WorkflowStepIds.AutoProduction.DieRecheckEntry, StringComparison.OrdinalIgnoreCase);
        }

        private WorkflowDefinition CreateAutoProductionWorkflowDefinition()
        {
            string substrateLoadStepId = WorkflowStepIdResolver.GetAutoProductionEntryStepId(ProcessFlowName.SubstrateLoadFlow);
            string substratePositionStepId = WorkflowStepIdResolver.GetAutoProductionEntryStepId(ProcessFlowName.SubstratePositionFlow);
            string substrateHeightMeasureStepId = WorkflowStepIdResolver.GetAutoProductionEntryStepId(ProcessFlowName.SubstrateHeightMeasureFlow);
            string chipPreparationSyncStepId = WorkflowStepIdResolver.GetAutoProductionEntryStepId(ProcessFlowName.ChipPreparationSyncFlow);
            string preTransferValidationStepId = WorkflowStepIdResolver.GetAutoProductionEntryStepId(ProcessFlowName.PreTransferValidationFlow);
            string waferAngleAdjustmentStepId = WorkflowStepIdResolver.GetAutoProductionEntryStepId(ProcessFlowName.ChipStraighteningFlow);
            string diePositionStepId = WorkflowStepIdResolver.GetAutoProductionEntryStepId(ProcessFlowName.DiePositionFlow);
            string dieTransferStepId = WorkflowStepIdResolver.GetAutoProductionEntryStepId(ProcessFlowName.DieTransferFlow);
            string dieRecheckStepId = WorkflowStepIdResolver.GetAutoProductionEntryStepId(ProcessFlowName.DieRecheckFlow);
            string chipUnloadStepId = WorkflowStepIdResolver.GetAutoProductionEntryStepId(ProcessFlowName.ChipUnloadFlow);
            string substrateUnloadStepId = WorkflowStepIdResolver.GetAutoProductionEntryStepId(ProcessFlowName.SubstrateUnloadFlow);

            WorkflowDefinition definition = new WorkflowDefinition(substrateLoadStepId);

            definition.AddStep(
                new WorkflowStep(
                        substrateLoadStepId,
                        new CompositeActivity(
                            ProcessFlowName.SubstrateLoadFlow,
                            new IActivity[]
                            {
                                new PreparationSignalActivity(ProcessFlowName.PreparationSignalFlow),
                                new ChipPreparationAutoLoadStartActivity(ProcessFlowName.ChipPreparationAutoLoadStartFlow),
                                new SubstrateLoadActivity(ProcessFlowName.SubstrateLoadFlow)
                            }))
                    .WithFlowName(ProcessFlowName.SubstrateLoadFlow)
                    .WithNextStep(substratePositionStepId));

            definition.AddStep(CreateFlowStep(
                substratePositionStepId,
                ProcessFlowName.SubstratePositionFlow,
                new SubstratePositionActivity(ProcessFlowName.SubstratePositionFlow, _substratePositionMotionService),
                substrateHeightMeasureStepId));

            definition.AddStep(
                new WorkflowStep(
                        substrateHeightMeasureStepId,
                        new SubstrateHeightMeasureActivity(ProcessFlowName.SubstrateHeightMeasureFlow, _substrateHeightMeasureService))
                    .WithFlowName(ProcessFlowName.SubstrateHeightMeasureFlow)
                    .WithNextStep(chipPreparationSyncStepId));

            definition.AddStep(CreateFlowStep(
                chipPreparationSyncStepId,
                ProcessFlowName.ChipPreparationSyncFlow,
                new ChipPreparationSyncActivity(ProcessFlowName.ChipPreparationSyncFlow),
                preTransferValidationStepId));

            definition.AddStep(CreateFlowStep(
                preTransferValidationStepId,
                ProcessFlowName.PreTransferValidationFlow,
                new PreTransferValidationActivity(ProcessFlowName.PreTransferValidationFlow),
                waferAngleAdjustmentStepId));

            definition.AddStep(CreateFlowStep(
                waferAngleAdjustmentStepId,
                ProcessFlowName.ChipStraighteningFlow,
                new WaferAngleAdjustmentActivity(ProcessFlowName.ChipStraighteningFlow),
                diePositionStepId));

            definition.AddStep(CreateFlowStep(
                diePositionStepId,
                ProcessFlowName.DiePositionFlow,
                new DiePositionActivity(ProcessFlowName.DiePositionFlow, _diePositionService),
                dieTransferStepId));

            definition.AddStep(CreateDieTransferStep(dieTransferStepId, waferAngleAdjustmentStepId, dieRecheckStepId, chipUnloadStepId, substrateUnloadStepId));

            definition.AddStep(CreateFlowStep(
                dieRecheckStepId,
                ProcessFlowName.DieRecheckFlow,
                new DieRecheckActivity(ProcessFlowName.DieRecheckFlow, _dieRecheckService),
                dieTransferStepId));

            definition.AddStep(
                new WorkflowStep(
                        chipUnloadStepId,
                        new ChipUnloadActivity(ProcessFlowName.ChipUnloadFlow))
                    .WithFlowName(ProcessFlowName.ChipUnloadFlow)
                    .AddJumpOnRouteValueOnSuccess(WorkflowContextKeys.AutoProductionRoute, AutoProductionRoute.BothExhausted, substrateUnloadStepId)
                    .WithNextStep(substrateLoadStepId));

            definition.AddStep(
                new WorkflowStep(
                        substrateUnloadStepId,
                        new SubstrateUnloadActivity(ProcessFlowName.SubstrateUnloadFlow))
                    .WithFlowName(ProcessFlowName.SubstrateUnloadFlow)
                    .WithNextStep(substrateLoadStepId));

            return definition;
        }

        private WorkflowStep CreateDieTransferStep(string stepId, string continueStepId, string recheckStepId, string chipUnloadStepId, string unloadStepId)
        {
            return new WorkflowStep(stepId, CreateDieTransferExecutionActivity())
                .WithFlowName(ProcessFlowName.DieTransferFlow)
                .AddJumpOnRouteValueOnSuccess(WorkflowContextKeys.AutoProductionRoute, AutoProductionRoute.Recheck, recheckStepId)
                .AddJumpOnRouteValueOnSuccess(WorkflowContextKeys.AutoProductionRoute, AutoProductionRoute.ContinueCurrentSubstrate, continueStepId)
                .AddJumpOnRouteValueOnSuccess(WorkflowContextKeys.AutoProductionRoute, AutoProductionRoute.ChipExhausted, chipUnloadStepId)
                .AddJumpOnRouteValueOnSuccess(WorkflowContextKeys.AutoProductionRoute, AutoProductionRoute.BothExhausted, chipUnloadStepId)
                .AddJumpOnRouteValueOnSuccess(WorkflowContextKeys.AutoProductionRoute, AutoProductionRoute.SubstrateComplete, unloadStepId)
                .WithNextStep(unloadStepId);
        }

        private IActivity CreateDieTransferExecutionActivity()
        {
            return new CompositeActivity(
                ProcessFlowName.DieTransferFlow,
                new IActivity[]
                {
                    new DieTransferActivity(ProcessFlowName.DieTransferFlow, _dieTransferMotionService),
                    new ChipPreparationConsumeActivity("ChipLoadConsume")
                });
        }

        private static WorkflowStep CreateFlowStep(string stepId, string flowName, IActivity activity, string defaultNextStepId)
        {
            WorkflowStep step = new WorkflowStep(stepId, activity)
                .WithFlowName(flowName);

            if (!string.IsNullOrWhiteSpace(defaultNextStepId))
            {
                step.WithNextStep(defaultNextStepId);
            }

            return step;
        }

        private string ResolveRequestedStartStepId(string selectedFlowName)
        {
            if (_processResultManager.TryGetResumeStepId(ProcessFlowName.AutoProduction, out var _))
            {
                return null;
            }

            return WorkflowStepIdResolver.GetAutoProductionEntryStepId(selectedFlowName);
        }
    }
}
