using Impact;
using Impact.Triggers;
using ImpactCFX;
using UnityEditor;
using UnityEngine;

namespace ImpactCFXConversion
{
    public class ImpactTriggerConverter : IAssetConverter
    {
        public Object Convert(Object source, ConversionContext conversionContext)
        {
            if (source is GameObject g)
            {
                Impact.Triggers.IImpactTrigger[] impactTriggers = g.GetComponentsInChildren<Impact.Triggers.IImpactTrigger>();

                foreach (IImpactTrigger obj in impactTriggers)
                {
                    //3D Triggers
                    if (obj is Impact.Triggers.ImpactCollisionTrigger3D collision3D)
                    {
                        ImpactCFX.Triggers.ImpactCollisionTrigger3D dst = collision3D.gameObject.AddComponent<ImpactCFX.Triggers.ImpactCollisionTrigger3D>();
                        convertCollisionTriggerBase(collision3D, dst, conversionContext);
                    }
                    else if (obj is Impact.Triggers.ImpactSlideAndRollTrigger3D slideAndRoll3D)
                    {
                        ImpactCFX.Triggers.ImpactSlideAndRollTrigger3D dst = slideAndRoll3D.gameObject.AddComponent<ImpactCFX.Triggers.ImpactSlideAndRollTrigger3D>();
                        convertSlideAndRollTriggerBase(slideAndRoll3D, dst, conversionContext);
                    }
                    else if (obj is Impact.Triggers.ImpactSpeculativeCollisionTrigger3D speculative3D)
                    {
                        ImpactCFX.Triggers.ImpactSpeculativeCollisionTrigger3D dst = speculative3D.gameObject.AddComponent<ImpactCFX.Triggers.ImpactSpeculativeCollisionTrigger3D>();
                        convertSpeculativeCollisionTriggerBase(speculative3D, dst, conversionContext);
                    }
                    else if (obj is Impact.Triggers.ImpactVelocityCollisionTrigger3D velocity3D)
                    {
                        ImpactCFX.Triggers.ImpactCollisionTrigger3D dst = velocity3D.gameObject.AddComponent<ImpactCFX.Triggers.ImpactCollisionTrigger3D>();
                        convertVelocityCollisionTriggerBase(velocity3D, dst, conversionContext);
                    }
                    else if (obj is Impact.Triggers.ImpactSimpleCollisionTrigger3D simple3D)
                    {
                        ImpactCFX.Triggers.ImpactSimpleCollisionTrigger3D dst = simple3D.gameObject.AddComponent<ImpactCFX.Triggers.ImpactSimpleCollisionTrigger3D>();
                        convertSimpleCollisionTrigger3D(simple3D, dst, conversionContext);
                    }
                    else if (obj is Impact.Triggers.ImpactCollisionTriggerWithCooldown3D cooldown3D)
                    {
                        ImpactCFX.Triggers.ImpactCollisionTrigger3D dst = cooldown3D.gameObject.AddComponent<ImpactCFX.Triggers.ImpactCollisionTrigger3D>();
                        convertCollisionTriggerWithCooldown3D(cooldown3D, dst, conversionContext);
                    }
                    //2D Triggers
                    else if (obj is Impact.Triggers.ImpactCollisionTrigger2D collision2D)
                    {
                        ImpactCFX.Triggers.ImpactCollisionTrigger2D dst = collision2D.gameObject.AddComponent<ImpactCFX.Triggers.ImpactCollisionTrigger2D>();
                        convertCollisionTriggerBase(collision2D, dst, conversionContext);
                    }
                    else if (obj is Impact.Triggers.ImpactSlideAndRollTrigger2D slideAndRoll2D)
                    {
                        ImpactCFX.Triggers.ImpactSlideAndRollTrigger2D dst = slideAndRoll2D.gameObject.AddComponent<ImpactCFX.Triggers.ImpactSlideAndRollTrigger2D>();
                        convertSlideAndRollTriggerBase(slideAndRoll2D, dst, conversionContext);
                    }
                    else if (obj is Impact.Triggers.ImpactSpeculativeCollisionTrigger2D speculative2D)
                    {
                        ImpactCFX.Triggers.ImpactSpeculativeCollisionTrigger2D dst = speculative2D.gameObject.AddComponent<ImpactCFX.Triggers.ImpactSpeculativeCollisionTrigger2D>();
                        convertSpeculativeCollisionTriggerBase(speculative2D, dst, conversionContext);
                    }
                    else if (obj is Impact.Triggers.ImpactVelocityCollisionTrigger2D velocity2D)
                    {
                        ImpactCFX.Triggers.ImpactCollisionTrigger3D dst = velocity2D.gameObject.AddComponent<ImpactCFX.Triggers.ImpactCollisionTrigger3D>();
                        convertVelocityCollisionTriggerBase(velocity2D, dst, conversionContext);
                    }
                    else if (obj is Impact.Triggers.ImpactSimpleCollisionTrigger2D simple2D)
                    {
                        ImpactCFX.Triggers.ImpactSimpleCollisionTrigger2D dst = simple2D.gameObject.AddComponent<ImpactCFX.Triggers.ImpactSimpleCollisionTrigger2D>();
                        convertSimpleCollisionTrigger2D(simple2D, dst, conversionContext);
                    }
                    else if (obj is Impact.Triggers.ImpactCollisionTriggerWithCooldown2D cooldown2D)
                    {
                        ImpactCFX.Triggers.ImpactCollisionTrigger3D dst = cooldown2D.gameObject.AddComponent<ImpactCFX.Triggers.ImpactCollisionTrigger3D>();
                        convertCollisionTriggerWithCooldown2D(cooldown2D, dst, conversionContext);
                    }
                    //Other triggers
                    else if (obj is Impact.Triggers.ImpactParticleCollisionTrigger particle)
                    {
                        ImpactCFX.Triggers.ImpactParticleSystemCollisionTrigger dst = particle.gameObject.AddComponent<ImpactCFX.Triggers.ImpactParticleSystemCollisionTrigger>();
                        convertParticleCollisionTrigger(particle, dst, conversionContext);
                    }
                    else
                    {
                        Debug.LogError($"Unable to convert trigger '{obj}'");
                    }
                }
            }

            return source;
        }

