using Godot;
using System.Collections.Generic;

namespace Rokojori
{  
  public class QueuedMessageData
  {
    public BitView view;
    public List<int> targetMembers;
  } 

  public class NetworkTransport
  {
    public static readonly List<int> ALL = new List<int>();
    
    public EventSlot<NetworkMessage> onMessage = new EventSlot<NetworkMessage>();

    protected List<QueuedMessageData> reliableMessages   = new List<QueuedMessageData>();
    protected List<QueuedMessageData> unreliableMessages = new List<QueuedMessageData>();

    public void SendMessage( BitView view, List<int> targetMembers, bool reliable = true )
    {
      RJLog.Log( "SendMessage", view, reliable, targetMembers );

      var data = new QueuedMessageData();
      data.view = view;
      data.targetMembers = targetMembers;

      if ( reliable )
      {
        reliableMessages.Add( data );
      }
      else
      {
        unreliableMessages.Add( data );
      }      
    } 
    
  }

  public class ExtendedNetworkTransport:NetworkTransport
  { 
    public void HandleIncomingMessage( long id, byte[] data )
    {
      var view = BitView.From( data );

      var nm = new NetworkMessage();
      nm.view = view;
      nm.sender = id;

      var nme = new NetworkMessageEvent();
      nme.message = nm;
      nme.bitOffset = 0;

      var manager = NetworkManager.Get();
      var references = manager.references;

      while ( nme.bitOffset < nm.view.numBits )
      {
        var offsetStart = nme.bitOffset;

        var target    = view.ReadUIntVL8();
        var netMember = references.GetMember( target );

        if ( netMember == null )
        {
          RJLog.Log( "ID not found:", target );
          return;
        }

        nme.bitOffsetMessageStart = view.pointerPosition;
        
        netMember.ReceiveNetworkMessage( nme );

        if ( view.pointerPosition <= offsetStart )
        {
          RJLog.Log( "Invalid pointer position: is smaller/equals:", offsetStart, ">>", view.pointerPosition );
          return;
        }

        nme.bitOffset = view.pointerPosition;
        nme.bitOffsetMessageStart = nme.bitOffset;
      }      

      
    }

    public void ProcessQueuedMessages()
    {
      var nm = Unique<NetworkManager>.Get();
      
      if ( nm.backend == null )
      {
        reliableMessages.Clear();
        unreliableMessages.Clear();
        return;
      } 

      if ( reliableMessages.Count > 0 || unreliableMessages.Count > 0 )
      {
        RJLog.Log( "ProcessQueuedMessages", reliableMessages.Count, unreliableMessages.Count );
      }
      

      for ( int i = 0; i < reliableMessages.Count; i++ )
      {
        var cm = reliableMessages[ i ];
        RJLog.Log( "Sending message:", cm );
        nm.backend.SendMessage( cm.view.GetBytes(), cm.targetMembers, true );
      }

      for ( int i = 0; i < unreliableMessages.Count; i++ )
      {
        var cm = unreliableMessages[ i ];
        RJLog.Log( "Sending message:", cm );
        nm.backend.SendMessage( cm.view.GetBytes(), cm.targetMembers, false );
      }

      reliableMessages.Clear();
      unreliableMessages.Clear();
    }
  }
}