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

namespace Rokojori
{
  [Tool]
  [GlobalClass]
  public partial class TextureAttributes:Node
  {
    public enum Size
    {
      _16,
      _32,
      _64,
      _128,
      _256,
      _512,
      _1024,
      _2048
    }

    public enum Channel
    {
      Red, Green, Blue, Alpha
    }

    public enum OperatorType
    {
      Set, Add, Subtract, Multiply
    }

    [Export]
    public Size sizeType =Size._64;

    [Export]
    public Image image;

    [Export]
    public Image.Format format = Image.Format.Rgb8;

    [Export] 
    public ImageTexture texture;

    [Export]
    public Color fillColor;

    List<System.Action<Image>> _updates = new List<System.Action<Image>>();

    public int size => _size;
    int _size = -1;

    [Export]
    public bool autoUpdate = true;

    [ExportToolButton( "Create") ]
    public Callable createButton => Callable.From(
      ()=>{ Create(); }
    );

    [ExportGroup( "Testing" )]
    [Export]
    public int testIndex;
    [Export]
    public Color allValue = new Color( 0, 0, 0 );

    [Export]
    public Color testValue = new Color( 1, 1, 1 );


    [ExportToolButton( "Test") ]
    public Callable testButton => Callable.From(
      ()=>{ Test(); }
    );  

   

    public void Test()
    {
      UpdateSize();

      image.Fill( allValue );

      var testUV = GetUV( testIndex );

      this.LogInfo( "index", testIndex, "uv", testUV, "value:", testValue );
      image.SetPixelv( testUV, testValue );

      texture.Update( image ); 
    }

    public override void _Process( double delta )
    {
      if ( ! autoUpdate )
      {
        return;
      }

      if ( _updates.Count > 0 )
      {
        UpdateSize();
        Flush();
      }

    }

    void UpdateSize()
    {
      _size = Mathf.RoundToInt( Mathf.Pow( 2, 4 + (int)(sizeType) ) );
    }
    
    public void Create()
    {
      UpdateSize();

      image = Image.CreateEmpty( _size, _size, false, format );
      image.Fill( new Color( 0, 0, 0, 0 ) );

      texture = ImageTexture.CreateFromImage( image );

    }

    public Vector2I GetUV( int index ) 
    {
      return new Vector2I(  index % _size, index / _size );
    } 

    public void SetColor( int index, Color value )
    {
      Update(
        ( Image i ) =>
        {
          var uv = GetUV( index );
          i.SetPixelv( uv, value );
        }
      );
    }

    float ApplyOperator( float leftValue, float rightValue, OperatorType operatorType )
    {
      if ( OperatorType.Set == operatorType )
      {
        return rightValue;
      }

      if ( OperatorType.Add == operatorType )
      {
        return leftValue + rightValue;
      }

      if ( OperatorType.Subtract == operatorType )
      {
        return leftValue - rightValue;
      }

      if ( OperatorType.Multiply == operatorType )
      {
        return leftValue * rightValue;
      }

      return leftValue;

    }

    public void SetChannel( int index, float value, Channel channel, OperatorType operatorType = OperatorType.Set )
    {
      Update(
        ( Image i ) =>
        {
          var uv = GetUV( index );
          var currentValue = i.GetPixelv( uv );

          if ( channel == Channel.Red )
          {
            currentValue.R = ApplyOperator( currentValue.R, value, operatorType );
          }
          else if ( channel == Channel.Green )
          {
            currentValue.G = ApplyOperator( currentValue.G, value, operatorType );
          }
          else if ( channel == Channel.Blue )
          {
            currentValue.B = ApplyOperator( currentValue.B, value, operatorType );
          }
          else if ( channel == Channel.Alpha )
          {
            currentValue.A = ApplyOperator( currentValue.A, value, operatorType );
          }

          i.SetPixelv( uv, currentValue );
        }
      );
    }

    public void Update( System.Action<Image> action )
    {
      _updates.Add( action );
    }

    public void Flush()
    {
      _updates.ForEach( u => u( image ) );
      _updates.Clear();
      texture.Update( image );
    }
  }

}