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

namespace Rokojori
{ 
  [Tool]
  [GlobalClass,Icon("res://addons/rokojori_action_library/Icons/UIImage.svg")]
  public partial class UIImage:TextureRect, UIStylePropertyContainerNode, UIHolderControl, IAssemblyReload
  { 
    protected UIImageType _imageType;
    [Export]
    public UIImageType imageType
    {
      get => _imageType;
      set 
      {        
        if ( _imageType != value  )
        {
          if ( _imageType != null )
          {
            _imageType.Clear( this );
          }
        }

        _imageType = value;

        UpdateImageType();
      }
    }

    UI ui;

    public void SetUI( UI ui, bool computeDepth = true )
    {
      this.ui = ui;
      
      if ( computeDepth )
      {
        ComputeUIAncestorDepth();
      }

    }

    
    public UI GetUI( bool computeDepth = true )
    {
      if ( this.ui != null )
      {
        return this.ui;
      }

      var ui = this.FindParentThatIs<UI>();

      if ( ui == null )
      {
        
        this._uiAncestorDepth = -1;
        this.LogInfo( "No UI in parents >", ui );

        return null;
        
      }

      if ( computeDepth )
      {
        ComputeUIAncestorDepth();
      }
      

      return ui;
    }

    protected void UpdateImageType()
    {
      ResetImageType();
      
      if ( _imageType != null )
      {
        // this.LogInfo( "Assigning Image Type:", _imageType );
        _imageType.Assign( this );
      }
    }

    string hoverID = IDGenerator.GenerateID();    

    public override void _Ready()
    {
      MouseEntered += ()=>
      {
        AddUISelectorFlag( UISelectorFlag.Hover, hoverID );
        this.SetDirty();
        
      };

      MouseExited += ()=>
      {
        RemoveUISelectorFlag( UISelectorFlag.Hover, hoverID );
        this.SetDirty();
      };

      UpdateImageType();
      ComputeUIAncestorDepth();
    }

    public override void _EnterTree()
    {
      UpdateImageType();
      ComputeUIAncestorDepth();
    }

    public override void _ExitTree()
    {
      ui = null;
      _uiAncestorDepth = -1;
    }

    public void OnAssemblyReloaded()
    {
      UpdateImageType();
    }
    
    int _uiAncestorDepth;

    public void ComputeUIAncestorDepth()
    {
      SetUIAncestorDepth( NodesWalker.Get().GetAncestorDistance( this, GetUI( false ) ) );
    }

    public void SetUIAncestorDepth( int depth )
    {
      _uiAncestorDepth = depth;
    }

    public int GetUIAncestorDepth()
    {
      return _uiAncestorDepth;
    }

    void ResetImageType()
    {
      ExpandMode = TextureRect.ExpandModeEnum.IgnoreSize;
      StretchMode = TextureRect.StretchModeEnum.Scale;
      Material = null;
    }

    protected override void Dispose( bool disposing)
    {
      _selectorFlagReferenceCounter.Clear();
    }

    MapList<UISelectorFlag,string> _selectorFlagReferenceCounter = new MapList<UISelectorFlag, string>();

    public void AddUISelectorFlag( UISelectorFlag flag, string reference = "" )
    {
      SetSelectorFlagReference( flag, reference, true );
    }

    public void RemoveUISelectorFlag( UISelectorFlag flag, string reference = "" )
    {
      SetSelectorFlagReference( flag, reference, false );
    }

    void SetSelectorFlagReference( UISelectorFlag flag, string reference, bool enable )
    {
      if ( enable )
      {
        _selectorFlagReferenceCounter.AddIfNotPresent( flag, reference );
      }
      else
      {
        _selectorFlagReferenceCounter.Remove( flag, reference );
      }

      var numFlagsBefore = _selectorFlags.Count;
      _selectorFlags = _selectorFlagReferenceCounter.Keys.ToList();
      
      var changed = numFlagsBefore != _selectorFlags.Count;

      UISelector.UpdateParentUISelectorFlags( this );

      if ( changed )
      {
        // this.LogInfo( "Changed flag:", flag, reference );
        this.SetDirty();
      }

      
    }

    List<UISelectorFlag> _selectorFlags = [];
    List<UISelectorFlag> _parentSelectorFlags = [];
    
    public List<UISelectorFlag> GetUISelectorFlags()
    {
      return _selectorFlags;
    }

    public List<UISelectorFlag> GetParentUISelectorFlags()
    {
      return _parentSelectorFlags;
    }

