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

namespace Rokojori
{ 
  [Tool]
  [GlobalClass,Icon("res://addons/rokojori_action_library/Icons/UIText.svg")]
  public partial class UIText:Label,UIStylePropertyContainerNode, iLocalizable, UIHolderControl, IAssemblyReload
  {
    
    LocalizedString _locale;

    [Export]
    public LocalizedString locale
    {
      get => _locale;

      set { _locale = value; UpdateLocalization(); }
    }

    public void Set( string value )
    {
      locale = LocaleText.Create( value );
    }

    [Export]
    public bool refreshText 
    {
      get => false;
      set { if ( value ) UpdateLocalization(); UpdateFont(); }
    }

    [Export]
    public bool disableLocalization = false;

    public void UpdateLocalization()
    {
      if ( disableLocalization )
      {
        return;
      }

      Text = LocalizedString.Get( _locale );

      _textDirty = true;

    }

    public void OnAssemblyReloaded()
    {
      UpdateLocalization();
      UpdateFont();
    }

    
    [Export]
    public bool alwaysMinimumSize = true;

    UIStyle _parentStyle;
    [Export]
    public UIStyle parentStyle
    {
      get => _parentStyle;
      set { _parentStyle = value; UpdateFont(); }
    }

    Font _font;
    [ExportGroup( "Font" )]
    [Export]
    public Font font
    {
      get => _font;
      set { _font = value; UpdateFont(); }
    }

    
    [Export]
    public UINumber fontSize;
    [Export]
    public UIColor fontColor;

    [Export]
    public UINumber outlineSize;
    [Export]
    public UIColor outlineColor;

    [Export]
    public UINumber shadowSize;
    [Export]
    public UIColor shadowColor;

    [Export]
    public UINumber shadowOffsetX;
    [Export]
    public UINumber shadowOffsetY;

    [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;

    public Font GetFont()
    {
      return font;
    }

    void UpdateFont()
    {
      var resolvedFont = UIStyle.Font( this );

      if ( resolvedFont != null )
      {
        RemoveThemeFontOverride( "font" );
        AddThemeFontOverride( "font", resolvedFont );

        _textDirty = true;
      }

    }

    

    string hoverID = IDGenerator.GenerateID();

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

        var currentHoverCursor = UIStyle.ResolveHoverCursor( this );

        if ( currentHoverCursor != null )
        {
          currentHoverCursor.ApplyCursor( this );
          appliedCursor = currentHoverCursor;
          this.LogInfo( "Set Cursor on Hover:", appliedCursor );
          // Input.SetDefaultCursorShape( hoverCursorShape );
          // MouseDefaultCursorShape = hoverCursorShape;
        }
        
      };

      MouseExited += ()=>
      {

        RemoveUISelectorFlag( UISelectorFlag.Hover, hoverID );
        this.SetDirty();

        if ( appliedCursor != null )
        {
          this.LogInfo( "Reset Cursor on Exit");
          appliedCursor.ClearCursor( this );
        }
      };
    }

    int _uiAncestorDepth;

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

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

