using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SharpDX;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using SharpDX.Windows;
using Buffer = SharpDX.Direct3D11.Buffer;
using Device = SharpDX.Direct3D11.Device;

namespace Framefield.Core.ID4b2f4bb1_3171_4bc5_857d_96cc107b2ad5
{
    public class Class_MultiIris__2_ : OperatorPart.Function
    {
        //>>> _inputids
        private enum InputId
        {
            LightIndex = 0,
            Count = 1,
            Distance = 2,
            SpreadValue = 3,
            SpreadRandom = 4,
            SizeValue = 5,
            SizeRandom = 6,
            RotateValue = 7,
            RotateRandom = 8,
            ColorR = 9,
            ColorG = 10,
            ColorB = 11,
            ColorA = 12,
            ColorRandomR = 13,
            ColorRandomG = 14,
            ColorRandomB = 15,
            ColorRandomA = 16,
            TextureCellsColumns = 17,
            TextureCellsRows = 18,
            RandomSeed = 19,
            ImageAtlas = 20,
            StretchX = 21,
            StretchY = 22,
            OffsetX = 23,
            OffsetY = 24,
            ScatterX = 25,
            ScatterY = 26,
            RotateEntities = 27,
            AutoRotate = 28,
            TriggerMode = 29,
            EdgeTriggerInside = 30,
            EdgeTriggerOutside = 31,
            CenterTriggerRadius = 32,
            CenterTriggerBias = 33,
            TriggerScaleOffset = 34,
            TriggerBrightnessOffset = 35
        }
        //<<< _inputids
        public override OperatorPartContext Eval(OperatorPartContext context, List<OperatorPart> inputs, int outputIdx) {
            //>>> _params
            var LightIndex = inputs[(int)InputId.LightIndex].Eval(context).Value;
            var Count = inputs[(int)InputId.Count].Eval(context).Value;
            var Distance = inputs[(int)InputId.Distance].Eval(context).Value;
            var SpreadValue = inputs[(int)InputId.SpreadValue].Eval(context).Value;
            var SpreadRandom = inputs[(int)InputId.SpreadRandom].Eval(context).Value;
            var Spread = new Vector2(SpreadValue, SpreadRandom);
            var SizeValue = inputs[(int)InputId.SizeValue].Eval(context).Value;
            var SizeRandom = inputs[(int)InputId.SizeRandom].Eval(context).Value;
            var Size = new Vector2(SizeValue, SizeRandom);
            var RotateValue = inputs[(int)InputId.RotateValue].Eval(context).Value;
            var RotateRandom = inputs[(int)InputId.RotateRandom].Eval(context).Value;
            var RotateEntities = inputs[(int)InputId.RotateEntities].Eval(context).Value;
            var Rotate = new Vector3(RotateValue, RotateRandom, RotateEntities);
            var ColorR = inputs[(int)InputId.ColorR].Eval(context).Value;
            var ColorG = inputs[(int)InputId.ColorG].Eval(context).Value;
            var ColorB = inputs[(int)InputId.ColorB].Eval(context).Value;
            var ColorA = inputs[(int)InputId.ColorA].Eval(context).Value;
            var Color = new Color4(ColorR, ColorG, ColorB, ColorA);
            var ColorRandomR = inputs[(int)InputId.ColorRandomR].Eval(context).Value;
            var ColorRandomG = inputs[(int)InputId.ColorRandomG].Eval(context).Value;
            var ColorRandomB = inputs[(int)InputId.ColorRandomB].Eval(context).Value;
            var ColorRandomA = inputs[(int)InputId.ColorRandomA].Eval(context).Value;
            var ColorRandom = new Color4(ColorRandomR, ColorRandomG, ColorRandomB, ColorRandomA);
            var TextureCellsColumns = inputs[(int)InputId.TextureCellsColumns].Eval(context).Value;
            var TextureCellsRows = inputs[(int)InputId.TextureCellsRows].Eval(context).Value;
            var TextureCells = new Vector2(TextureCellsColumns, TextureCellsRows);
            var RandomSeed = inputs[(int)InputId.RandomSeed].Eval(context).Value;
            var ImageAtlas = inputs[(int)InputId.ImageAtlas].Eval(context).Image; // Needs to be checked for null!
            var StretchX = inputs[(int)InputId.StretchX].Eval(context).Value;
            var StretchY = inputs[(int)InputId.StretchY].Eval(context).Value;
            var Stretch = new Vector2(StretchX, StretchY);
            var OffsetX = inputs[(int)InputId.OffsetX].Eval(context).Value;
            var OffsetY = inputs[(int)InputId.OffsetY].Eval(context).Value;
            var Offset = new Vector2(OffsetX, OffsetY);
            var ScatterX = inputs[(int)InputId.ScatterX].Eval(context).Value;
            var ScatterY = inputs[(int)InputId.ScatterY].Eval(context).Value;
            var Scatter = new Vector2(ScatterX, ScatterY);
            var AutoRotate = (int) inputs[(int)InputId.AutoRotate].Eval(context).Value;
            var TriggerMode = (int) inputs[(int)InputId.TriggerMode].Eval(context).Value;
            var EdgeTriggerInside = inputs[(int)InputId.EdgeTriggerInside].Eval(context).Value;
            var EdgeTriggerOutside = inputs[(int)InputId.EdgeTriggerOutside].Eval(context).Value;
            var EdgeTrigger = new Vector2(EdgeTriggerInside, EdgeTriggerOutside);
            var CenterTriggerRadius = inputs[(int)InputId.CenterTriggerRadius].Eval(context).Value;
            var CenterTriggerBias = inputs[(int)InputId.CenterTriggerBias].Eval(context).Value;
            var CenterTrigger = new Vector2(CenterTriggerRadius, CenterTriggerBias);
            var TriggerScaleOffset = inputs[(int)InputId.TriggerScaleOffset].Eval(context).Value;
            var TriggerBrightnessOffset = inputs[(int)InputId.TriggerBrightnessOffset].Eval(context).Value;
            //<<< _params
            var SceneInput = inputs[0];
            
            // Get Camera Object
            Matrix worldToView = context.WorldToCamera*context.CameraProjection;
            Matrix viewToWorld = Matrix.Invert(worldToView);

            
            // Get Light Position
            Vector2 lightScreenPos = new Vector2(1,1);

            var pointLightContainer = (HashSet<IPointLight>) context.Objects[OperatorPartContext.POINT_LIGHT_CONTAINER_ID];
            if (pointLightContainer.Count > (int)LightIndex) {
                var pointLight = pointLightContainer.ToArray()[(int)LightIndex];
                Vector4 pos = Vector3.Transform(pointLight.Position, worldToView);
                pos /= pos.W;
                lightScreenPos.X = pos.X;
                lightScreenPos.Y = pos.Y;
            }
            
            // Random
            var rand = new Random((int)RandomSeed);
            
            // Set Texture
            Utilities.DisposeObj( ref _texture);
            try {
                //Logger.Info(this,"here");
                _texture = new ShaderResourceView(context.D3DDevice, ImageAtlas);
            }
            catch (Exception e) {
                Logger.Info(this,"ERROR "+  e.ToString());
            }

            var prevTexture0 = context.Texture0;
            if (_texture != null) {
                context.Texture0 = _texture;
            }

            // Render Planes
            for (var i = 0; i < Count; ++i) {
                var positionOnLine = (float)( (-Distance +1
                                       + SpreadValue  * (-0.5 + i / Count)) 
                                       * (SpreadRandom * (rand.NextDouble()- 0.5) + 1));
                
                Vector2 objectScreenPos = lightScreenPos * positionOnLine;
                
                objectScreenPos+= new Vector2((float)(Scatter.X * (rand.NextDouble()-0.5)), (float)(Scatter.Y * (rand.NextDouble()-0.5))) ;
                

                // Set transform matrix
                var ObjectPositionInView = new Vector4(objectScreenPos.X, objectScreenPos.Y, 0.2f, 1);
                Vector4 ObjectPositionInWorld = Vector4.Transform(ObjectPositionInView, viewToWorld);
                ObjectPositionInWorld /= ObjectPositionInWorld.W;

                var objectToWorld = Matrix.Invert(context.WorldToCamera);
                objectToWorld.Row4 = ObjectPositionInWorld;

                float scale = SizeValue * (float)(1.0 + SizeRandom  * (rand.NextDouble() - 0.5));
                
                var color = new Vector4(
                    ColorR +  ColorRandomR * (float)(rand.NextDouble() - 0.5)*4, 
                    ColorG +  ColorRandomG * (float)(rand.NextDouble() - 0.5)*4, 
                    ColorB +  ColorRandomB * (float)(rand.NextDouble() - 0.5)*4, 
                    ColorA * (1-  ColorRandomA * (float)(rand.NextDouble() ))
                  );
                
                // Center Trigger
                if( TriggerMode !=0) {
                    
                    var triggerPosition = TriggerMode == 1 ? lightScreenPos
                                                           : objectScreenPos;
                   
                    
                    var centerTriggerAmount = CenterTriggerRadius > 0.0001 ? Math.Max(0, CenterTriggerRadius - triggerPosition.Length())/CenterTriggerRadius
                                                                           : 0; 
                    float smoothEdgeTriggerAmount = 0;
                    if( EdgeTriggerInside > 0 && EdgeTriggerOutside > 0) {
                    
                        var insideEdgeTriggerAmountY =   Math.Min( EdgeTriggerInside,  Math.Max(0,   Math.Abs(triggerPosition.Y) -1  + EdgeTriggerInside ) )/ EdgeTriggerInside;
                        var outsideEdgeTriggerAmountY =  Math.Min( EdgeTriggerOutside,  Math.Max(0,  -Math.Abs(triggerPosition.Y) +1  + EdgeTriggerOutside ) )/ EdgeTriggerOutside; 

                        var insideEdgeTriggerAmountX =   Math.Min( EdgeTriggerInside,  Math.Max(0,   Math.Abs(triggerPosition.X) -1  + EdgeTriggerInside ) )/ EdgeTriggerInside;
                        var outsideEdgeTriggerAmountX =  Math.Min( EdgeTriggerOutside,  Math.Max(0,  -Math.Abs(triggerPosition.X) +1  + EdgeTriggerOutside ) )/ EdgeTriggerOutside;                    
                    
                        var edgeTriggerAmount = Math.Max(insideEdgeTriggerAmountY * outsideEdgeTriggerAmountY,  insideEdgeTriggerAmountX * outsideEdgeTriggerAmountX);
                        var t= edgeTriggerAmount; 
                        smoothEdgeTriggerAmount = t*t*(3 - 2*t);
                    }
                    
                    
                    var totalTriggerAmount = centerTriggerAmount + smoothEdgeTriggerAmount;    
                    scale *= (1+ TriggerScaleOffset * totalTriggerAmount);
                    color.W += TriggerBrightnessOffset * totalTriggerAmount;
                
                
                }
                
                objectToWorld =   Matrix.Scaling( scale, scale, 1) * objectToWorld;
                
                var autoRotation = 0f;
                if(AutoRotate == 1) {
                    //rotateToCenter = (float)(Math.Atan2( objectScreenPos.X- lightScreenPos.X, objectScreenPos.Y - lightScreenPos.Y) + 3.1415/2) * RotateToLight;
                    autoRotation = (float)(Math.Atan2( objectScreenPos.X- lightScreenPos.X, objectScreenPos.Y - lightScreenPos.Y) + 3.1415/2);
                }   
                else if(AutoRotate == 2) {
                    autoRotation = (float)(Math.Atan2( objectScreenPos.X, objectScreenPos.Y) + 3.1415/2);                
                }
                                

                objectToWorld = Matrix.Translation( OffsetX, OffsetY,0) 
                              * Matrix.RotationZ( (float)( 
                                            (RotateValue) / 180 * Math.PI 
                                          - autoRotation 
                                          + (RotateRandom / 180 * Math.PI) * (rand.NextDouble()-0.5) 
                                          + (RotateEntities / 180 * Math.PI) * i/Count
                                          )) * objectToWorld;
                
                var oldObjectToWorld = context.ObjectTWorld;
                context.ObjectTWorld = objectToWorld*context.ObjectTWorld;
                
                // Transforom UV to pick correct texture cell
                if(TextureCellsRows == 0)
                    TextureCellsRows = 1;
                    
                if(TextureCellsColumns == 0)
                    TextureCellsColumns =1;

                int row = (int)(Math.Floor(i/TextureCellsColumns) % TextureCellsRows);
                int column = (int)(i%TextureCellsRows);
                    
                var translationUV = new Vector3(1/TextureCellsColumns * column, 1/TextureCellsRows * row, 0);
                var rotationUV = new Quaternion();
                var scaleUV = new Vector3(1/TextureCellsColumns, 1/TextureCellsRows,0);
                var pivotUV = new Vector3(0,0,0);
            
                var transformUV = Matrix.Transformation(pivotUV, new Quaternion(), scaleUV, pivotUV, rotationUV, translationUV);
                var prevTransformUV = context.TextureMatrix;
                context.TextureMatrix = transformUV * prevTransformUV;
                
                // Render plane                
                    var normal = new Vector3(0.0f, 0.0f, -1.0f);
                    

                    var tangent = new Vector3(1.0f, 0.0f, 0.0f);
                    var binormal = new Vector3(0.0f, -1.0f, 0.0f);
                    
                    var inputElements = new InputElement[] {
                        new InputElement("POSITION", 0, SharpDX.DXGI.Format.R32G32B32A32_Float, 0, 0),
                        new InputElement("NORMAL", 0, SharpDX.DXGI.Format.R32G32B32_Float, 16, 0),
                        new InputElement("COLOR", 0, SharpDX.DXGI.Format.R32G32B32A32_Float, 28, 0),
                        new InputElement("TEXCOORD", 0, SharpDX.DXGI.Format.R32G32_Float, 44, 0),
                        new InputElement("TANGENT", 0, SharpDX.DXGI.Format.R32G32B32_Float, 52, 0),
                        new InputElement("BINORMAL", 0, SharpDX.DXGI.Format.R32G32B32_Float, 64, 0)
                    };
                    
                    int numQuads = 1;                     
                    var attributesSize = 76;
                    int numTriangles = numQuads * 2;
                    int streamSize = numTriangles * 3 * attributesSize;

                    if (_planeMesh == null) {
                        var vertices = new Buffer(context.D3DDevice, new DataStream(streamSize, true, true), new BufferDescription() {
                            BindFlags = BindFlags.VertexBuffer,
                            CpuAccessFlags = CpuAccessFlags.Write,
                            OptionFlags = ResourceOptionFlags.None,
                            SizeInBytes = streamSize,
                            Usage = ResourceUsage.Dynamic
                        });
                        _planeMesh = new Mesh() { InputElements = inputElements, Vertices = vertices, NumTriangles = numTriangles, AttributesSize = attributesSize };
                    }

                    DataStream vertexStream;
                    context.D3DDevice.ImmediateContext.MapSubresource(_planeMesh.Vertices, MapMode.WriteDiscard, SharpDX.Direct3D11.MapFlags.None, out vertexStream); 

                    //var aspect=  (StretchValue +  StretchRandom * ((float)rand.NextDouble()-0.5f) );
                    float width = StretchX * 0.1f;
                    float height=StretchY * 0.1f; 
                    
                    float startX = -width/2.0f;
                    float startZ = -height/2.0f;

                    //float z=0; // wech
                    float normalizedBottom= 0;
                    float bottom = startZ;
                    float normalizedTop = 1;
                    float top = startZ + height;

                    float normalizedLeft = 0;
                    float left = startX;
                    float normalizedRight = 1;
                    float right = startX + width;

                    // tri 1 vert 1
                    vertexStream.Write(new Vector4(right, top, 0, 1));
                    vertexStream.Write(normal);
                    vertexStream.Write(color);
                    vertexStream.Write(new Vector2(normalizedRight, 1.0f- normalizedTop));
                    vertexStream.Write(tangent);
                    vertexStream.Write(binormal);

                    // tri 1 vert 2
                    vertexStream.Write(new Vector4(right, bottom, 0, 1));
                    vertexStream.Write(normal);
                    vertexStream.Write(color);
                    vertexStream.Write(new Vector2(normalizedRight, 1.0f-normalizedBottom));
                    vertexStream.Write(tangent);
                    vertexStream.Write(binormal);

                    // tri 1 vert 3
                    vertexStream.Write(new Vector4(left, bottom, 0, 1));
                    vertexStream.Write(normal);
                    vertexStream.Write(color);
                    vertexStream.Write(new Vector2(normalizedLeft, 1.0f-normalizedBottom));
                    vertexStream.Write(tangent);
                    vertexStream.Write(binormal);

                    // tri 2 vert 1
                    vertexStream.Write(new Vector4(left, bottom, 0, 1));
                    vertexStream.Write(normal);
                    vertexStream.Write(color);
                    vertexStream.Write(new Vector2(normalizedLeft, 1.0f-normalizedBottom));
                    vertexStream.Write(tangent);
                    vertexStream.Write(binormal);

                    // tri 2 vert 2
                    vertexStream.Write(new Vector4(left, top, 0, 1));
                    vertexStream.Write(normal);
                    vertexStream.Write(color);
                    vertexStream.Write(new Vector2(normalizedLeft, 1.0f-normalizedTop));
                    vertexStream.Write(tangent);
                    vertexStream.Write(binormal);

                    // tri 2 vert 3
                    vertexStream.Write(new Vector4(right, top, 0, 1));
                    vertexStream.Write(normal);
                    vertexStream.Write(color);
                    vertexStream.Write(new Vector2(normalizedRight, 1.0f-normalizedTop));
                    vertexStream.Write(tangent);
                    vertexStream.Write(binormal);

                    context.D3DDevice.ImmediateContext.UnmapSubresource(_planeMesh.Vertices, 0);

                    Changed = false;

                context.Renderer.SetupEffect(context);
                context.Renderer.Render(_planeMesh, context);



                context.ObjectTWorld = oldObjectToWorld;
                context.TextureMatrix = prevTransformUV;

            }
            
            // Restore Texture
            context.Texture0 = prevTexture0;
            
            
            //!!automatic generated code starts here
            return context;
        }
        
        public override void Dispose() {
            Utilities.DisposeObj( ref _texture);
            Utilities.DisposeObj(ref _planeMesh);
        }
        
        
        private Mesh _planeMesh = null;        
        ShaderResourceView _texture = null;
    }
}