    public void CopyNumberShaderPropertyFrom( CustomMaterialProperty<float> name, UINumber number, TransitionSettings transition )
    {
      SetNumberShaderProperty( name.GetPropertyName<FloatPropertyName>(), number, transition );
    }

    public void SetNumberShaderProperty( FloatPropertyName name, UINumber number, TransitionSettings transition )
    {
      if ( name == null || name.propertyName == null )
      {
        this.LogInfo( "Name is null" );
        return;
      }

      
      for ( int i = 0; i < numberProperties.Length; i++ )
      {
        if ( numberProperties[ i ] == null || numberProperties[ i ].floatPropertyName == null )
        {
          return;
        }
      }

      if ( number == null )
      {
        var index = Arrays.FindIndex( numberProperties, p => p.floatPropertyName.propertyName == name.propertyName );
        numberProperties = Arrays.RemoveIndex( numberProperties, index );
        return;
      }
      
      var shaderUINumber = Arrays.Find( numberProperties, p => p.floatPropertyName.propertyName == name.propertyName );

      if ( shaderUINumber == null )
      {
        shaderUINumber = new ShaderUINumber();
        shaderUINumber.floatPropertyName = name;
        numberProperties = Arrays.AddEntry( numberProperties, shaderUINumber );
      }

      shaderUINumber.number = number;
    }

    public void CopyColorShaderPropertyFrom( CustomMaterialProperty<Color> name, UIColor color )
    {
      SetColorShaderProperty( name.GetPropertyName<ColorPropertyName>(), color );
    }

    public void SetColorShaderProperty( ColorPropertyName name, UIColor color )
    {
      if ( name == null || name.propertyName == null )
      {
        this.LogInfo( "Name is null" );
        return;
      }

      for ( int i = 0; i < colorProperties.Length; i++ )
      {
        if ( colorProperties[ i ] == null || colorProperties[ i ].colorPropertyName == null )
        {
          return;
        }
      }

      if ( color == null )
      {
        var index = Arrays.FindIndex( colorProperties, p => p != null && p.colorPropertyName.propertyName == name.propertyName );
        colorProperties = Arrays.RemoveIndex( colorProperties, index );
        return;
      }

      var shaderUIColor = Arrays.Find( colorProperties, p => p != null && p.colorPropertyName.propertyName == name.propertyName );
      
      if ( shaderUIColor == null )
      {
        shaderUIColor = new ShaderUIColor();
        shaderUIColor.colorPropertyName = name;
        colorProperties = Arrays.AddEntry( colorProperties, shaderUIColor );
      }

      shaderUIColor.color = color;
    }

    [Export]
    public UIStyle parentStyle;

    [ExportGroup("Size & Margins")]
    [Export]
    public UINumber width;

    [Export]
    public UINumber height;


    [Export] 
    public UINumber margin;

    [Export] 
    public UINumber marginLeft;
    [Export] 
    public UINumber marginTop;
    [Export] 
    public UINumber marginRight;
    [Export] 
    public UINumber marginBottom;


    [ExportGroup( "Position" )]
    [Export]
    public UIPosition position;
    [Export]
    public UILineWrap lineWrap;
    [Export]
    public UINumber left;
    [Export]
    public UINumber top;
    [Export]
    public UINumber right;
    [Export]
    public UINumber bottom;

    
    [ExportGroup( "Rotation & Scale" )]
    [Export]
    public UINumber pivotX;
    [Export]
    public UINumber pivotY;

    [Export]
    public UINumber rotation;

    [Export]
    public UINumber scale;

    [Export]
    public UINumber scaleX;
    [Export]
    public UINumber scaleY;

    [ExportGroup( "Modulation" )]
    [Export]
    public UIColor modulationColor;
    [Export]
    public UIColor selfModulationColor;


    [ExportGroup( "Shader Properties" )]
    [Export]
    public ShaderUIColor[] colorProperties = new ShaderUIColor[ 0 ];
    // public List<ActiveStyleTransition<UIColor,ColorPropertyName>> activeShaderColorTransitions = new List<ActiveStyleTransition<UIColor,ColorPropertyName>>();
    // public List<ActiveStyleTransition<UIColor,ColorPropertyName>> GetActiveShaderUIColorTransitions()
    // {
    //   return activeShaderColorTransitions;
    // }

    [Export]
    public ShaderUINumber[] numberProperties = new ShaderUINumber[ 0 ];
    // public List<ActiveStyleTransition<UINumber,FloatPropertyName>> activeShaderNumberTransitions = new List<ActiveStyleTransition<UINumber,FloatPropertyName>>();
    // public List<ActiveStyleTransition<UINumber,FloatPropertyName>> GetActiveShaderUINumberTransitions()
    // {
    //   return activeShaderNumberTransitions;
    // }

