using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Kav
{
    public class DeferredPBREffect : Effect
    {
        EffectParameter gPositionParam;
        EffectParameter gAlbedoParam;
        EffectParameter gNormalParam;
        EffectParameter gMetallicRoughnessParam;

        EffectParameter shadowMapOneParam;
        EffectParameter shadowMapTwoParam;
        EffectParameter shadowMapThreeParam;
        EffectParameter shadowMapFourParam;

        EffectParameter lightSpaceMatrixOneParam;
        EffectParameter lightSpaceMatrixTwoParam;
        EffectParameter lightSpaceMatrixThreeParam;
        EffectParameter lightSpaceMatrixFourParam;

        EffectParameter viewMatrixParam;
        EffectParameter cascadeFarPlanesParam;

        EffectParameter directionalLightColorParam;
        EffectParameter directionalLightDirectionParam;

        EffectParameter eyePositionParam;

        PointLightCollection pointLightCollection;

        public Texture2D GPosition { get; set; }
        public Texture2D GAlbedo { get; set; }
        public Texture2D GNormal { get; set; }
        public Texture2D GMetallicRoughness { get; set; }

        public Texture2D ShadowMapOne { get; set; }
        public Texture2D ShadowMapTwo { get; set; }
        public Texture2D ShadowMapThree { get; set; }
        public Texture2D ShadowMapFour { get; set; }

        public Matrix LightSpaceMatrixOne { get; set; }
        public Matrix LightSpaceMatrixTwo { get; set; }
        public Matrix LightSpaceMatrixThree { get; set; }
        public Matrix LightSpaceMatrixFour { get; set; }

        public Matrix ViewMatrix { get; set; }
        public readonly float[] CascadeFarPlanes;

        public Vector3 DirectionalLightColor { get; set; }
        public Vector3 DirectionalLightDirection { get; set; }

        public Vector3 EyePosition { get; set; }

        public int MaxPointLights { get; } = 64;

        public PointLightCollection PointLights
        {
            get { return pointLightCollection; }
            private set { pointLightCollection = value; }
        }

        public DeferredPBREffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DeferredPBREffect)
        {
            CascadeFarPlanes = new float[4];

            CacheEffectParameters();

            pointLightCollection = new PointLightCollection(
                Parameters["PointLightPositions"],
                Parameters["PointLightColors"],
                MaxPointLights
            );
        }

        protected DeferredPBREffect(DeferredPBREffect cloneSource) : base(cloneSource)
        {
            GPosition = cloneSource.GPosition;
            GAlbedo = cloneSource.GAlbedo;
            GNormal = cloneSource.GNormal;
            GMetallicRoughness = cloneSource.GMetallicRoughness;

            EyePosition = cloneSource.EyePosition;

            PointLights = new PointLightCollection(
                Parameters["LightPositions"],
                Parameters["PositionLightColors"],
                MaxPointLights
            );

            for (int i = 0; i < MaxPointLights; i++)
            {
                PointLights[i] = cloneSource.PointLights[i];
            }
        }

        public override Effect Clone()
        {
            return new DeferredPBREffect(this);
        }

        protected override void OnApply()
        {
            gPositionParam.SetValue(GPosition);
            gAlbedoParam.SetValue(GAlbedo);
            gNormalParam.SetValue(GNormal);
            gMetallicRoughnessParam.SetValue(GMetallicRoughness);

            shadowMapOneParam.SetValue(ShadowMapOne);
            shadowMapTwoParam.SetValue(ShadowMapTwo);
            shadowMapThreeParam.SetValue(ShadowMapThree);
            shadowMapFourParam.SetValue(ShadowMapFour);

            lightSpaceMatrixOneParam.SetValue(LightSpaceMatrixOne);
            lightSpaceMatrixTwoParam.SetValue(LightSpaceMatrixTwo);
            lightSpaceMatrixThreeParam.SetValue(LightSpaceMatrixThree);
            lightSpaceMatrixFourParam.SetValue(LightSpaceMatrixFour);

            viewMatrixParam.SetValue(ViewMatrix);
            cascadeFarPlanesParam.SetValue(CascadeFarPlanes);

            directionalLightColorParam.SetValue(DirectionalLightColor);
            directionalLightDirectionParam.SetValue(DirectionalLightDirection);

            eyePositionParam.SetValue(EyePosition);
        }

        void CacheEffectParameters()
        {
            gPositionParam                 = Parameters["gPosition"];
            gAlbedoParam                   = Parameters["gAlbedo"];
            gNormalParam                   = Parameters["gNormal"];
            gMetallicRoughnessParam        = Parameters["gMetallicRoughness"];

            shadowMapOneParam              = Parameters["shadowMapOne"];
            shadowMapTwoParam              = Parameters["shadowMapTwo"];
            shadowMapThreeParam            = Parameters["shadowMapThree"];
            shadowMapFourParam             = Parameters["shadowMapFour"];

            lightSpaceMatrixOneParam       = Parameters["LightSpaceMatrixOne"];
            lightSpaceMatrixTwoParam       = Parameters["LightSpaceMatrixTwo"];
            lightSpaceMatrixThreeParam     = Parameters["LightSpaceMatrixThree"];
            lightSpaceMatrixFourParam      = Parameters["LightSpaceMatrixFour"];

            viewMatrixParam                = Parameters["ViewMatrix"];
            cascadeFarPlanesParam          = Parameters["CascadeFarPlanes"];

            directionalLightDirectionParam = Parameters["DirectionalLightDirection"];
            directionalLightColorParam     = Parameters["DirectionalLightColor"];

            eyePositionParam               = Parameters["EyePosition"];
        }
    }
}