using RTSEngine.Attack;
using RTSEngine.Entities;
using RTSEngine.Health;
using RTSEngine.ResourceExtension;
using RTSEngine.EntityComponent;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using RTSEngine.Animation;
using RTSEngine.Demo;
using RTSEngine.BuildingExtension;
using System.IO;
using System;
using RTSEngine.Model;

namespace RTSEngine.EditorOnly
{
    [System.Serializable]
    public struct TransformList
    {
        public List<Transform> transforms;
    }

    [System.Serializable]
    public struct ProgressObject
    {
        public string comp;
        public Transform obj;
    }

    // Demo

    [System.Serializable]
    public struct CropState
    {
        public List<Transform> show;
        public List<Transform> hide;
    }

    [System.Serializable]
    public class EntityFields
    {
        public string code;
        public GameObject entityModel;

        public List<Transform> destroyStateShow;
        public List<Transform> destroyStateHide;

        public List<TransformList> healthStatesShow;
        public List<TransformList> healthStatesHide;

        public EntityFields(Entity entity)
        {
            code = entity.Code;

            var entitySO = new SerializedObject(entity);

            entityModel = (entitySO.FindProperty("model").objectReferenceValue as MonoBehaviour)?.gameObject;
            if(entityModel == null)
            {
                return;
            }

            UnityEngine.GameObject.DestroyImmediate(entity.GetComponentInChildren<EntityModelConnections>(), true);

            var healthSO = new SerializedObject(entity.GetComponent<EntityHealth>());

            if (true)
            {
                destroyStateShow = new List<Transform>();
                destroyStateHide = new List<Transform>();
                var destroyState = healthSO.FindProperty("destroyState");

                var showList = destroyState.FindPropertyRelative("showChildObjects");
                for (int j = 0; j < showList.arraySize; j++)
                {
                    var objProp = showList.GetArrayElementAtIndex(j).FindPropertyRelative("obj");
                    destroyStateShow.Add((objProp.objectReferenceValue as Transform));
                }

                var hideList = destroyState.FindPropertyRelative("hideChildObjects");
                for (int j = 0; j < hideList.arraySize; j++)
                {
                    var objProp = hideList.GetArrayElementAtIndex(j).FindPropertyRelative("obj");
                    destroyStateHide.Add((objProp.objectReferenceValue as Transform));
                }
            }

            if(true)
            {
                healthStatesShow = new List<TransformList>();
                healthStatesHide = new List<TransformList>();
                var states = healthSO.FindProperty("states");
                for (int i = 0; i < states.arraySize; i++)
                {
                    healthStatesShow.Add(new TransformList() { transforms = new List<Transform>() });
                    healthStatesHide.Add(new TransformList() { transforms = new List<Transform>() });

                    var state = states.GetArrayElementAtIndex(i);
                    var showList = state.FindPropertyRelative("showChildObjects");
                    for (int j = 0; j < showList.arraySize; j++)
                    {
                        var objProp = showList.GetArrayElementAtIndex(j).FindPropertyRelative("obj");
                        healthStatesShow[i].transforms.Add((objProp.objectReferenceValue as Transform));
                    }

                    var hideList = state.FindPropertyRelative("hideChildObjects");
                    for (int j = 0; j < hideList.arraySize; j++)
                    {
                        var objProp = hideList.GetArrayElementAtIndex(j).FindPropertyRelative("obj");
                        healthStatesHide[i].transforms.Add((objProp.objectReferenceValue as Transform));
                    }
                }
            }
        }
    }

    [System.Serializable]
    public class ResourceFields : EntityFields
    {
        public List<Transform> workerPositions;

        public List<Transform> collectedStateShow;
        public List<Transform> collectedStateHide;

        // Demo
        public Animator chestAnimator;