    [ExportGroup("Transitions")]
    [Export]
    public TransitionSettingsAll transitionSettings;
    public TransitionSettingsAll GetTransitionSettingsAll()
    {
      return transitionSettings;
    }

    [Export]
    public UINumberTransition[] numberTransitions = new UINumberTransition[0];
    public UINumberTransition[] GetNumberTransitions()
    {
      return numberTransitions;
    }
    
    public List<ActiveStyleTransition<UINumber,UIStyleNumberPropertyAndName>> activeNumberTransitions = new List<ActiveStyleTransition<UINumber,UIStyleNumberPropertyAndName>>();
    public List<ActiveStyleTransition<UINumber,UIStyleNumberPropertyAndName>> GetActiveUINumberTransitions()
    {
      return activeNumberTransitions;
    }

    [Export]
    public UIColorTransition[] colorTransitions = new UIColorTransition[ 0 ];
    public UIColorTransition[] GetColorTransitions()
    {
      return colorTransitions;
    }
    
    public List<ActiveStyleTransition<UIColor,UIStyleColorPropertyAndName>> activeColorTransitions = new List<ActiveStyleTransition<UIColor,UIStyleColorPropertyAndName>>();
    public List<ActiveStyleTransition<UIColor,UIStyleColorPropertyAndName>> GetActiveUIColorTransitions()
    {
      return activeColorTransitions;
    }

    

    public UIStyle GetUIStyleParent()
    {
      return parentStyle;
    }

    public void SetUIStyleParent( UIStyle uiStyle )
    {
      parentStyle = uiStyle;
    }

    public UIPosition GetUIPosition()
    {
      return position;
    }

    public UILineWrap GetUILineWrap()
    {
      return lineWrap;
    }

    public UILayout GetUILayout()
    {
      return UILayout.___;
    }

    [ExportGroup( "Pointer" )]
    [Export]
    public UICursor hoverCursor;

    public UICursor GetHoverCursor( UIStylePropertyContainer container )
    {
      return hoverCursor;
    }


    public ShaderUIColor[] GetShaderUIColors()
    {
      return colorProperties;
    }

    public ShaderUINumber[] GetShaderUINumbers()
    {
      return numberProperties;
    }

    public UINumber GetUIStyleNumberProperty( UIStyleNumberProperty property, string shaderPropertyName, UIStylePropertyContainer source )
    {

      switch ( property )
      {
        case UIStyleNumberProperty.Width:  return width;
        case UIStyleNumberProperty.Height: return height;

        case UIStyleNumberProperty.Margin:       return margin;

        case UIStyleNumberProperty.MarginLeft:   return marginLeft;
        case UIStyleNumberProperty.MarginRight:  return marginRight;
        case UIStyleNumberProperty.MarginTop:    return marginTop;
        case UIStyleNumberProperty.MarginBottom: return marginBottom;


        case UIStyleNumberProperty.Left:   return left;
        case UIStyleNumberProperty.Right:  return right;
        case UIStyleNumberProperty.Top:    return top;
        case UIStyleNumberProperty.Bottom: return bottom;

        case UIStyleNumberProperty.PivotX:   return pivotX;
        case UIStyleNumberProperty.PivotY:   return pivotY;
        case UIStyleNumberProperty.Rotation: return rotation;

        case UIStyleNumberProperty.Scale:    return scale;
        case UIStyleNumberProperty.ScaleX:   return scaleX;
        case UIStyleNumberProperty.ScaleY:   return scaleY;
      }


      if ( UIStyleNumberProperty.FloatShaderProperty != property )
      {
        return null;
      }

      if ( imageType != null )
      {
        var uiNumber = imageType.GetUIStyleNumberProperty( property, shaderPropertyName );

        if ( uiNumber != null )
        {
          return uiNumber;
        }
      }     

      var numberProperty = numberProperties.Find( n => n.floatPropertyName.propertyName == shaderPropertyName );

      if ( numberProperty != null )
      {
        return numberProperty.number;
      }


      return null;
    }

