
using Godot;
using System.Collections.Generic;


namespace Rokojori
{  
  [Tool]
  [GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Action.svg")]
  public partial class Action : NetworkNode
  {
    public enum ActionTriggerMode
    {
      Only_When_Processing_In_Hierarchy,
      Always
    }

    [Export]
    public ActionTriggerMode triggerMode = ActionTriggerMode.Only_When_Processing_In_Hierarchy;

    [ExportToolButton( "(?)  Help", Icon = "Help") ]
    public Callable openHelpButton => Callable.From( ()=> 
    { 
      #if TOOLS
      Rokojori.Tools.OnlineDocs.Open( GetType() );
      #endif
    }
    );

    [ExportToolButton( "Trigger Action in Editor")]
    public Callable TriggerActionButton => Callable.From( ()=> Trigger() );

    NetworkNodeSlot _seedSlot        = new NetworkNodeSlot();
    NetworkNodeSlot _dataSlot        = new NetworkNodeSlot();
    NetworkNodeSlot _seedAndDataSlot = new NetworkNodeSlot();

    public static bool IsAction( Node n )
    {
      if ( n == null )
      {
        return false;
      }

      return n is Action || GDScriptAction.IsRJAction( n );
    }

    public void LogInfoFor( Node n, string message )
    {
      var className = GDScriptNames.ExtractClassName( n );
      RJLog.GDLog( $"{className}._onTrigger()", n, message );
    }


    protected override List<NetworkNodeMember> CreateNetworkNodeMembers()
    {
      return new List<NetworkNodeMember>()
      {
        _networkNodeSlot,
        _seedSlot, 
        _dataSlot,
        _seedAndDataSlot
      };
    } 
    
    public Action()
    {
      
    }

    public void Trigger()
    {
      if ( ! IsInstanceValid( this ) )
      {
        return;
      }

      if ( triggerMode == ActionTriggerMode.Only_When_Processing_In_Hierarchy && ! this.IsProcessingInHierarchy() )
      {
        return;
      }

      _isNetworkedTrigger = false;
      _sendsSeed = false;
      _sendsData = false;
      _OnTrigger();

      SendOverNetwork();

    }

    protected void SendOverNetwork()
    {
      // check which type
      // 0 0 => vanilla
      // 1 0 => with seed
      // 0 1 => with data
      // 1 1 => with seed + data
    }

    protected void _OnNetworkTrigger( int seed, BitView data )
    {
      _isNetworkedTrigger = true;
      _receivedNetworkSeed = seed;
      _receivedNetworkData = data;

      _OnTrigger();

      _isNetworkedTrigger = false;
    }

    bool _isNetworkedTrigger = false;
    int _receivedNetworkSeed = -1;
    int _sendingNetworkSeed  = -1;

    bool _sendsSeed = false;
    bool _sendsData = false;

    BitView _sendingNetworkData;
    BitView _receivedNetworkData;

    public bool isNetworkedTrigger => _isNetworkedTrigger;
    
    static readonly int maxNetworkSeed = Mathf.RoundToInt( Mathf.Pow( 2, 30 ) );

    protected int networkSeed
    {
      get
      {
        if ( _isNetworkedTrigger )
        {
          return _receivedNetworkSeed;
        } 

        if ( _sendsSeed )
        {
          return _sendingNetworkSeed;
        }

        _sendingNetworkSeed = GodotRandom.Get().IntegerExclusive( 0, maxNetworkSeed );
        _sendsSeed = true;

        return _sendingNetworkSeed;
      }
    }

    protected BitView GetNetworkData( BitView view )
    {
      if ( _isNetworkedTrigger )
      {
        return _receivedNetworkData;
      }

      _sendingNetworkData = view;
      _sendsData = true;
      return _sendingNetworkData;
    }

    protected virtual void _OnTrigger()
    {
      
    }

  

    public static void Trigger( Action action )
    {
      if ( action == null )
      {
        return;
      }      

      action.Trigger();
    }

    public static void TriggerAll( Action[] actions, Node target, bool triggerDirectChildren, Action caller = null )
    {
      if ( actions != null )
      {
        for ( int i = 0; i < actions.Length; i++ )
        { 
          Action.Trigger( actions[ i ] );
        }      
      }

      if ( ! triggerDirectChildren )
      {
        return;
      }

      Nodes.ForEachDirectChild<Action>( target, ( a ) => Trigger( a ) );
    }

    public static void TriggerAll( Action action, Node target, bool triggerDirectChildren )
    {
      if ( action != null )
      {
        Action.Trigger( action );
             
      }

      if ( ! triggerDirectChildren )
      {
        return;
      }

       Nodes.ForEachDirectChild<Action>( target, ( a ) => Trigger( a ) );
    }
  }

}