        public ResourceFields(Resource resource) : base(resource)
        {
            workerPositions = new List<Transform>();

            var workerSO = new SerializedObject(resource.GetComponentInChildren<ResourceWorkerManager>());
            var workerProp = workerSO.FindProperty("workerPositions");
            for (int i = 0; i < workerProp.arraySize; i++)
            {
                var objProp = workerProp.GetArrayElementAtIndex(i).FindPropertyRelative("obj");
                workerPositions.Add(objProp.objectReferenceValue as Transform);
            }

            var healthSO = new SerializedObject(resource.GetComponent<ResourceHealth>());

            if (true)
            {
                collectedStateShow = new List<Transform>();
                collectedStateHide = new List<Transform>();
                var collectedProp = healthSO.FindProperty("collectedState");

                var showList = collectedProp.FindPropertyRelative("showChildObjects");
                for (int j = 0; j < showList.arraySize; j++)
                {
                    var objProp = showList.GetArrayElementAtIndex(j).FindPropertyRelative("obj");
                    collectedStateShow.Add((objProp.objectReferenceValue as Transform));
                }

                var hideList = collectedProp.FindPropertyRelative("hideChildObjects");
                for (int j = 0; j < hideList.arraySize; j++)
                {
                    var objProp = hideList.GetArrayElementAtIndex(j).FindPropertyRelative("obj");
                    collectedStateHide.Add((objProp.objectReferenceValue as Transform));
                }
            }

            // Demo
            if(resource.GetComponentInChildren<OpenTreasureHandler>())
            {
                chestAnimator = new SerializedObject(resource.GetComponentInChildren<OpenTreasureHandler>()).FindProperty("animator.obj").objectReferenceValue as Animator;
            }
        }
    }

    [System.Serializable]
    public class FactionEntityFields : EntityFields
    {
        public List<Renderer> coloredRenderers;

        public Transform attackTargetPosition;
        public Transform attackTargetPositionExternal;

        public Transform dropOffPosition;

        public List<ProgressObject> progressObjects;

        public List<Transform> toggableObjects;
        public List<Transform> attackLaunchPositions;
        public List<Transform> delayParentObjects;

        public List<Transform> carrierAdd;
        public List<Transform> carrierSlots;
        public List<Transform> carrierEject;

        public Transform creatorSpawn;
        public Transform rallypoint;

        // Fog of War
        public List<Transform> sameVisibility;