    public void SetUIStyleNumberProperty( UIStyleNumberProperty property, UINumber number )
    {
      switch ( property )
      {
        case UIStyleNumberProperty.Left:   { left = number; } break;
        case UIStyleNumberProperty.Right:  { right = number; } break;
        case UIStyleNumberProperty.Top:    { top = number; } break;
        case UIStyleNumberProperty.Bottom: { bottom = number; } break;

        case UIStyleNumberProperty.Width:  { width = number; } break;
        case UIStyleNumberProperty.Height: { height = number; } break;

        case UIStyleNumberProperty.Margin:       { margin = number; } break;

        case UIStyleNumberProperty.MarginLeft:   { marginLeft = number; } break;
        case UIStyleNumberProperty.MarginRight:  { marginRight = number; } break;
        case UIStyleNumberProperty.MarginTop:    { marginTop = number; } break;
        case UIStyleNumberProperty.MarginBottom: { marginBottom = number; } break;

        case UIStyleNumberProperty.PivotX:   { pivotX = number; } break;
        case UIStyleNumberProperty.PivotY:   { pivotY = number; } break;
        case UIStyleNumberProperty.Rotation: { rotation = number; } break;

        case UIStyleNumberProperty.Scale:    { scale = number; } break;
        case UIStyleNumberProperty.ScaleX:   { scaleX = number; } break;
        case UIStyleNumberProperty.ScaleY:   { scaleY = number; } break;
      }

      this.SetLayoutDirtyFlag();

    }

    bool _isAnimated = false;
    int _isLayoutDirty = 3;

    public void ResetDirtyFlags()
    {
      _isAnimated = false;
      _isLayoutDirty = Mathf.Max( 0, _isLayoutDirty - 1 );
    }

    public void SetAnimatedFlag()
    {
      _isAnimated = true;
      this.SetDirty();
    }

    public void SetLayoutDirtyFlag()
    {
      _isLayoutDirty = 3;
      this.SetDirty();
    }


    public bool IsDirty()
    {
      return _isAnimated || _isLayoutDirty != 0 || this.HasActiveTransitions();
    }


    public void Update()
    {

      if ( Texture == null )
      {
        Size = new Vector2( 0, 0 );
        return;
      }

      var tw = Texture.GetWidth();
      var th = Texture.GetHeight();
      

      var w = UINumber.Compute( this, UIStyleNumberProperty.Width, tw, tw / 100f );
      var h = UINumber.Compute( this, UIStyleNumberProperty.Height, th, th / 100f );

      Size = new Vector2( w, h );
     
      if ( Material == null )
      {
        return;
      }

      var ui = GetUI();

      if ( ui == null || ui.settings == null || ui.settings.sizePropertyName == null )
      {
        return;
      }


      ui.settings.sizePropertyName.Set( Material, Size );
      
      Modulate = UIColor.Compute( this, UIStyleColorProperty.ModulationColor, "", Colors.White );
      SelfModulate = UIColor.Compute( this, UIStyleColorProperty.SelfModulationColor, "", Colors.White );

      var colorProperties = imageType != null ? imageType.GetColorShaderProperties() : [];
      var colorPropertyName = new ColorPropertyName();

      foreach ( var c in colorProperties )
      {
        var color = UIColor.Compute( this, UIStyleColorProperty.ColorShaderProperty, c, Colors.White );
        colorPropertyName.propertyName = c;
        colorPropertyName.Set( Material, color );
      } 

      var numberProperties = imageType != null ? imageType.GetNumberShaderProperties() : [];
      var numberPropertyName = new FloatPropertyName();

      foreach ( var n in numberProperties )
      {
        var relative = 100f;
        
        if ( n.EndsWith( ".x" ) )
        {
          relative = w;
        }
        else if ( n.EndsWith( ".y" ) )
        {
          relative = h;
        }


        var value = UINumber.Compute( this, UIStyleNumberProperty.FloatShaderProperty, n, 0, relative );
        numberPropertyName.propertyName = n;
        numberPropertyName.Set( Material, value );
      }                       

    }

    

    public UIColor GetUIStyleColorProperty( UIStyleColorProperty property, string shaderPropertyName, UIStylePropertyContainer source )
    {
       switch ( property )
      {
        case UIStyleColorProperty.ModulationColor:      return modulationColor;
        case UIStyleColorProperty.SelfModulationColor:  return selfModulationColor;
      } 
      
      if ( property != UIStyleColorProperty.ColorShaderProperty )
      {
        return null;
      }
     
      if ( imageType != null )
      {
        var uiColor = imageType.GetUIStyleColorProperty( property, shaderPropertyName );

        if ( uiColor != null )
        {
          return uiColor;
        }
      }      

      var shaderUIColor = colorProperties.Find( c => c.colorPropertyName.propertyName == shaderPropertyName ); 

      return shaderUIColor != null ? shaderUIColor.color : null;
        
    }

    public Font GetFont()
    {
      return null;
    }

    public Vector2 GetUISize()
    {
      return GetSize();
    }
  }
}