
using Godot;
using System.Collections.Generic;

namespace Rokojori
{  
  [Tool]
  [GlobalClass]
  public partial class CompositorEffectLayout:Resource
  {    
    [Export]
    public Compositor compositor;

    
    public List<CompositorVFX> activeVFX = []; 

    int refresh = 0;
    int refreshFrames = 1000;

    public RangeI GetLayerRange( CompositorEffectLayer layer )
    {
      var effects = compositor.CompositorEffects;

      var start = effects.FindIndex( e => e is CompositorEffectLayerSlot slot && slot.layer == layer );

      if ( start == -1 )
      {
        return null;
      }

      var end = effects.FindIndex( e => e is CompositorEffectLayerSlot, start + 1 );

      if ( end == -1 )
      {
        end = effects.Count;
      }
      
      end--;
      
      return new RangeI( start, end );
    }

    public void Update()
    {
      refresh--;

      if ( refresh <= 0 )
      {
        refresh = refreshFrames;

        // activeVFX.ForEach( 
        //   a => 
        //   {
        //     a.DuplicateHack();          
        //   }
        // );
      }
    }

    public void Ensure( CompositorVFX vfx, RokojoriCompositorEffect[] effects, bool inside )
    {
      if ( inside )
      {
        activeVFX.AddIfNotPresent( vfx );
      }
      else
      {
        activeVFX.Remove( vfx );
      }
      
      
      var list = new List<System.Tuple<RokojoriCompositorEffect,RokojoriCompositorEffect>>();
      var replacements = new List<System.Tuple<RokojoriCompositorEffect,RokojoriCompositorEffect>>();

      effects.ForEach(
        ( e )=>
        {
          var el = compositor.CompositorEffects.Find( ce => ce is RokojoriCompositorEffect re && re.compositorEffectID != null && re.compositorEffectID == e.compositorEffectID );

          var isInside = el != null;

          if ( isInside == inside )
          {
            if ( el != e )
            {
              replacements.Add( new System.Tuple<RokojoriCompositorEffect, RokojoriCompositorEffect>( e, el as RokojoriCompositorEffect ) ) ;
            }

            return;
          }

          list.Add( new System.Tuple<RokojoriCompositorEffect, RokojoriCompositorEffect>( e, el as RokojoriCompositorEffect ) );
          
         
        }
      );

      replacements.ForEach(
        ( t )=>
        {
          var ef = t.Item1;
          var indexE = effects.IndexOf( ef );
          var ce = t.Item2;
          var fx = compositor.CompositorEffects;
          var indexC = fx.IndexOf( ce );

          if ( indexC == -1 || indexE == -1 )
          {
            // this.LogInfo( "Not replacing: ","Compositor index:", indexC, "VFX index:", indexE );
            return;
          }

          effects[ indexE ] = (RokojoriCompositorEffect) ef.Duplicate();

          fx[ indexC ] = effects[ indexE ];
          compositor.CompositorEffects = fx;          
        }
      );


      if ( list.Count > 0 )
      {
        var newEffects = compositor.CompositorEffects;

        if ( inside )
        {
          var insertionIndex = newEffects.Count;

          if ( vfx.layer != null )
          {
            var range = GetLayerRange( vfx.layer );
            this.LogInfo( "Insertion Layer:", vfx.layer, ">>", range );

            insertionIndex = range.max + 1;

            if ( range != null )
            {
              var found = false;

              for ( int i = range.min; ! found && i <= range.max; i++ )
              {
                // this.LogInfo( "Checking:", i );

                if ( newEffects[ i ] != null && newEffects[ i ] is RokojoriCompositorEffect r && 
                     r.compositorEffectID != null && 
                     r.compositorEffectID.priority > vfx.priority
                )
                {
                  found = true;
                  // this.LogInfo( "Found lower insertion Index:", insertionIndex, ">>", i );
                  insertionIndex = i;
                  
                }
                else
                {
                  if ( newEffects[ i ] != null && newEffects[ i ] is RokojoriCompositorEffect rfx )
                  {
                    // this.LogInfo( "Found insertion Index:", rfx.compositorEffectID );
                  }
                }
              }
            }
          }

          // this.LogInfo( "Insertion Index:", insertionIndex, ">>", newEffects.Count );
          
          list.ForEach(
            ( l )=>
            {
              var index = effects.IndexOf( l.Item1 );
              effects[ index ] = (RokojoriCompositorEffect) effects[ index ].Duplicate();
              effects[ index ].compositorEffectID.layer = vfx.layer;
              effects[ index ].compositorEffectID.priority = vfx.layer == null ? 0 : vfx.priority;
              newEffects.Insert( insertionIndex, effects[ index ] );

              insertionIndex ++;
            }
          );
        }
        else
        {
          list.ForEach( e => newEffects.Remove( e.Item2 ) );
        }

        compositor.CompositorEffects = newEffects;
      }
      
    }

    
  }
}