using Godot;
using System.Collections.Generic;

namespace Rokojori
{ 
  public class UIFlowLayout
  {
    public class ControlPlacement
    {
      public Control control;
      public float offset;
      public float width;
      public float height;

      public float yOffset = 0;
      
      public float maxX => offset + width;

      public void AlignVertically( float maxHeight, float alignment )
      {
        var bottomAlignement = maxHeight - height;

        yOffset = Mathf.Lerp( 0, bottomAlignement, alignment );
      }

      public void Apply( Vector2 parentOffset )
      {
        if ( UIStyling.CanPosition( control ) )
        {
          var container = (UIStylePropertyContainer) control;

          
          var offsetX  = UINumber.Compute( control, UIStyleNumberProperty.Left, 0 ) - 
                         UINumber.Compute( control, UIStyleNumberProperty.Right, 0 );
                         
          var offsetY  = UINumber.Compute( control, UIStyleNumberProperty.Top, 0 ) - 
                         UINumber.Compute( control, UIStyleNumberProperty.Bottom, 0 );

          UILayouting.SetPosition( control, parentOffset + new Vector2( offset + offsetX, yOffset + offsetY ) );
        }
        else
        { 
          control.Position = parentOffset + new Vector2( offset, yOffset );
        }
      }
    }

    public class Line
    {
      public float y;
      public float size;
      public float xOffset;
      public List<ControlPlacement> placements = new List<ControlPlacement>();

      public float maxY => y + size;
      public float maxX => placements.Count == 0 ? 0 : placements[ placements.Count -1 ].maxX ;
    }

    public static void Apply( UIRegion region )
    {      
      var lines = CreateLines( region );

      AdjustLines( region, lines );

      var maxWidth = 0f;
      var maxHeight = 0f;
      var maxVerticalPlacementOffset = 0f;
      var maxLineY = lines.Count > 0 ? lines[ lines.Count - 1 ].maxY : 0;

      region.contentSize.X = 0f;
      region.contentSize.Y = maxLineY;

      lines.ForEach( l => { region.contentSize.X = Mathf.Max( region.contentSize.X, l.maxX ); } );


      if ( UINumber.IsNullOrNone( region, UIStyleNumberProperty.Width ) )
      {
        maxWidth = region.contentSize.X;
      }
      else
      {
        maxWidth = UINumber.Compute( region, UIStyleNumberProperty.Width ); 
      }

      if ( ! UINumber.IsNullOrNone( UIStyle.GetUINumberProperty( region, UIStyleNumberProperty.Height ) ) )
      {
        maxHeight = UINumber.Compute( region, UIStyleNumberProperty.Height );         
        maxVerticalPlacementOffset = maxHeight - maxLineY;       

      }
      else if ( lines.Count > 0 )
      {
        maxHeight = maxLineY;
      }  

      var margin     = UINumber.Compute( region, UIStyleNumberProperty.Margin, 0 );
      var marginLeft = margin + UINumber.Compute( region, UIStyleNumberProperty.MarginLeft, 0 );
      var marginTop  = margin + UINumber.Compute( region, UIStyleNumberProperty.MarginTop, 0 ); 

      var marginRight = margin + UINumber.Compute( region, UIStyleNumberProperty.MarginRight, 0 );
      var marginBottom = margin + UINumber.Compute( region, UIStyleNumberProperty.MarginBottom, 0 ); 

      
      var verticalPlacementOffset = UINumber.Compute( region, UIStyleNumberProperty.VerticalPlacement, 0, maxVerticalPlacementOffset );

      // if ( region.Name == "Audio" )
      // {
      //   RJLog.Log( "Audio Placements", 
      //     "maxHeight:", maxHeight, 
      //     "maxLineY:", maxLineY,
      //     "maxVerticalPlacementOffset", maxVerticalPlacementOffset,
      //     "verticalPlacementOffset", verticalPlacementOffset
      //   );
      // }


      var marginOffset = new Vector2( marginLeft, marginTop + verticalPlacementOffset );

      region.contentOffset = marginOffset;

      PlaceControls( region, lines, marginOffset );

      var alignmentWidth = maxWidth + marginLeft + marginRight;
      var horizontalAlignment = UINumber.Compute( region, UIStyleNumberProperty.HorizontalAlignment, 0 );
      region.contentOffset.X = Mathf.Lerp( 0, alignmentWidth - region.contentSize.X, horizontalAlignment / 100f );

      var verticalMargins = marginTop + marginBottom;
      var horizontalMargins = marginLeft + marginRight;

      region.Size = new Vector2( maxWidth + horizontalMargins, maxHeight + verticalMargins );

      if ( UIStyle.Position( region ) == UIPosition.Parent_Anchor )
      {
        UILayouting.SetPositionInParentAnchor( region );
      }

      Nodes.ForEachDirectChild<Control>( region,
        child =>
        {
          
          var styleContainer = child as UIStylePropertyContainer;

          if ( styleContainer == null || UIStyle.Position( styleContainer ) != UIPosition.Parent_Anchor )
          {
            return;
          }

          UILayouting.UpdateChild( child );
          UILayouting.SetPositionInParentAnchor( styleContainer );
          
        }
      );
    }

