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.ID1ab8493b_717f_4f24_b9d5_831030d1f069
{
    public class Class_MultiIris : 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,
            RotateToLight = 17,
            TextureCellsColumns = 18,
            TextureCellsRows = 19,
            RandomSeed = 20,
            ImageAtlas = 21,
            StretchValue = 22,
            StretchRandom = 23,
            OffsetX = 24,
            OffsetY = 25,
            ScatterX = 26,
            ScatterY = 27
        }
        //<<< _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 Rotate = new Vector2(RotateValue, RotateRandom);
            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 RotateToLight = inputs[(int)InputId.RotateToLight].Eval(context).Value;
            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; if (ImageAtlas == null) return context;
            var StretchValue = inputs[(int)InputId.StretchValue].Eval(context).Value;
            var StretchRandom = inputs[(int)InputId.StretchRandom].Eval(context).Value;
            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);
            //<<< _params
            var SceneInput = inputs[0];
            //var TranslateX = (float) inputs[1].Eval(context).Value;
            //var TranslateY = (float) inputs[2].Eval(context).Value;
            //var TranslateZ = (float) inputs[3].Eval(context).Value;
            //var RotateX = (float) inputs[4].Eval(context).Value;
            //var RotateY = (float) inputs[5].Eval(context).Value;
            //var RotateZ = (float) inputs[6].Eval(context).Value;
            //var ScaleX = (float) inputs[7].Eval(context).Value;
            //var ScaleY = (float) inputs[8].Eval(context).Value;
            //var ScaleZ = (float) inputs[9].Eval(context).Value;
            //var Count = (float) inputs[10].Eval(context).Value;
            
            //!!automatic generated code ends here
            /*
            var translation = new Vector3(TranslateX, TranslateY, TranslateZ);
            var rotation = Quaternion.RotationYawPitchRoll(RotateX / 360.0f * (float)(2.0 * Math.PI),
                                                           RotateY / 360.0f * (float)(2.0 * Math.PI),
                                                           RotateZ / 360.0f * (float)(2.0 * Math.PI));
            var scale = new Vector3(ScaleX, ScaleY, ScaleZ);
            
            var transform = Matrix.Transformation(new Vector3(), new Quaternion(), scale, new Vector3(), rotation, translation);
            */
//            var prevTransform = context.ObjectTWorld;
            
            // 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) {
                float positionOnLine = (float)(SpreadValue * (-0.5 + i / Count) + SpreadRandom * (rand.NextDouble()-0.5) - Distance);
                
                Vector2 objectScreenPos = lightScreenPos * positionOnLine;
                
                objectScreenPos+= Offset + 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;

                //var rotation = Quaternion.RotationYawPitchRoll(0 / 360.0f * (float)(2.0 * Math.PI),
                //                                               0 / 360.0f * (float)(2.0 * Math.PI),
                //                                               0 / 360.0f * (float)(2.0 * Math.PI));
                float scale = SizeValue * (float)(1.0 + SizeRandom  * (rand.NextDouble() - 0.5));
                
                objectToWorld =   Matrix.Scaling( scale, scale, 1) * objectToWorld;
                
                
                //float flip = positionOnLine > 0f ? 0 : 180;
                
                float rotateToCenter = (float)(Math.Atan2( objectScreenPos.X- lightScreenPos.X, objectScreenPos.Y - lightScreenPos.Y) + 3.1415/2) * RotateToLight;
          
                objectToWorld =  Matrix.RotationZ( (float)( (RotateValue) / 180 * Math.PI - rotateToCenter +  RotateRandom * (rand.NextDouble()-0.5) )) * 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                
                //if (_planeMesh == null || Changed) {
                    // Function>>> 
                    var normal = new Vector3(0.0f, 0.0f, -1.0f);
                    
                    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() ))
                                      );
                    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); 

                    float width = 0.1f;
                    float height=0.1f* (StretchValue +  StretchRandom * ((float)rand.NextDouble()) );
                    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);
                    // <<< Function

                    Changed = false;
                //}

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



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

            }
            
            // Restore Texture
            context.Texture0 = prevTexture0;
            
//            context.ObjectTWorld = prevTransform;
            
            //!!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;
    }
}

