using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Text;
using Godot;

namespace Rokojori
{  
  [Tool]
  [GlobalClass]
  public partial class Flash:SequenceAction
  { 
    [Export]
    public FlashEffect flashEffect;

    [Export]
    public Node3D[] targets = new Node3D[ 0 ];   

    int animationID = -1;
    int actionID = -1;

    Vector3 randomization;
    List<Material> _materials;

    OmniLight3D light = null;

    Color ComputeLightColor( Color color, float phase )
    {
      if ( phase < 5/100f )
      {
        var n = MathX.NormalizeClamped( phase, 0, 5/100f );
        color *= n;
        
      }
      else if ( phase > 95/100f )
      {
        var n = MathX.NormalizeClamped( phase, 1, 95/100f );
        color *= n;
      }
      
      return color;
    }

    protected override void _OnTrigger()
    {
      if ( actionID != -1 )
      {
        CancelAction( actionID );
      }


      actionID = DispatchStart();

      var random = LCG.WithSeed( networkSeed );

      // RJLog.Log( "Setting actionID id", actionID, GetLastSequenceActionID() ); 

      var flashCurve = flashEffect.flashCurve;
      var color = flashEffect.color.GetHDRColor();
      var timeline = flashEffect.timeline;

      randomization = flashCurve.Randomize( random );
      var duration = flashCurve.GetRandomizedDuration( randomization );

      
      var transparentColor = color;
      var initialAlpha = transparentColor.A;

      transparentColor.A = 0;

      Material material = null;
      

      if ( FlashEffect.FlashLightMode.No_Light != flashEffect.lightMode )
      {
        light = targets[ 0 ].CreateChild<OmniLight3D>();
        light.LightColor = ComputeLightColor( color, 0 );
        light.LightEnergy = 0;
        light.OmniRange = flashEffect.lightRange;
        light.ShadowEnabled = flashEffect.lightHasShadows;

        // RJLog.Log( "Created Light", light.Name );

      }

      if ( FlashEffect.MaterialMode.Flat_Standard3D == flashEffect.materialMode )
      {
        var standardMaterial = new StandardMaterial3D();
        standardMaterial.AlbedoColor = transparentColor;
        standardMaterial.ShadingMode = BaseMaterial3D.ShadingModeEnum.Unshaded;
        standardMaterial.Transparency = BaseMaterial3D.TransparencyEnum.Alpha;

        material = standardMaterial;
      }
      else if ( FlashEffect.MaterialMode.Fresnel_FresnelOverlay == flashEffect.materialMode )
      {
        var fresnelMaterial = new FresnelOverlayMaterial();
        fresnelMaterial.albedo.Set( transparentColor );

        material = fresnelMaterial;
      }
      else if ( FlashEffect.MaterialMode.Scan_ScanGradient == flashEffect.materialMode )
      {
        var scanMaterial = ScanGradientMaterial.White.Get();
        
        scanMaterial.albedo.Set( transparentColor );

        material = scanMaterial;
      }
      else if ( FlashEffect.MaterialMode.Shield_TriPlanarOverlay == flashEffect.materialMode )
      {
        var shieldMaterial = TriPlanarOverlayMaterial.WhiteShield.Get();
        
        shieldMaterial.albedo.Set( transparentColor );

        material = shieldMaterial;
      }
      else 
      {
        material = flashEffect.customMaterial;
        flashEffect.customFlashColorProperty.Set( material, transparentColor );
      } 

      

      _materials = new List<Material>();


      Arrays.ForEach( targets, 
        t =>
        {
          var m = (Material) material.Duplicate( true );

          _materials.Add( m );

          Materials.AddOverlay( t, m ); 

        }
      ); 

      var start = timeline.position;
      var end   = start + duration;


      animationID = TimeLineManager.ScheduleSpanIn( timeline, 0, duration, 
        ( TimeLineSpan span, TimeLineSpanUpdateType type )=>
        {         

          if ( animationID != span.id )
          {
            return;
          }          

          var phase = span.phase; 
          var value = flashCurve.Sample( phase );

          
          var phaseColor = color;
          phaseColor.A = value * initialAlpha;         

          if ( light != null )
          {
            light.LightEnergy = value * flashEffect.lightFlashCurveScale;
            light.LightColor = ComputeLightColor( color, phase );
          }

          _materials.ForEach(
            ( m )=>
            {
              if ( FlashEffect.MaterialMode.Flat_Standard3D == flashEffect.materialMode )
              {
                var sm = m as StandardMaterial3D;
                sm.AlbedoColor = phaseColor;
              }
              else if ( FlashEffect.MaterialMode.Fresnel_FresnelOverlay == flashEffect.materialMode )
              {
                var sm = m as FresnelOverlayMaterial;
                sm.albedo.Set( phaseColor );
              } 
              else if ( FlashEffect.MaterialMode.Scan_ScanGradient == flashEffect.materialMode )
              {
                var sm = m as ScanGradientMaterial;
                sm.albedo.Set( phaseColor );
                sm.offset.Set( phase * 3 );
              }     
              else if ( FlashEffect.MaterialMode.Shield_TriPlanarOverlay == flashEffect.materialMode )
              {
                var sm = m as TriPlanarOverlayMaterial;
                sm.albedo.Set( phaseColor );
              }     
              else if ( FlashEffect.MaterialMode.Custom_Material == flashEffect.materialMode )
              {
                var sm = m as ShaderMaterial;
                flashEffect.customFlashColorProperty.Set( sm, phaseColor );
              }      

            }
          );


          if ( type == TimeLineSpanUpdateType.End )
          {
            if ( light != null )
            {
              light.LightEnergy = 0;
              light.LightColor = new Color( 0, 0, 0, 0 );
            }

            CancelAction( actionID );
            DispatchEnd( actionID );
          }

        }
      ).id; 

 
    }

    public override void CancelAction( int id )
    {

      if ( false && actionID != id )
      {
        // RJLog.Log( "actionID != id", "id", id, "actionID", actionID );
        return;
      }

      if ( light != null )
      {
        // RJLog.Log( "Removing Light", light.Name );
        Nodes.RemoveAndDelete( light );

        light = null;
      }


      RemoveMaterials();

      

      
    }

    void RemoveMaterials()
    {
      if ( _materials.Count != targets.Length )
      { 
        return;
      }

      for ( int i = 0; i < targets.Length; i++ )
      {
        Materials.RemoveOverlay( targets[ i ], _materials[ i ] ); 
      }

      _materials.Clear();
    }


  }
}