        public FactionEntityFields(FactionEntity factionEntity) : base(factionEntity)
        {
            if (true)
            {
                this.coloredRenderers = new List<Renderer>();
                var entitySO = new SerializedObject(factionEntity);
                var coloredRenderersProp = entitySO.FindProperty("coloredRenderers");
                for (int i = 0; i < coloredRenderersProp.arraySize; i++)
                {
                    var objProp = coloredRenderersProp.GetArrayElementAtIndex(i).FindPropertyRelative("renderer.obj");
                    coloredRenderers.Add(objProp.objectReferenceValue as Renderer);
                }
            }

            attackTargetPosition = new SerializedObject(factionEntity.GetComponent<FactionEntityHealth>()).FindProperty("attackTargetPosition.obj").objectReferenceValue as Transform;
            if (factionEntity.GetComponentInChildren<FactionEntityAttackTargetGetter>())
                attackTargetPositionExternal = new SerializedObject(factionEntity.GetComponentInChildren<FactionEntityAttackTargetGetter>()).FindProperty("attackTargetPosition.obj").objectReferenceValue as Transform;

            if(factionEntity.GetComponentInChildren<DropOffTarget>())
            {
                dropOffPosition = (new SerializedObject(factionEntity.GetComponentInChildren<DropOffTarget>())).FindProperty("dropOffPosition.obj").objectReferenceValue as Transform;
            }

            if (true)
            {
                toggableObjects = new List<Transform>();
                attackLaunchPositions = new List<Transform>();
                delayParentObjects = new List<Transform>();

                foreach (FactionEntityAttack attackComp in factionEntity.GetComponentsInChildren<FactionEntityAttack>())
                {
                    var SO = new SerializedObject(attackComp);
                    var toggableObejctsProp = SO.FindProperty("weapon.toggableObjects");
                    for (int i = 0; i < toggableObejctsProp.arraySize; i++)
                    {
                        var objProp = toggableObejctsProp.GetArrayElementAtIndex(i).FindPropertyRelative("obj");
                        toggableObjects.Add(objProp.objectReferenceValue as Transform);
                    }

                    var prop = SO.FindProperty("launcher.sources");
                    for (int i = 0; i < prop.arraySize; i++)
                    {
                        var launchPositionProp = prop.GetArrayElementAtIndex(i).FindPropertyRelative("launchPosition.obj");
                        attackLaunchPositions.Add(launchPositionProp.objectReferenceValue as Transform);

                        var delayParentObjectProp = prop.GetArrayElementAtIndex(i).FindPropertyRelative("delayParentObject.obj");
                        delayParentObjects.Add(delayParentObjectProp.objectReferenceValue as Transform);
                    }
                }
            }

            if(true)
            {
                progressObjects = new List<ProgressObject>();

                foreach(FactionEntityTargetProgressComponent<IEntity> comp in factionEntity.GetComponentsInChildren<FactionEntityTargetProgressComponent<IEntity>>())
                {
                    progressObjects.Add(new ProgressObject
                    {
                        comp = comp.Code,
                        obj = new SerializedObject(comp).FindProperty("inProgressObject.obj").objectReferenceValue as Transform
                    });
                }
                foreach(FactionEntityTargetProgressComponent<IFactionEntity> comp in factionEntity.GetComponentsInChildren<FactionEntityTargetProgressComponent<IFactionEntity>>())
                {
                    progressObjects.Add(new ProgressObject
                    {
                        comp = comp.Code,
                        obj = new SerializedObject(comp).FindProperty("inProgressObject.obj").objectReferenceValue as Transform
                    });
                }
                foreach(FactionEntityTargetProgressComponent<IResource> comp in factionEntity.GetComponentsInChildren<FactionEntityTargetProgressComponent<IResource>>())
                {
                    progressObjects.Add(new ProgressObject
                    {
                        comp = comp.Code,
                        obj = new SerializedObject(comp).FindProperty("inProgressObject.obj").objectReferenceValue as Transform
                    });
                }
                foreach(FactionEntityTargetProgressComponent<IBuilding> comp in factionEntity.GetComponentsInChildren<FactionEntityTargetProgressComponent<IBuilding>>())
                {
                    progressObjects.Add(new ProgressObject
                    {
                        comp = comp.Code,
                        obj = new SerializedObject(comp).FindProperty("inProgressObject.obj").objectReferenceValue as Transform
                    });
                }
                foreach(FactionEntityTargetProgressComponent<IResource> comp in factionEntity.GetComponentsInChildren<FactionEntityTargetProgressComponent<IResource>>())
                {
                    progressObjects.Add(new ProgressObject
                    {
                        comp = comp.Code,
                        obj = new SerializedObject(comp).FindProperty("inProgressObject.obj").objectReferenceValue as Transform
                    });
                }
            }

            if(factionEntity.GetComponentInChildren<UnitCarrier>())
            {
                carrierAdd = new List<Transform>();
                carrierEject = new List<Transform>();
                carrierSlots = new List<Transform>();

                var carrireSO = new SerializedObject(factionEntity.GetComponentInChildren<UnitCarrier>());
                var addProp = carrireSO.FindProperty("addablePositions");
                for (int i = 0; i < addProp.arraySize; i++)
                {
                    var objProp = addProp.GetArrayElementAtIndex(i).FindPropertyRelative("obj");
                    carrierAdd.Add(objProp.objectReferenceValue as Transform);
                }

                var slotProp = carrireSO.FindProperty("carrierPositions");
                for (int i = 0; i < slotProp.arraySize; i++)
                {
                    var objProp = slotProp.GetArrayElementAtIndex(i).FindPropertyRelative("obj");
                    carrierSlots.Add(objProp.objectReferenceValue as Transform);
                }

                var ejectProp = carrireSO.FindProperty("ejectablePositions");
                for (int i = 0; i < ejectProp.arraySize; i++)
                {
                    var objProp = ejectProp.GetArrayElementAtIndex(i).FindPropertyRelative("obj");
                    carrierEject.Add(objProp.objectReferenceValue as Transform);
                }
            }

            if(factionEntity.GetComponentInChildren<UnitCreator>())
            {
                var creatorSO = new SerializedObject(factionEntity.GetComponentInChildren<UnitCreator>());
                creatorSpawn = creatorSO.FindProperty("spawnTransform.obj").objectReferenceValue as Transform;
            }

            if(factionEntity.GetComponentInChildren<Rallypoint>())
            {
                var rallypointSO = new SerializedObject(factionEntity.GetComponentInChildren<Rallypoint>());
                rallypoint = rallypointSO.FindProperty("gotoTransform.obj").objectReferenceValue as Transform;
            }

            if(factionEntity.GetComponent("FogOfWarEntity"))
            {
                sameVisibility = new List<Transform>();
                var SO = new SerializedObject(factionEntity.GetComponent("FogOfWarEntity"));

                var sameVisProp = SO.FindProperty("sameVisibilityObjects");
                for (int i = 0; i < sameVisProp.arraySize; i++)
                {
                    var objProp = sameVisProp.GetArrayElementAtIndex(i).FindPropertyRelative("obj");
                    sameVisibility.Add(objProp.objectReferenceValue as Transform);
                }

            }
        }
    }