    public int GetUIAncestorDepth()
    {
      return _uiAncestorDepth;
    }

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

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

    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.SetDirty();
      }
    }
    
    List<UISelectorFlag> _selectorFlags = [];
    List<UISelectorFlag> _parentSelectorFlags = [];
    
    public List<UISelectorFlag> GetUISelectorFlags()
    {
      return _selectorFlags;
    }

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

    public override void _GuiInput( InputEvent inputEvent )
    {
      if ( ! handleMouseEvents )
      {
        return;
      }

      if ( inputEvent is InputEventMouseButton mb )
      {
        if ( mb.Pressed )
        {
          if ( mb.ButtonIndex == MouseButton.Left )
          {
            Action.Trigger( onLeftClick );
          }

          if ( mb.ButtonIndex == MouseButton.Middle )
          {
            Action.Trigger( onMiddleClick );
          }

           if ( mb.ButtonIndex == MouseButton.Right )
          {
            Action.Trigger( onRightClick );
          }
        }
      }     
   
    }

 
    [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;
    }

    LabelSettings _labelSettings;

    

    public LabelSettings uiTextLabelSettings
    {
      get 
      {
        if ( _labelSettings == null )
        {
          _labelSettings = new LabelSettings();
        }

        LabelSettings = _labelSettings;
        return _labelSettings;
      }

      set 
      {
        _labelSettings = value.Duplicate() as LabelSettings;
        LabelSettings = _labelSettings;
      }
    }

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

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


    [Export]
    public bool handleMouseEvents = false;

    [Export]
    public Action onLeftClick;

    [Export]
    public Action onMiddleClick;

    [Export]
    public Action onRightClick;

    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.___;
    } 

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

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

    

    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;

        case UIStyleNumberProperty.FontSize: return fontSize;
        case UIStyleNumberProperty.FontOutlineSize: return outlineSize;
        case UIStyleNumberProperty.FontShadowSize: return shadowSize;
        case UIStyleNumberProperty.FontShadowOffsetX: return shadowOffsetX;
        case UIStyleNumberProperty.FontShadowOffsetY: return shadowOffsetY;
      }


      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.FontSize: { fontSize = number; _textDirty = true; } break;
        case UIStyleNumberProperty.FontOutlineSize: { outlineSize = number; _styleDirty = true; } break;
        case UIStyleNumberProperty.FontShadowSize: { shadowSize = number; _styleDirty = true; } break;
        case UIStyleNumberProperty.FontShadowOffsetX: { shadowOffsetX = number; _styleDirty = true; } break;
        case UIStyleNumberProperty.FontShadowOffsetY: { shadowOffsetY = number; _styleDirty = true; } 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();
    }

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

    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();
    }

    bool _styleDirty = false;
    bool _textDirty = false;


    public class CachedLabelSettings
    {
      public CachedUINumber fontSize = CachedUINumber.AsInt( UIStyleNumberProperty.FontSize ).SetMin( 1 );
      public CachedUINumber outlineSize = CachedUINumber.AsInt( UIStyleNumberProperty.FontOutlineSize );
      public CachedUINumber shadowSize = CachedUINumber.AsInt( UIStyleNumberProperty.FontShadowSize );
      public CachedUINumber shadowOffsetX = CachedUINumber.AsFloat( UIStyleNumberProperty.FontShadowOffsetX );
      public CachedUINumber shadowOffsetY = CachedUINumber.AsFloat( UIStyleNumberProperty.FontShadowOffsetY );

      public CachedUIColor fontColor = CachedUIColor.Create( UIStyleColorProperty.FontColor );
      public CachedUIColor outlineColor = CachedUIColor.Create( UIStyleColorProperty.FontOutlineColor );
      public CachedUIColor shadowColor = CachedUIColor.Create( UIStyleColorProperty.FontShadowColor );

      public CachedUIColor modulationColor = CachedUIColor.Create( UIStyleColorProperty.ModulationColor );
      public CachedUIColor selfModulationColor = CachedUIColor.Create( UIStyleColorProperty.SelfModulationColor );

      public bool HasChanged()
      {
        if ( fontSize.changed || outlineSize.changed || shadowSize.changed || fontColor.changed )
        {
          return true;
        }

        if ( modulationColor.changed || selfModulationColor.changed )
        {
          return true;
        }

        if ( outlineSize.valueInt > 0  && outlineColor.changed )
        {
          return true;
        }

        if ( shadowSize.valueInt > 0 &&  ( shadowOffsetX.changed || shadowOffsetY.changed || shadowColor.changed ) )
        {
          return true;
        }

        return false;
      }


      bool _wasCreated = false;
      public bool wasCreated => _wasCreated;

      public LabelSettings CreateLabelSettings()
      {
        _wasCreated = true;
        var l = new LabelSettings();
        l.FontSize = fontSize.valueInt;
        l.OutlineSize = outlineSize.valueInt;
        l.ShadowSize = shadowSize.valueInt;

        l.FontColor = fontColor.color;

        if ( outlineSize.valueInt > 0 )
        {
          l.OutlineColor = outlineColor.color;
        }

        if ( shadowSize.valueInt > 0 )
        {
          l.ShadowColor = shadowColor.color;
          l.ShadowOffset = new Vector2( shadowOffsetX.value, shadowOffsetY.value );
        }

        

        return l;
      }
    }

    CachedLabelSettings cachedLabelSettings = new CachedLabelSettings();

    public void Update()
    {

      // FONT SIZE
      // VIEW WIDTH/HEIGHT
      // PARENT WIDTH/HEIGHT
      // PARENT STYLE

      // PROP

      var ui = GetUI();

      var updateTextField = _textDirty || ui.fontSizeChanged || ui.sizeChanged;

      if ( ui.fontSizeChanged || ! cachedLabelSettings.wasCreated )
      {
        cachedLabelSettings.fontSize.alternative = UINumber.em( this );
        cachedLabelSettings.fontSize.relative = UINumber.em( this ) / 100f ;         
      }

      cachedLabelSettings.fontSize.Compute( this );
      cachedLabelSettings.outlineSize.Compute( this );
      cachedLabelSettings.shadowSize.Compute( this );
      cachedLabelSettings.fontColor.Compute( this );
      cachedLabelSettings.modulationColor.Compute( this );
      cachedLabelSettings.selfModulationColor.Compute( this );
      

      
      if ( cachedLabelSettings.outlineSize.valueInt > 0 )
      {
        cachedLabelSettings.outlineColor.Compute( this );
      } 

      if ( cachedLabelSettings.shadowSize.valueInt > 0 )
      {
        cachedLabelSettings.shadowOffsetX.Compute( this );
        cachedLabelSettings.shadowOffsetY.Compute( this );
        cachedLabelSettings.shadowColor.Compute( this );
      }

      updateTextField = updateTextField || cachedLabelSettings.fontSize.changed;

      if ( cachedLabelSettings.HasChanged() || ! cachedLabelSettings.wasCreated )
      {
        uiTextLabelSettings = cachedLabelSettings.CreateLabelSettings();
        Modulate = cachedLabelSettings.modulationColor.color;
        SelfModulate = cachedLabelSettings.selfModulationColor.color;
      }

      // uiTextLabelSettings.FontSize = Mathf.Max( 1,
      //   UINumber.ComputeInt( this, UIStyleNumberProperty.FontSize, UINumber.em( this ), UINumber.em( this ) / 100f ) );
     
      // uiTextLabelSettings.OutlineSize = 
      //   UINumber.ComputeInt( this, UIStyleNumberProperty.FontOutlineSize, 0 );         

      // uiTextLabelSettings.ShadowSize = 
      //   UINumber.ComputeInt( this, UIStyleNumberProperty.FontShadowSize, 0 );

      // uiTextLabelSettings.ShadowOffset = new Vector2(
      //   UINumber.Compute( this, UIStyleNumberProperty.FontShadowOffsetX, 0 ),
      //   UINumber.Compute( this, UIStyleNumberProperty.FontShadowOffsetY, 0 )
      // );  

      // uiTextLabelSettings.FontColor = 
      //   UIColor.Compute( this, UIStyleColorProperty.FontColor, "", Colors.White );
      
      // uiTextLabelSettings.OutlineColor = 
      //   UIColor.Compute( this, UIStyleColorProperty.FontOutlineColor, "", Colors.Transparent );

      // uiTextLabelSettings.ShadowColor = 
      //   UIColor.Compute( this, UIStyleColorProperty.FontShadowColor, "", Colors.Black );

                

      if ( ! updateTextField )
      {
        return;
      }

      UpdateMinimumSize();

      if ( alwaysMinimumSize )
      {            
        if ( AutowrapMode == TextServer.AutowrapMode.Word )
        {
          AutowrapMode = TextServer.AutowrapMode.Off;
          var minSize = GetMinimumSize();
          AutowrapMode = TextServer.AutowrapMode.Word;

          var w = UINumber.Compute( this, UIStyleNumberProperty.Width, minSize.X, minSize.X / 100f );
          var h = UINumber.Compute( this, UIStyleNumberProperty.Height, minSize.Y, minSize.Y / 100f );

          CustomMinimumSize = new Vector2( Mathf.Min( minSize.X, w ), 0 );
        }

        Size = GetMinimumSize();
      }
      else
      {
        var minSize = GetMinimumSize();

        var w = UINumber.Compute( this, UIStyleNumberProperty.Width, minSize.X, minSize.X / 100f );
        var h = UINumber.Compute( this, UIStyleNumberProperty.Height, minSize.Y, minSize.Y / 100f );

        // RJLog.Log( "Set Image Size", w, h );
        Size = new Vector2( w, h );
      }


    }
    public UIColor GetUIStyleColorProperty( UIStyleColorProperty property, string shaderPropertyName, UIStylePropertyContainer source )
    {
      switch ( property )
      {
        case UIStyleColorProperty.FontColor:         return fontColor;
        case UIStyleColorProperty.FontOutlineColor:  return outlineColor;
        case UIStyleColorProperty.FontShadowColor:   return shadowColor;
        case UIStyleColorProperty.ModulationColor:      return modulationColor;
        case UIStyleColorProperty.SelfModulationColor:  return selfModulationColor;
      } 

      return null;
    }

    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 )
      {        
        _uiAncestorDepth = -1;
        // this.LogInfo( "No UI in parents >", HierarchyName.Of( this ) );

        return null;        
      }

      if ( computeDepth )
      { 
        ComputeUIAncestorDepth();
      }

      return ui;
    }
  }
}