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

namespace Rokojori
{  
  public class RDPushConstants
  {
    protected List<float> _floats = new List<float>();
    protected int _floatIndex = 0;

    protected List<int> _ints = new List<int>();
    protected int _intIndex = 0;

    protected byte[] _bytes;

    public void Reset()
    {
      _floatIndex = 0;
      _intIndex = 0;
    }

    public int size => _floatIndex + _intIndex;

    public int internalSize => _floats.Count + _ints.Count;

    public string info 
    {
      get
      {
        var bytesInfo = _bytes == null ? "null" : (_bytes.Length + "" );
        return $"size:{size} internalSize:{internalSize} bytes:{bytesInfo} floats:{_floats.Count} ints:{_ints.Count}";
      }
    }

    public void Clear()
    {
      _floats.Clear();
      _ints.Clear();
      _bytes = null;
    }

    public void SetOrAdd( bool reset, params object[] objects )
    { 
      if ( reset )
      {
        Reset();
      }

      for ( int i = 0; i < objects.Length; i++ )
      {
        if ( objects[ i ] is int intValue)
        {
          _AddInt( intValue );
        }

        else if ( objects[ i ] is float floatValue )
        {
          _AddFloat( floatValue );
        }

        else if ( objects[ i ] is Vector2 vec2 )
        {
          _AddVector2( vec2 );
        }

        else if ( objects[ i ] is Vector2I vec2I )
        {
          _AddVector2( vec2I );
        }

        else if ( objects[ i ] is Vector3 vec3 )
        {
          _AddVector3( vec3 );
        }

        else if ( objects[ i ] is Vector3I vec3I )
        {
          _AddVector3( vec3I );
        }

        else if ( objects[ i ] is Vector4 vec4 )
        {
          _AddVector4( vec4 );
        }

        else if ( objects[ i ] is Color color )
        {
          _AddVector4( color.ToVector4() );
        }
      }
      
    }

    public void Set( params object[] objects )
    { 
      SetOrAdd( true, objects );      
    }

    protected void _AddFloat( float value )
    {
      if ( _floatIndex >= _floats.Count )
      {
        _floats.Add( value );
        _floatIndex = _floats.Count;
      }
      else
      {
        _floats[ _floatIndex ] = value;
        _floatIndex ++;
      }
    }

    protected void _AddInt( int value )
    {
      if ( _intIndex >= _ints.Count )
      {
        _ints.Add( value );
        _intIndex = _ints.Count;
      }
      else
      {
        _ints[ _intIndex ] = value;
        _intIndex ++;
      }
    }

    public void Add( params float[] values )
    {
      for ( int i = 0; i < values.Length; i++ )
      {
        _AddFloat( values[ i ] );
      }
    }

    protected void _AddVector2( Vector2 value )
    {
      _AddFloat( value.X );
      _AddFloat( value.Y ); 
    }

    protected void _AddVector2I( Vector2I value )
    {
      _AddInt( value.X );
      _AddInt( value.Y ); 
    }

    public void Add( params Vector2[] values )
    {
      for ( int i = 0; i < values.Length; i++ )
      {
        _AddVector2( values[ i ] );
      }
    }

    public void Add( params Vector2I[] values )
    {
      for ( int i = 0; i < values.Length; i++ )
      {
        _AddVector2I( values[ i ] );
      }
    }

    protected void _AddVector3( Vector3 value )
    {
      _AddFloat( value.X );
      _AddFloat( value.Y ); 
      _AddFloat( value.Z ); 
    }

    protected void _AddVector3I( Vector3I value )
    {
      _AddInt( value.X );
      _AddInt( value.Y ); 
      _AddInt( value.Z ); 
    }

    public void Add( params Vector3[] values )
    {
      for ( int i = 0; i < values.Length; i++ )
      {
        _AddVector3( values[ i ] );
      }
    }
    
    public void Add( params Vector3I[] values )
    {
      for ( int i = 0; i < values.Length; i++ )
      {
        _AddVector3I( values[ i ] );
      }
    }

    protected void _AddVector4( Vector4 value )
    {
      _AddFloat( value.X );
      _AddFloat( value.Y ); 
      _AddFloat( value.Z ); 
      _AddFloat( value.W ); 
    } 

    public void Add( params Vector4[] values )
    {
      for ( int i = 0; i < values.Length; i++ )
      {
        _AddVector4( values[ i ] );
      }
    }
 

    

    public void Add( params int[] values )
    {
      for ( int i = 0; i < values.Length; i++ )
      {
        _AddInt( values[ i ] );
      }
    }

    public byte[] bytes
    {
      get 
      {
        var numBytes = ( _intIndex + _floatIndex ) * 4;

        while ( numBytes % 16 != 0 )
        {
          numBytes ++;
        }

        if ( _bytes == null || _bytes.Length != numBytes )
        {
          _bytes = new byte[ numBytes ];
        }

        for ( int i = 0; i < _floats.Count; i++ )
        {
          var floatBytes = BitConverter.GetBytes( _floats[ i ] );
          Array.Copy( floatBytes, 0, _bytes, i * 4, 4 );
        }

        var floatsOffset = _floats.Count * 4;

        for ( int i = 0; i < _ints.Count; i++ )
        {
          var intBytes = BitConverter.GetBytes( _ints[ i ] );
          Array.Copy( intBytes, 0, _bytes, i * 4 + floatsOffset, 4 );
        }

        var intsOffset = floatsOffset + _ints.Count * 4;

        for ( int i = intsOffset; i < _bytes.Length; i++ )
        {
          _bytes[ i ] = 0;
        }

        return _bytes;
      }
    }
    
  }   
}