using Godot;

using System.Collections.Generic;
using System;

namespace Rokojori
{  
  public interface iNodeState
  {
    public void OnNodeStateChanged();
    
  }

  public static class NodeState
  {
    
    public static bool IsVisible( this Node n )
    {
      if ( n is Node3D )
      {
        return ( n as Node3D ).Visible;
      }

      if ( n is Node2D )
      {
        return ( n as Node2D ).Visible;
      }

      if ( n is CanvasItem )
      {
        return ( n as CanvasItem ).Visible;
      }     

      return false;
    }

    public static bool IsProcessingInHierarchy( this Node n )
    {
      if ( n.ProcessMode == Node.ProcessModeEnum.Disabled )
      {
        return false;
      }

      if ( n.ProcessMode == Node.ProcessModeEnum.Always )
      {
        return true;
      }

      var paused = n.GetTree().Paused;

      if ( n.ProcessMode == Node.ProcessModeEnum.Pausable )
      {
        return ! paused;
      }

      if ( n.ProcessMode == Node.ProcessModeEnum.WhenPaused )
      {
        return paused;
      }

      var p = n.GetParent();

      if ( p == null )
      {
        return ! paused;
      }

      return p.IsProcessingInHierarchy();

    }

    public static void Configure( Node target, NodeStateConfiguration configuration )
    {
      Configure( target, 
        configuration.processEnabled, configuration.inputEnabled, configuration.physicsEnabled, 
        configuration.signalsEnabled, configuration.visible, configuration.setProcessMode, configuration.processMode 
      );
    }

    public static void SetNodeState( this Node target, NodeStateConfiguration configuration )
    {
      Configure( target, configuration );
    }

    public static void SetVisibility( this Node target, bool visible )
    {
      Configure( target, Trillean.Any, Trillean.Any, Trillean.Any, Trillean.Any, TrilleanLogic.FromBool( visible ), false, Node.ProcessModeEnum.Inherit );
    }

    public static void Configure( 
      Node target, 
      Trillean processEnabled, Trillean inputEnabled,Trillean physicsEnabled,
      Trillean signalsEnabled,Trillean visible, bool setProcessMode, Node.ProcessModeEnum processMode 
    )
    {
      if ( ! Node.IsInstanceValid( target ) )
      {
        return;
      }

      if ( Trillean.Any != processEnabled )
      {
        target.SetProcess( TrilleanLogic.ToBool( processEnabled ) );
      }

      if ( Trillean.Any != inputEnabled )
      {
        target.SetProcessInput( TrilleanLogic.ToBool( inputEnabled ) );
      }

      if ( Trillean.Any != physicsEnabled )
      {
        target.SetPhysicsProcess( TrilleanLogic.ToBool( physicsEnabled ) );
      }

      if ( Trillean.Any != signalsEnabled )
      {
        target.SetBlockSignals( ! TrilleanLogic.ToBool( signalsEnabled ) );
      }

      if ( setProcessMode )
      {
        target.ProcessMode = processMode;
      }


      if ( Trillean.Any != visible )
      {
        var n = target;

        var visibleFlag = TrilleanLogic.ToBool( visible );

        if ( n is Node3D )
        {
          ( n as Node3D ).Visible = visibleFlag;
        }

        if ( n is Node2D )
        {
          ( n as Node2D ).Visible = visibleFlag;
        }

        if ( n is CanvasItem )
        {
          ( n as CanvasItem ).Visible = visibleFlag;
        }     
      } 


      if ( target is iNodeState ins )
      {
        ins.OnNodeStateChanged();
      }
    }


    public static void Configure( Node n, bool processEnabled, bool inputEnabled, bool physicsEnabled, bool signalsEnabled, 
                                          Node.ProcessModeEnum processMode, bool visible )
    {
      if ( n == null || ! Node.IsInstanceValid( n ) )
      {
        return;
      }

      n.SetProcess( processEnabled );
      n.SetProcessInput( inputEnabled );
      n.SetPhysicsProcess( physicsEnabled );
      n.SetBlockSignals( ! signalsEnabled );
            
      n.ProcessMode = processMode;

      if ( n is Node3D n3 )
      {
        n3.Visible = visible;
      }

      if ( n is Node2D n2 )
      {
        n2.Visible = visible;
      }

      if ( n is CanvasItem ci )
      {
        ci.Visible = visible;
      }            

      if ( n is iNodeState ins )
      {
        ins.OnNodeStateChanged();
      }

      if ( n is CollisionShape3D cs )
      {
        cs.Disabled = ! physicsEnabled;
      }
    }

    public static void Set( Node n, bool enabled )
    {
      Configure( n, enabled, enabled, enabled, enabled, enabled ? Node.ProcessModeEnum.Inherit : Node.ProcessModeEnum.Disabled, enabled );
    }

    public static void Set( Node[] nodes, bool enabled )
    {
      Arrays.ForEach( nodes, n => Set( n, enabled ) );
    }

    public static void Enable( this Node n )
    {
      Set( n, true );
    }

    public static void Enable( params Node[] n )
    {
      Set( n, true );
    }

    public static void Disable( this Node n )
    {
      Set( n, false );
    }

    public static void Disable( params Node[] n )
    {
      Set( n, false );
    }

    public static void SetEnabledState( this Node n, bool isEnabled )
    {
      if ( isEnabled )
      {
        n.Enable();
      }
      else
      {
        n.Disable();
      }
    }

  }
}