using System.Collections;
using System.Collections.Generic;
using Godot;
using System;

using System.Threading.Tasks;

namespace Rokojori
{
  public class MultiBakeModeOctahedral:MultiBakeModeImplementation
  {
    public readonly string octahedralShader = "res://addons/rokojori_action_library/External/Imposter/materials/shaders/ImpostorShader.gdshader";

    public override MultiBaker.BakeMode GetBakeMode()
    {
      return MultiBaker.BakeMode.Octahedral;
    }

    public override int GetNumViews()
    {
      var views = multiBaker.octahedralSides * multiBaker.octahedralSides;

      return views;
    }

    public override void CreateMaterial( bool preview )
    {
      var mb = multiBaker;
      var material = new ShaderMaterial();
      material.Shader = ResourceLoader.Load( octahedralShader ) as Shader;

      material.SetShaderParameter( "isFullSphere", mb.octahedralFullSphere );
      material.SetShaderParameter( "imposterFrames", Vector2.One * mb.octahedralSides  );

      Materials.Set( mb.X_outputMesh, material );    
    }

    public override void AssignMaterial( BakingMaterialMode mode, Texture2D texture )
    {
      var mb = multiBaker;
      mb.CacheTexture( mode, texture );
      
      var material = Materials.Get<ShaderMaterial>( mb.X_outputMesh );     

      if ( material == null )
      {
        RJLog.Log( "Not the right material on mesh!" );
        return;
      }

      if ( BakingMaterialMode.Albedo == mode || BakingMaterialMode.Preview == mode )
      {
        material.SetShaderParameter( "imposterTextureAlbedo", texture );
      }
      else if ( BakingMaterialMode.Normals == mode )
      {
        material.SetShaderParameter( "imposterTextureNormal", texture );
      }
      else if ( BakingMaterialMode.ORM == mode )
      {
        material.SetShaderParameter( "imposterTextureOrm", texture );
      }
      else if ( BakingMaterialMode.Depth == mode )
      {
        material.SetShaderParameter( "imposterTextureDepth", texture );
      }


      
    }

    public override void CreateBakes()
    {
      var fov = multiBaker.GetCameraFOV();
      var distance = multiBaker.GetCameraDistance();
      var outputScale = multiBaker.GetOutputScale();

      var _bakers = multiBaker.bakers;
      var mb = multiBaker;


      _bakers.ForEach(
        b =>
        {
          b.useCustomFOV = true;
          b.customFOV = fov;

          b.useCustomDistance = true;
          b.customDistance = distance;

          b.rotationMode = Baker.RotationMode.Quaternion;
        }
      );


      var numTextures = GetNumViews();
      var textureAlignment = TextureMerger.ComputeTextureAlignment( numTextures );
 
      var toOP = 1f / ( mb.octahedralSides - 1 );
      var index = 0;

      for ( int x = 0; x < mb.octahedralSides; x++ )
      {
        for ( int y = 0; y < mb.octahedralSides; y++ )
        {
          var inverseX = ( mb.octahedralSides - 1 ) - x; 
          var inverseY = ( mb.octahedralSides - 1 ) - y; 
          var octahedralPosition = new Vector2( y, inverseX ) * toOP;

          var normal = OctahedralMapping.Map( octahedralPosition, mb.octahedralFullSphere );

          _bakers[ index ].rotationQuaternion = Math3D.LookRotation( normal, true ).Normalized().Inverse();           

          index ++;
        }


      }

      var qm = new QuadMesh();
      mb.X_outputMesh.Mesh = qm;
      var magicScale = 0.75f;
      mb.X_outputMesh.Scale = Vector3.One * ( outputScale / mb.cameraZoom * magicScale);
            
      
    }

    
  }
}