    [System.Serializable]
    public class UnitFields : FactionEntityFields
    {
        public Animator animator;
        public List<Transform> collectObjects;
        public List<Transform> dropOffObjects;

        // Demo
        public Transform model;

        public UnitFields(Unit unit) : base(unit)
        {
            if (true)
            {
                animator = new SerializedObject(unit.GetComponentInChildren<UnitAnimatorController>()).FindProperty("animator.obj").objectReferenceValue as Animator;
            }

            if(unit.GetComponentInChildren<ResourceCollector>())
            {
                collectObjects = new List<Transform>();
                var collectorSO = new SerializedObject(unit.GetComponentInChildren<ResourceCollector>());

                var collectProp = collectorSO.FindProperty("collectableResources");
                for (int i = 0; i < collectProp.arraySize; i++)
                {
                    var objProp = collectProp.GetArrayElementAtIndex(i).FindPropertyRelative("enableObject.obj");
                    collectObjects.Add(objProp.objectReferenceValue as Transform);
                }
            }

            if(unit.GetComponentInChildren<DropOffSource>())
            {
                dropOffObjects = new List<Transform>();
                var dropOffSO = new SerializedObject(unit.GetComponentInChildren<DropOffSource>());

                var collectProp = dropOffSO.FindProperty("dropOffResources");
                for (int i = 0; i < collectProp.arraySize; i++)
                {
                    var objProp = collectProp.GetArrayElementAtIndex(i).FindPropertyRelative("enableObject.obj");
                    dropOffObjects.Add(objProp.objectReferenceValue as Transform);
                }
            }

            // Demo
            if(unit.GetComponentInChildren<VehicleUnitDestroyHeightModifier>())
            {
                model = new SerializedObject(unit.GetComponentInChildren<VehicleUnitDestroyHeightModifier>()).FindProperty("model.obj").objectReferenceValue as Transform;
            }
        }
    }

    [System.Serializable]
    public class BuildingFields : FactionEntityFields
    {
        public List<Transform> workerPositions;

        public List<TransformList> constructionShow;
        public List<TransformList> constructionHide;

        public List<Transform> constructionCompleteShow;
        public List<Transform> constructionCompleteHide;

        // Advanced Placement:
        public List<Renderer> placementRenderers;

        // Demo
        public Transform model;
        public List<CropState> cropState;
        public List<Transform> farmWorkingPositions;
        public Transform infiniteRotTarget;
        public Transform door;