    static List<Line> CreateLines( UIRegion region )
    {
      var x = 0f;
      var width = UINumber.Compute( region, UIStyleNumberProperty.Width, 100000000f );

      var lines = new List<Line>();
      var currentLine = new Line();

      var elementSpacing = UINumber.Compute( region, UIStyleNumberProperty.ElementSpacing, 0f );
      var lineSpacing    = UINumber.Compute( region, UIStyleNumberProperty.LineSpacing, 0f );

      Nodes.ForEachDirectChild<Control>( region, c => UILayouting.UpdateChild( c ) );
      

      Nodes.ForEachDirectChild<Control>( region,
        child =>
        {
          if ( ! child.Visible )
          {
            return;
          }

          var styleContainer = child as UIStylePropertyContainer;

          if ( styleContainer != null && UIStyle.Position( styleContainer ) == UIPosition.Parent_Anchor )
          {
            return;
          }
          
          var cWidth  = UILayouting.GetWidth( child );
          var cHeight = UILayouting.GetHeight( child );

          var nextEndX = x + cWidth + elementSpacing;

          var lineWrap = UIStyle.LineWrap( styleContainer );

          lineWrap = lineWrap == UILineWrap.___ ? UILineWrap.Wrap_On_Overflow : lineWrap;
          
          if ( UILineWrap.Wrap_Never != lineWrap && ( UILineWrap.Wrap_Always == lineWrap || nextEndX > width ) )
          {
            lines.Add( currentLine );
            currentLine = new Line();
            x = 0;
            currentLine.y = lines[ lines.Count - 1 ].maxY + lineSpacing;
            nextEndX = cWidth + elementSpacing;
          }    

          var placement = new ControlPlacement();
          placement.offset = x;
          placement.width = cWidth;
          placement.height = cHeight;
          placement.control = child;

          currentLine.placements.Add( placement );

          currentLine.size = Mathf.Max( currentLine.size, cHeight );

          x = nextEndX;
        }
      );

      if ( lines.Count > 0 )
      {
        currentLine.y = lines[ lines.Count - 1 ].maxY + lineSpacing;
      }
      
      lines.Add( currentLine );

      return lines;
    }

    


    static void AdjustLines( UIRegion region, List<Line> lines )
    {      
      var verticalAlignment = UINumber.Compute( region, UIStyleNumberProperty.VerticalAlignment, 0.5f, 1 );

      lines.ForEach(  line => AdjustVerticalAlignment( region, line, verticalAlignment ) );

      if ( UINumber.IsNullOrNone( region, UIStyleNumberProperty.Width ) )
      {
        return;
      }

      var horizontalAlignment = UINumber.Compute( region, UIStyleNumberProperty.HorizontalAlignment, 0.5f, 1 );
      var maxWidth = UINumber.Compute( region, UIStyleNumberProperty.Width, 0 );

      lines.ForEach( line => AdjustHorizontalAlignment( region, line, horizontalAlignment, maxWidth ) );

    }

    static void AdjustVerticalAlignment( UIRegion region, Line line, float verticalAlignment )
    {
      line.placements.ForEach( p => p.AlignVertically( line.size, verticalAlignment ) );
    }

     static void AdjustHorizontalAlignment( UIRegion region, Line line, float horizontalAlignment, float maxWidth )
    {
      var lineWidth = line.maxX;
      var maxOffset = maxWidth - lineWidth;

      line.xOffset = horizontalAlignment * maxOffset;
    }


    static void PlaceControls( UIRegion region, List<Line> lines, Vector2 marginOffset )
    {
      lines.ForEach(
        line =>
        {
          var offset = new Vector2( line.xOffset, line.y ) + marginOffset;
          line.placements.ForEach( p => p.Apply( offset ) );
        }
      );
    }
  }


}