using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;


public class CubeManager : MonoBehaviour
{
    public string datasetName = "MeltingBe";
    public RenderTexture volumeRenderTexture;
    public RenderTexture positionRenderTexture;
    public uint frameStep = 1;

    [Header("Texture path is hardcoded because reasons.")]
    //private string texturePrefixHardcodedHACK = "C:\\DATA\\Texture3D"; //TODO: horrible temporary hack
    //private string texturePrefixHardcodedHACK = "/home/strazce/tmp/LNS2022/Texture3D"; //TODO: I know...

    [HideInInspector] public Vector3 physicalExtent;
    [HideInInspector] public float valueScale = 1.0f;
    [HideInInspector] public string datasetPrefix = "Texture3D";

    public bool asAsset = true;
    
    public int frameSkip = 1;
    
    public uint loopNumber = 0; //TODO: Should be readonly

    private string _datasetPath;

    private CubeFileInfo _cubeFileInfo;
    private float[] _normFactors;
    private string[] _volumeFileNames;
    private string[] _positionFileNames;

    private Texture3D[] _frameBuffer = new Texture3D[2];

    private int _currentTextureFrameIndex = 0;
    private int _previousTextureFrameIndex = 0;
    private int _currentBuffer = 0;
    private uint _frameIndex = 0;

    void Awake()
    {
        string texturePrefixPath = asAsset ? Path.Combine("", datasetPrefix) : Path.Combine(Application.dataPath, "..", datasetPrefix);
        _datasetPath = Path.Combine(texturePrefixPath, datasetName);

        string json = "";
        
        if (!asAsset)
        {
            _datasetPath = Path.GetFullPath(_datasetPath); // normalize to canonical path
            string jsonFileName = Path.Combine(_datasetPath, "fileInfo.json");
            json = File.ReadAllText(jsonFileName);
        }
        if (asAsset)
        {
            //FIXME: only one file of this name allowed in Resources folder
            TextAsset targetFile = Resources.Load<TextAsset>("Texture3D/KAU/fileInfo");
            json = targetFile.ToString();
        }

        _cubeFileInfo = JsonUtility.FromJson<CubeFileInfo>(json);

        float maxValue = _cubeFileInfo.norms.Max();
        _normFactors = _cubeFileInfo.norms.Select(x => x / maxValue).ToArray();
        _volumeFileNames = _cubeFileInfo.volumes.ToArray();
        _positionFileNames = _cubeFileInfo.positions.ToArray();

        physicalExtent = new Vector3(
            Math.Abs(_cubeFileInfo.size[0] * _cubeFileInfo.axes[0].magnitude),
            Math.Abs(_cubeFileInfo.size[1] * _cubeFileInfo.axes[1].magnitude),
            Math.Abs(_cubeFileInfo.size[2] * _cubeFileInfo.axes[2].magnitude)
            );
    }

    void OnEnable()
    {
        loopNumber = 0;
        // Pre-load first frame
        _frameBuffer[0] = new Texture3D(_cubeFileInfo.size[0], _cubeFileInfo.size[1], _cubeFileInfo.size[2], TextureFormat.RFloat, false);
        _frameBuffer[1] = new Texture3D(_cubeFileInfo.size[0], _cubeFileInfo.size[1], _cubeFileInfo.size[2], TextureFormat.RFloat, false);
        _currentTextureFrameIndex = 0;
        if (asAsset)
        {
            string volumeFileName = Path.Combine(datasetPrefix, datasetName, Path.GetFileNameWithoutExtension(_volumeFileNames[_currentTextureFrameIndex]));
            var wtf = Resources.Load(volumeFileName);
            Debug.Log(wtf);
            _frameBuffer[_currentBuffer] = (Texture3D)Resources.Load(volumeFileName);
            Debug.Log(volumeFileName);
        }
        else
        {
            string volumeFileName = Path.Combine(_datasetPath, _volumeFileNames[_currentTextureFrameIndex]);
            byte[] rawData = File.ReadAllBytes(volumeFileName);
            _frameBuffer[_currentBuffer].SetPixelData(rawData, 0);
        }
        Debug.Log(_currentBuffer);
        Debug.Log(_frameBuffer[_currentBuffer]);
        _frameBuffer[_currentBuffer].Apply();

        // advance the active texture frame
        _currentBuffer = 1 - _currentBuffer;
        _previousTextureFrameIndex = _currentTextureFrameIndex;
        _currentTextureFrameIndex += frameSkip;
    }

    void Update()
    {
        if (_frameIndex % frameStep == 0)
        {
            // Positions without double buffering for now - picking the previous frame.
            //TODO: They should be all put in a texture array and sampled by frame count inside the VFX graph.
            Texture2D positionTexture = new Texture2D(_cubeFileInfo.nAtoms, 1, TextureFormat.RGBAFloat, false);
            if (asAsset)
            {
                string positionFileName = Path.Combine(datasetPrefix, datasetName, Path.GetFileNameWithoutExtension(_positionFileNames[_previousTextureFrameIndex]));
                positionTexture = (Texture2D)Resources.Load(positionFileName);
            }
            else
            {
                string positionFileName = Path.Combine(_datasetPath, _positionFileNames[_previousTextureFrameIndex]);
                byte[] rawData = File.ReadAllBytes(positionFileName);
                positionTexture.LoadRawTextureData(rawData);
            }
            positionTexture.Apply();
            Graphics.Blit(positionTexture, positionRenderTexture);

            // Double buffering for volumetric textures.
            if (asAsset)
            {
                string volumeFileName = Path.Combine(datasetPrefix, datasetName, Path.GetFileNameWithoutExtension(_volumeFileNames[_currentTextureFrameIndex]));
                _frameBuffer[_currentBuffer] = (Texture3D)Resources.Load(volumeFileName);
            }
            else
            {
                string volumeFileName = Path.Combine(_datasetPath, _volumeFileNames[_currentTextureFrameIndex]);
                byte[] rawData = File.ReadAllBytes(volumeFileName);
                _frameBuffer[_currentBuffer].SetPixelData(rawData, 0);
            }
            _frameBuffer[_currentBuffer].Apply();

            // Blit doesn't work for 3D textures. Copy data from previous frame to the render texture.
            Graphics.CopyTexture(_frameBuffer[1 - _currentBuffer], volumeRenderTexture);
            // Expose the scaling factor.
            valueScale = _normFactors[_previousTextureFrameIndex];

            // Advance the active texture frame.
            _previousTextureFrameIndex = _currentTextureFrameIndex;
            _currentBuffer = 1 - _currentBuffer;
            _currentTextureFrameIndex += frameSkip;
            // Increase the loop counter (number of full animations played)
            if (_currentTextureFrameIndex >= _volumeFileNames.Length)
            {
                _currentTextureFrameIndex = 0;
                loopNumber++;
            }
        }
        ++_frameIndex;

    }
}
