//>>> _using
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SharpDX;
using SharpDX.Direct3D11;
using SharpDX.Windows;
//<<< _using
using SharpDX.DXGI;
using System.IO;
using Framefield.Core.Rendering;

namespace Framefield.Core.IDaf6a1a59_1913_445a_8314_befc14c1cdcd
{
    public class Class_LoadVolume : FXSourceCodeFunction
    {
        //>>> _inputids
        private enum InputId
        {
            Code = 0,
            FilePath = 1,
            DataSizeX = 2,
            DataSizeY = 3,
            DataSizeZ = 4,
            SizeX = 5,
            SizeY = 6,
            SizeZ = 7,
            OffsetX = 8,
            OffsetY = 9,
            WindowLevel = 10,
            WindowSize = 11,
            RotateX = 12,
            RotateY = 13,
            RotateZ = 14,
            OffsetZ = 15,
            Scene = 16
        }
        //<<< _inputids

        public override void Dispose() {
            base.Dispose();
            Utilities.DisposeObj(ref _imageData);
            Utilities.DisposeObj(ref _imageDataSRV);
        }

        public override OperatorPartContext Eval(OperatorPartContext context, List<OperatorPart> inputs, int outputIdx) 
        {
            //>>> _params
            var Code = inputs[(int)InputId.Code].Eval(context).Text;
            var FilePath = inputs[(int)InputId.FilePath].Eval(context).Text;
            var DataSizeX = inputs[(int)InputId.DataSizeX].Eval(context).Value;
            var DataSizeY = inputs[(int)InputId.DataSizeY].Eval(context).Value;
            var DataSizeZ = inputs[(int)InputId.DataSizeZ].Eval(context).Value;
            var DataSize = new Vector3(DataSizeX, DataSizeY, DataSizeZ);
            var SizeX = inputs[(int)InputId.SizeX].Eval(context).Value;
            var SizeY = inputs[(int)InputId.SizeY].Eval(context).Value;
            var SizeZ = inputs[(int)InputId.SizeZ].Eval(context).Value;
            var Size = new Vector3(SizeX, SizeY, SizeZ);
            var OffsetX = inputs[(int)InputId.OffsetX].Eval(context).Value;
            var OffsetY = inputs[(int)InputId.OffsetY].Eval(context).Value;
            var OffsetZ = inputs[(int)InputId.OffsetZ].Eval(context).Value;
            var Offset = new Vector3(OffsetX, OffsetY, OffsetZ);
            var WindowLevel = inputs[(int)InputId.WindowLevel].Eval(context).Value;
            var WindowSize = inputs[(int)InputId.WindowSize].Eval(context).Value;
            var Window = new Vector2(WindowLevel, WindowSize);
            var RotateX = inputs[(int)InputId.RotateX].Eval(context).Value;
            var RotateY = inputs[(int)InputId.RotateY].Eval(context).Value;
            var RotateZ = inputs[(int)InputId.RotateZ].Eval(context).Value;
            var Rotate = new Vector3(RotateX, RotateY, RotateZ);
            var Scene = inputs[(int)InputId.Scene];
            //<<< _params

            if (TryUpdateVolumeTexture(FilePath, DataSize))
                Changed = true;

            if (_firstEval) {
                for (int i = 0; i < NumCodes(); ++i)
                    Compile(i);
                _firstEval = false;
                Changed = true;
            }

            if (_imageDataSRV != null) {
                var rotation = Quaternion.RotationYawPitchRoll(RotateY / 360.0f * (float)(2.0 * Math.PI),
                                                               RotateX / 360.0f * (float)(2.0 * Math.PI),
                                                               RotateZ / 360.0f * (float)(2.0 * Math.PI));
                var rotTransform = Matrix.RotationQuaternion(rotation);

                _effect.GetVariableByName("Size").AsVector().Set(Size);
                _effect.GetVariableByName("Offset").AsVector().Set(Offset);
                _effect.GetVariableByName("rotationMatrix").AsMatrix().SetMatrix(rotTransform);
                _effect.GetVariableByName("Window").AsVector().Set(Window);
                _effect.GetVariableByName("imageData").AsShaderResource().SetResource(_imageDataSRV);

                using (new PropertyStasher<OperatorPartContext>(context, "Effect")) {
                    context.Effect = _effect;
                    Scene.Eval(context);
                }
            }

            return context;
        }

        private bool TryUpdateVolumeTexture(string filename, Vector3 size) {
            if (_fileName == filename && _size == size)
                return false;

            Utilities.DisposeObj(ref _imageData);
            Utilities.DisposeObj(ref _imageDataSRV);

            try {
                var immediateContext = D3DDevice.Device.ImmediateContext;

                var descStaging = new Texture3DDescription() {
                    BindFlags = BindFlags.None,
                    Format = Format.R16_UNorm,
                    Width = (int)size.X,
                    Height = (int)size.Y,
                    Depth = (int)size.Z,
                    MipLevels = 0,
                    Usage = ResourceUsage.Staging,
                    OptionFlags = ResourceOptionFlags.None,
                    CpuAccessFlags = CpuAccessFlags.Write
                };
                var imageTexStaging = new Texture3D(D3DDevice.Device, descStaging);

                DataStream dataStream;
                immediateContext.MapSubresource(imageTexStaging, 0, 0, MapMode.Write, SharpDX.Direct3D11.MapFlags.None, out dataStream);
                using (dataStream)
                {
                    dataStream.Position = 0;

                    var fs = new FileStream(filename, FileMode.Open);
                    if (fs.Length <= dataStream.Length)
                        fs.CopyTo(dataStream);
                    fs.Close();
                }
                immediateContext.UnmapSubresource(imageTexStaging, 0);


                Texture3DDescription desc = descStaging;
                desc.BindFlags = BindFlags.ShaderResource;
                desc.Usage = ResourceUsage.Default;
                desc.CpuAccessFlags = CpuAccessFlags.None;

                _imageData = new Texture3D(D3DDevice.Device, desc);
                _imageDataSRV = new ShaderResourceView(D3DDevice.Device, _imageData);
                immediateContext.CopyResource(imageTexStaging, _imageData);
                Utilities.DisposeObj(ref imageTexStaging);

                _fileName = filename;
                _size = size;
                return true;
            }
            catch (Exception) {
                Logger.Error(this,"Error loading volume {0}", filename);
            }
            return false;;
        }

        bool _firstEval = true;
        string _fileName;
        Vector3 _size;
        Texture3D _imageData;
        ShaderResourceView _imageDataSRV;
    }
}