        public BuildingFields(Building building) : base(building)
        {
            workerPositions = new List<Transform>();

            var workerSO = new SerializedObject(building.GetComponentInChildren<BuildingWorkerManager>());
            var workerProp = workerSO.FindProperty("workerPositions");
            for (int i = 0; i < workerProp.arraySize; i++)
            {
                var objProp = workerProp.GetArrayElementAtIndex(i).FindPropertyRelative("obj");
                workerPositions.Add(objProp.objectReferenceValue as Transform);
            }

            if (true)
            {
                var healthSO = new SerializedObject(building.GetComponent<BuildingHealth>());

                constructionShow = new List<TransformList>();
                constructionHide = new List<TransformList>();
                var states = healthSO.FindProperty("constructionStates");
                for (int i = 0; i < states.arraySize; i++)
                {
                    constructionShow.Add(new TransformList() { transforms = new List<Transform>() });
                    constructionHide.Add(new TransformList() { transforms = new List<Transform>() });

                    var state = states.GetArrayElementAtIndex(i);
                    var showList = state.FindPropertyRelative("showChildObjects");
                    for (int j = 0; j < showList.arraySize; j++)
                    {
                        var objProp = showList.GetArrayElementAtIndex(j).FindPropertyRelative("obj");
                        constructionShow[i].transforms.Add((objProp.objectReferenceValue as Transform));
                    }

                    var hideList = state.FindPropertyRelative("hideChildObjects");
                    for (int j = 0; j < hideList.arraySize; j++)
                    {
                        var objProp = hideList.GetArrayElementAtIndex(j).FindPropertyRelative("obj");
                        constructionHide[i].transforms.Add((objProp.objectReferenceValue as Transform));
                    }
                }

                if (true)
                {
                    var complete = healthSO.FindProperty("constructionCompleteState");
                    constructionCompleteShow = new List<Transform>();
                    constructionCompleteHide = new List<Transform>();

                    var showList = complete.FindPropertyRelative("showChildObjects");
                    for (int i = 0; i < showList.arraySize; i++)
                    {
                        var objProp = showList.GetArrayElementAtIndex(i).FindPropertyRelative("obj");
                        constructionCompleteShow.Add((objProp.objectReferenceValue as Transform));
                    }

                    var hideList = complete.FindPropertyRelative("hideChildObjects");
                    for (int i = 0; i < hideList.arraySize; i++)
                    {
                        var objProp = hideList.GetArrayElementAtIndex(i).FindPropertyRelative("obj");
                        constructionCompleteHide.Add((objProp.objectReferenceValue as Transform));
                    }
                }
            }

            if(building.GetComponentInChildren<BuildingPlacer>().gameObject.GetComponent("BuildingPlacementMaterialHandler"))
            {
                placementRenderers = new List<Renderer>();
                var SO = new SerializedObject(building.GetComponentInChildren<BuildingPlacer>().gameObject.GetComponent("BuildingPlacementMaterialHandler"));

                var placementProp = SO.FindProperty("placementMaterials");
                for (int i = 0; i < placementProp.arraySize; i++)
                {
                    var objProp = placementProp.GetArrayElementAtIndex(i).FindPropertyRelative("renderer.obj");
                    placementRenderers.Add(objProp.objectReferenceValue as Renderer);
                }
            }

            // Demo
            if(building.GetComponentInChildren<BuildingModelHeightModifier>())
            {
                model = new SerializedObject(building.GetComponentInChildren<BuildingModelHeightModifier>()).FindProperty("model.obj").objectReferenceValue as Transform;
            }

            if(building.GetComponentInChildren<FarmStateHandler>())
            {
                cropState = new List<CropState>();
                var SO = new SerializedObject(building.GetComponentInChildren<FarmStateHandler>());
                var states = SO.FindProperty("farmStates");
                for (int i = 0; i < states.arraySize; i++)
                {
                    cropState.Add(new CropState { show = new List<Transform>(), hide = new List<Transform>() });

                    var state = states.GetArrayElementAtIndex(i).FindPropertyRelative("crops");
                    for (int n = 0; n < state.arraySize; n++)
                    {
                        var showList = state.GetArrayElementAtIndex(n).FindPropertyRelative("show");
                        for (int j = 0; j < showList.arraySize; j++)
                        {
                            var objProp = showList.GetArrayElementAtIndex(j).FindPropertyRelative("obj");
                            cropState[i].show.Add((objProp.objectReferenceValue as Transform));
                        }

                        var hideList = state.GetArrayElementAtIndex(n).FindPropertyRelative("hide");
                        for (int j = 0; j < hideList.arraySize; j++)
                        {
                            var objProp = hideList.GetArrayElementAtIndex(j).FindPropertyRelative("obj");
                            cropState[i].hide.Add((objProp.objectReferenceValue as Transform));
                        }
                    }
                }

                farmWorkingPositions = new List<Transform>();
                var workingPositionsProp = SO.FindProperty("workingPositions");
                for (int i = 0; i < workingPositionsProp.arraySize; i++)
                {
                    var objProp = workingPositionsProp.GetArrayElementAtIndex(i).FindPropertyRelative("obj");
                    farmWorkingPositions.Add((objProp.objectReferenceValue as Transform));
                }

            }

            if(building.GetComponentInChildren<InfiniteRotation>())
            {
                infiniteRotTarget = new SerializedObject(building.GetComponentInChildren<InfiniteRotation>()).FindProperty("target.obj").objectReferenceValue as Transform;
            }

            if(building.GetComponentInChildren<BarracksDoorSystem>())
            {
                door = new SerializedObject(building.GetComponentInChildren<BarracksDoorSystem>()).FindProperty("door.obj").objectReferenceValue as Transform;
            }
        }
    }