        private void convertCollisionTriggerBase<TCollision, TContact>(Impact.Triggers.ImpactCollisionTriggerBase<TCollision, TContact> src, ImpactCFX.Triggers.ImpactCollisionTriggerBase dst, ConversionContext conversionContext) where TCollision : IImpactCollisionWrapper<TContact> where TContact : IImpactContactPoint
        {
            convertTriggerBase(src, dst, conversionContext);
            ConversionUtility.LogComponentConversion(src, dst, src.gameObject);
        }

        private void convertSimpleCollisionTrigger3D(Impact.Triggers.ImpactSimpleCollisionTrigger3D src, ImpactCFX.Triggers.ImpactSimpleCollisionTrigger3D dst, ConversionContext conversionContext)
        {
            convertTriggerBase(src, dst, conversionContext);
            ConversionUtility.LogComponentConversion(src, dst, src.gameObject);
        }

        private void convertSimpleCollisionTrigger2D(Impact.Triggers.ImpactSimpleCollisionTrigger2D src, ImpactCFX.Triggers.ImpactSimpleCollisionTrigger2D dst, ConversionContext conversionContext)
        {
            convertTriggerBase(src, dst, conversionContext);
            ConversionUtility.LogComponentConversion(src, dst, src.gameObject);
        }

        private void convertVelocityCollisionTriggerBase(Impact.Triggers.ImpactVelocityCollisionTriggerBase src, ImpactCFX.Triggers.ImpactCollisionTriggerBase dst, ConversionContext conversionContext)
        {
            convertTriggerBase(src, dst, conversionContext);

            SerializedObject serializedObject = new SerializedObject(dst);

            serializedObject.FindProperty("collisionVelocityMethod").enumValueIndex = (int)CollisionVelocityMethod.ChangeInVelocity;

            serializedObject.ApplyModifiedProperties();
            serializedObject.Dispose();

            ConversionUtility.LogComponentConversion(src, dst, src.gameObject);
        }

        private void convertCollisionTriggerWithCooldown3D(Impact.Triggers.ImpactCollisionTriggerWithCooldown3D src, ImpactCFX.Triggers.ImpactCollisionTriggerBase dst, ConversionContext conversionContext)
        {
            convertTriggerBase(src, dst, conversionContext);

            SerializedObject serializedObject = new SerializedObject(dst);
            SerializedObject srcSerializedObject = new SerializedObject(src);

            serializedObject.FindProperty("cooldown").floatValue = srcSerializedObject.FindProperty("cooldown").floatValue;

            serializedObject.ApplyModifiedProperties();
            serializedObject.Dispose();
            srcSerializedObject.Dispose();

            ConversionUtility.LogComponentConversion(src, dst, src.gameObject);
        }

        private void convertCollisionTriggerWithCooldown2D(Impact.Triggers.ImpactCollisionTriggerWithCooldown2D src, ImpactCFX.Triggers.ImpactCollisionTriggerBase dst, ConversionContext conversionContext)
        {
            convertTriggerBase(src, dst, conversionContext);

            SerializedObject serializedObject = new SerializedObject(dst);
            SerializedObject srcSerializedObject = new SerializedObject(src);

            serializedObject.FindProperty("cooldown").floatValue = srcSerializedObject.FindProperty("cooldown").floatValue;

            serializedObject.ApplyModifiedProperties();
            serializedObject.Dispose();
            srcSerializedObject.Dispose();

            ConversionUtility.LogComponentConversion(src, dst, src.gameObject);
        }

