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

namespace Rokojori
{  
  [Tool]
  [GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Flash.svg")]
  public partial class Flash:SequenceAction
  { 
    [Export]
    public FlashEffect flashEffect;

    Node3D[] _targets = [];

    [Export]
    public Node3D[] targets
    {
      get => _targets;
      set 
      {
        _targets = value;
        _allTargets = null;
      } 
    }

    [Export]
    public bool includeChildren = false;

    bool refresh = false;
    
    Node3D[] _allTargets;
    public Node3D[] allTargets 
    {
      get 
      {
        if ( _allTargets != null )
        {
          return _allTargets;
        }

        if ( includeChildren )
        {
          _allTargets = Nodes.GetAnyChildren( _targets ).ToArray();
        }
        else
        {
          _allTargets = _targets;
        }

        _allTargets = Lists.Filter( Lists.From( allTargets ), t =>  t as GeometryInstance3D != null ).ToArray(); 

        return _allTargets;
      
      }  
    }
    int animationID = -1;
    int actionID = -1;

    Vector3 randomization;
    List<Material> _materials;

    OmniLight3D light = null;

    // [Export]
    // public Node3D debugProcessIndicator;

    // [Export]
    // public Vector3 debugProcessIndicatorFull = new Vector3( 5, 0, 0 );

    // [Export]
    // public StandardMaterial3D debugMaterial;

    public override string[] _GetConfigurationWarnings()
    {
      var warnigns = new List<string>();

      if ( flashEffect == null )
      {
        warnigns.Add( HierarchyName.Of( this ) + ":" + "No flash effect assigned");
      }

      for ( int i = 0; i < targets.Length; i++ )
      {
        if ( targets[ i ] == null )
        {
         warnigns.Add( HierarchyName.Of( this ) + ":" +"A null target is assigned in the targets array at index " + i );
        }
        
      }

      return warnigns.ToArray();
    }

    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 = allTargets[ 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>();

      if ( Engine.IsEditorHint() )
      {
        _allTargets = null;
      }

      Arrays.ForEach( allTargets, 
        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 )=>
          {         

          // debugProcessIndicator.Position = debugProcessIndicatorFull * span.phase;

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

          // debugProcessIndicator.Position = debugProcessIndicatorFull * span.phase;

          var phase = span.phase; 

          
          var value = flashCurve.Sample( phase );
          
          
          var phaseColor = color;
          phaseColor.A = value * initialAlpha;         

          // debugMaterial.AlbedoColor = phaseColor;
 
          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 );
          }
          

        },
        this
      ).id; 

 
    }

    public override void CancelAction( int id )
    {

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

        light = null;
      }


      RemoveMaterials();

      

      
    }

    void RemoveMaterials()
    {
      if ( _materials == null || allTargets == null || _materials.Count != allTargets.Length )
      { 
        return;
      }

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

      _materials.Clear();
    }


  }
}