using Godot;
using System.Collections.Generic;

namespace Rokojori
{  
  [Tool][GlobalClass]
  public partial class LANNetworkBackend:NetworkBackend
  {    
    SceneMultiplayer multiplayer;
    ENetMultiplayerPeer peer;

    public override void StartSession( NetworkSessionRequest request )
    {
      if ( _state != NetworkSessionConnectionState.Not_Connected )
      {
        return;
      }

      AssignSessionRequestVariables( request );

      RJLog.Log( "Starting Session:", request );

      _state = NetworkSessionConnectionState.Connecting_As_Server;
      
      multiplayer = new SceneMultiplayer();

      ConnectListeners();


      peer = new ENetMultiplayerPeer();       

      var result = peer.CreateServer( port, maxMembers );

      if ( Error.Ok != result )
      { 
        _state = NetworkSessionConnectionState.Not_Connected;    

        RJLog.Log( "Session Creation Failed >>", result );   
        return;
      }

      _state = NetworkSessionConnectionState.Connected_As_Server;
      _sessionManager.SetInSessionState();
      
      Action.Trigger( _manager.onStartedSession );


      AssignMultiplyer();
    }

    public override void JoinSession( NetworkSessionRequest request )
    {
      if ( _state != NetworkSessionConnectionState.Not_Connected )
      {
        if ( NetworkSessionConnectionState.Connected_As_Client == _state )
        {
          SendData();
        }

        return;
      }

      AssignSessionRequestVariables( request );

      RJLog.Log( "Joining Session:", request );

      _state = NetworkSessionConnectionState.Connecting_As_Client;
      
      multiplayer = new SceneMultiplayer();

      ConnectListeners();

      peer = new ENetMultiplayerPeer();       

      var result = peer.CreateClient( serverIP, port, maxMembers );

      if ( Error.Ok != result )
      { 
        RJLog.Log( "Joining Session Failed >>", result );   
        _state = NetworkSessionConnectionState.Not_Connected;       
        return;
      }

      AssignMultiplyer();

      
    }

    void SendData()
    {
      var data = new byte[]{ 0, 1, 2, 3 };
      
      var resultSending = multiplayer.SendBytes( data, 0, MultiplayerPeer.TransferModeEnum.Reliable );
      
      RJLog.Log( "Sending Data", resultSending, ByteView.Stringify( data ) );
    } 

    void AssignMultiplyer()
    {
      RJLog.Log( "AssignMultiplyer" );   
      multiplayer.MultiplayerPeer = peer;
      GetTree().SetMultiplayer( multiplayer );
    }

    void ConnectListeners()
    {
      multiplayer.PeerPacket += ( id, data )=>
      {
        RJLog.Log( "Received Data:", id, ByteView.Stringify( data ) );
        _transport.HandleIncomingMessage( id, data );
      };

      multiplayer.ConnectionFailed += ()=>
      {
        RJLog.Log( "ConnectionFailed" );
        _state = NetworkSessionConnectionState.Not_Connected;         
      };

      multiplayer.ServerDisconnected += ()=>
      {
        RJLog.Log( "ServerDisconnected" );
        _state = NetworkSessionConnectionState.Not_Connected;       
        _sessionManager.SetInSessionState( false );

      };

      multiplayer.ConnectedToServer += ()=>
      {
        RJLog.Log( "Connecting_As_Client" );
        _state = NetworkSessionConnectionState.Connected_As_Client;
        _sessionManager.SetInSessionState();

        Action.Trigger( _manager.onStartedSession );
      };

      multiplayer.PeerConnected += ( id )=>
      {
        RJLog.Log( "PeerConnected:", id );
        var networkSessionMember = NetworkSessionMember.Create( id );
        _sessionManager.AddMember( networkSessionMember );        
      };

      multiplayer.PeerDisconnected += ( id )=>
      {
        RJLog.Log( "PeerDisconnected:", id );
        _sessionManager.RemoveMember( id );
        
      };
    }

    public override void SendMessage( byte[] bytes, List<int> targetMembers, bool reliable = true )
    {
      var mode = reliable ? MultiplayerPeer.TransferModeEnum.Reliable : MultiplayerPeer.TransferModeEnum.Unreliable;

      if ( targetMembers.Count == 0 )
      {
        var resultSending = multiplayer.SendBytes( bytes, 0, mode );      
        RJLog.Log( "Sending Data", resultSending, ByteView.Stringify( bytes ) );
      }
      else
      {
        targetMembers.ForEach(
          ( id )=>
          {
             var resultSending = multiplayer.SendBytes( bytes, id, mode );      
             RJLog.Log( "Sending Data", resultSending, ByteView.Stringify( bytes ) );
          }
        );
      } 
      
    }

  }
}