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



namespace Rokojori
{
  public class TextureCombinerBuffer
  {
    int _width;
    public int width => _width;

    int _height;
    public int height => _height;

    Color[] _pixels;    

    public static TextureCombinerBuffer Create( int w, int h )
    {
      var tb = new TextureCombinerBuffer();
      tb._height = h;
      tb._width = w;
      tb._pixels = new Color[ w * h ];

      return tb;
    }

    public static TextureCombinerBuffer From( Texture2D texture )
    {
      var buffer = Create( texture.GetWidth(), texture.GetHeight() );
      buffer.CopyFrom( texture.GetImage(), 0, 0, 0, 0, texture.GetWidth(), texture.GetHeight() );

      return buffer;
    }

    public TextureCombinerBuffer Resize( int w, int h )
    {
      var buffer = Create( w, h );

      for ( int i = 0; i < w; i++ )
      {
        for ( int j = 0; j < h; j++ )
        {
          var uv = new Vector2( i / (float) w, j / (float) h );
          var pixel = SampleBilinearUV( uv );

          buffer.SetAt( i, j, pixel );
        }
      }

      return buffer;

      }

    public void CopyFrom( Image image, int sourceX, int sourceY, int ownX, int ownY, int w, int h )
    {

      for ( int i = 0; i < w; i++ )
      {
        for ( int j = 0; j < h; j++ )
        {
          var sourcePixel = image == null ? new Color( 0, 0, 0 ) : image.GetPixel( sourceX + i, sourceY + j );

          SetAt( ownX + i, ownY + j, sourcePixel );

        }
      }
    }


    public void CopyTo( int ownX, int ownY, int targetX, int targetY, int w, int h, TextureCombinerBuffer output )
    {
      for ( int i = 0; i < w; i++ )
      {
        for ( int j = 0; j < h; j++ )
        {
          var ownPixel = GetAt( ownX + i, ownY + j );

          output.SetAt( targetX + i, targetY + j, ownPixel );

        }
      }
    }

    public void CopyTo( int ownX, int ownY, int targetX, int targetY, int w, int h, Image image )
    {
      for ( int i = 0; i < w; i++ )
      {
        for ( int j = 0; j < h; j++ )
        {
          var ownPixel = GetAt( ownX + i, ownY + j );
          image.SetPixel( targetX + i, targetY + j, ownPixel );

        }
      }
    }

    public int ComputeIndexFromPosition( int x, int y )
    {
      return x + y * _width;
    }

    public void SetAt( int x, int y, Color value )
    {
      SetIndexed( ComputeIndexFromPosition( x, y ), value );
    }
    
    public void SetIndexed( int index, Color value )
    {
      _pixels[ index ] = value;
    }

    public Color GetAt( int x, int y )
    {
      return GetIndexed( ComputeIndexFromPosition( x, y ) );
    }

    public Color GetIndexed( int index )
    {
      return _pixels[ index ];
    } 

    public Color SampleNearestUV( Vector2 uv )
    {
      return SampleNearestImage( uv.X * width, uv.Y * height );
    }

    public Color SampleNearestImage( float imageDimensionsX, float imageDimensionsY )
    {
      return GetAt( Mathf.RoundToInt( imageDimensionsX ), Mathf.RoundToInt( imageDimensionsY ) ); 
    }    

    public Color SampleBilinearUV( Vector2 uv )
    {
      return SampleBilinearImage( uv.X * width, uv.Y * height );
    }

    public Color SampleBilinearImage( float imageDimensionsX, float imageDimensionsY )
    {
      var lowX  = Mathf.FloorToInt( imageDimensionsX );
      var highX = Mathf.Min( lowX + 1, width - 1 );
      var lerpX = imageDimensionsX - lowX;

      
    
      var lowY  = Mathf.FloorToInt( imageDimensionsY );
      var highY = Mathf.Min( lowY + 1, height - 1 );
      var lerpY = imageDimensionsY - lowY;


      var ll = GetAt( lowX, lowY );
      var hl = GetAt( highX, lowY );

      var lh = GetAt( lowX, highY );
      var hh = GetAt( highX, highY );

      var upper = ll.Lerp( hl, lerpX );
      var lower = lh.Lerp( hh, lerpX );

      return upper.Lerp( lower, lerpY ); 
    }
  }
}