        private void convertSlideAndRollTriggerBase<TCollision, TContact>(Impact.Triggers.ImpactSlideAndRollTriggerBase<TCollision, TContact> src, ImpactCFX.Triggers.ImpactSlideAndRollTriggerBase dst, ConversionContext conversionContext) where TCollision : IImpactCollisionWrapper<TContact> where TContact : IImpactContactPoint
        {
            SerializedObject serializedObject = new SerializedObject(dst);

            serializedObject.FindProperty("enableSliding").boolValue = src.SlideMode == SlideMode.Normal;
            serializedObject.FindProperty("enableRolling").boolValue = src.RollMode == RollMode.Normal;

            serializedObject.ApplyModifiedProperties();
            serializedObject.Dispose();

            convertTriggerBase(src, dst, conversionContext);

            ConversionUtility.LogComponentConversion(src, dst, src.gameObject);
        }

        private void convertSpeculativeCollisionTriggerBase<TCollision, TContact>(Impact.Triggers.ImpactSpeculativeCollisionTriggerBase<TCollision, TContact> src, ImpactCFX.Triggers.ImpactSpeculativeCollisionTriggerBase dst, ConversionContext conversionContext) where TCollision : IImpactCollisionWrapper<TContact> where TContact : IImpactContactPoint
        {
            SerializedObject serializedObject = new SerializedObject(dst);

            serializedObject.FindProperty("maxCollisionsPerFrame").intValue = src.MaxCollisionsPerFrame;
            serializedObject.FindProperty("contactPointComparisonThreshold").floatValue = src.ContactPointComparisonThreshold;
            serializedObject.FindProperty("contactPointLifetime").floatValue = src.ContactPointLifetime;

            serializedObject.ApplyModifiedProperties();
            serializedObject.Dispose();

            convertTriggerBase(src, dst, conversionContext);

            ConversionUtility.LogComponentConversion(src, dst, src.gameObject);
        }

        private void convertTriggerBase<TCollision, TContact>(Impact.Triggers.ImpactTriggerBase<TCollision, TContact> src, ImpactCFX.Triggers.ImpactTriggerBase dst, ConversionContext conversionContext) where TCollision : IImpactCollisionWrapper<TContact> where TContact : IImpactContactPoint
        {
            SerializedObject serializedObject = new SerializedObject(dst);

            serializedObject.FindProperty("triggerEnabled").boolValue = src.Enabled;
            serializedObject.FindProperty("contactMode").enumValueIndex = (int)src.ContactMode;

            if (src.MainTarget != null)
            {
                if (conversionContext.TryGetConvertedObject(src.MainTarget.GetInstanceID(), out Object obj))
                {
                    serializedObject.FindProperty("impactObject").objectReferenceValue = obj;
                }
                else
                {
                    Debug.LogError($"Could not find converted Impact Object Base for trigger '{src.gameObject.name}'");
                }
            }

            serializedObject.ApplyModifiedProperties();
            serializedObject.Dispose();

            conversionContext.AddObjectToDestroy(ConversionContextDestroyGroupType.Components, src);
        }

        private void convertParticleCollisionTrigger(Impact.Triggers.ImpactParticleCollisionTrigger src, ImpactCFX.Triggers.ImpactParticleSystemCollisionTrigger dst, ConversionContext conversionContext)
        {
            SerializedObject serializedObject = new SerializedObject(dst);
            SerializedObject srcSerializedObject = new SerializedObject(src);

            serializedObject.FindProperty("triggerEnabled").boolValue = src.Enabled;
            serializedObject.FindProperty("particles").objectReferenceValue = srcSerializedObject.FindProperty("_particles").objectReferenceValue;
            serializedObject.FindProperty("isParticleSystem").boolValue = srcSerializedObject.FindProperty("_particles").objectReferenceValue != null;

            if (src.MainTarget != null)
            {
                if (conversionContext.TryGetConvertedObject(src.MainTarget.GetInstanceID(), out Object obj))
                {
                    serializedObject.FindProperty("impactObject").objectReferenceValue = obj;
                }
                else
                {
                    Debug.LogError($"Could not find converted Impact Object Base for trigger '{src.gameObject.name}'");
                }
            }
            else
            {
                serializedObject.FindProperty("impactObject").objectReferenceValue = null;
            }

            serializedObject.ApplyModifiedProperties();
            serializedObject.Dispose();
            srcSerializedObject.Dispose();

            conversionContext.AddObjectToDestroy(ConversionContextDestroyGroupType.Components, src);

            ConversionUtility.LogComponentConversion(src, dst, src.gameObject);
        }
    }
}