    [System.Serializable]
    public class Fields
    {
        public UnitFields[] unitFields;
        public BuildingFields[] buildingFields;
        public ResourceFields[] resourceFields;

        public Fields(UnitFields[] unitFields, BuildingFields[] buildingFields, ResourceFields[] resourceFields)
        {
            this.unitFields = unitFields;
            this.buildingFields = buildingFields;
            this.resourceFields = resourceFields;
        }
    }


    public class EntityFieldExtractor
    {

        [MenuItem("RTS Engine/Update/2023.0.0/Step 1: Extract Entity Fields", false, 1501)]
        private static void ExtractFields()
        {
            var entities = RTSEditorHelper.GetEntities().Values.Where(e => e.IsValid()).Select(e => e.gameObject.GetComponent<Entity>());
            var units = entities.Where(e => e.IsValid() && e.IsUnit()).ToList();
            var buildings = entities.Where(e => e.IsValid() && e.IsBuilding()).ToList();
            var resources = entities.Where(e => e.IsValid() && e.IsResourceOnly()).ToList();
            var fields = new Fields(
                units.Select(unit => new UnitFields(unit as Unit)).ToArray(),
                buildings.Select(building => new BuildingFields(building as Building)).ToArray(),
                resources.Select(resource => new ResourceFields(resource as Resource)).ToArray()
            );

            string saveFolder = "RTS Engine Update Handling/2023.0.0/Json";
            string saveName = "fields";
            string saveFormat = "txt";

            try
            {
                saveFolder.Trim('/');
                saveFolder = $"{Application.dataPath}/{saveFolder.Trim('/')}/";

                saveFormat = $".{saveFormat.Trim('.')}";

                if (!Directory.Exists(saveFolder))
                {
                    Directory.CreateDirectory(saveFolder);
                }

                //Check if filename already exists.. etc..
                string fieldsJson = JsonUtility.ToJson(fields);
                string filePath = $@"{saveFolder}{saveName}{saveFormat}";

                File.WriteAllText(filePath, fieldsJson);
            }
            catch(Exception e)
            {
                Debug.LogError($"[EntityFieldsExtractor - 2023.0.0 Update] Exception raised when attempting to save extracted fields {e}");
                return;
            }

            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
        }
    }
}
