using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Svelto.ECS;

using GamecraftModdingAPI.Utility;

namespace GamecraftModdingAPI.Blocks
{
    /// <summary>
    /// [EXPERIMENTAL] Common block signal operations
    /// The functionality in this class only works when in a game.
    /// </summary>
    public static class Signals
    {
        // Signal constants
        public static readonly float HIGH = 1.0f;
        public static readonly float POSITIVE_HIGH = HIGH;
        public static readonly float NEGATIVE_HIGH = -1.0f;
        public static readonly float LOW = 0.0f;

        private static SignalEngine signalEngine = new SignalEngine();

        /// <summary>
        /// Set the electric block's (first) signal value.
        /// </summary>
		/// <param name="blockID">The block's id.</param>
        /// <param name="signal">The signal value (-1 to 1; not enforced).</param>
		/// <param name="input">Whether to retrieve input IDs (true) or output IDs (false).</param>
		/// <param name="owned">Whether the block is in the owned group (true) or functional group (false)</param>
        public static void SetSignalByBlock(uint blockID, float signal, bool input = true, bool owned = true)
        {
			EGID egid = new EGID(blockID, owned ? BlockIdentifiers.OWNED_BLOCKS : BlockIdentifiers.FUNCTIONAL_BLOCK_PARTS);
            if (signalEngine.IsInGame && signalEngine.IsSimulationMode())
            {
                signalEngine.SetSignal(egid, signal, out uint _, input);
            }
        }

		public static void SetSignalByBlock(EGID blockID, float signal, bool input = true)
        {
            if (signalEngine.IsInGame && signalEngine.IsSimulationMode())
            {
				signalEngine.SetSignal(blockID, signal, out uint _, input);
            }
        }

        /// <summary>
        /// Set the signal's value.
        /// </summary>
        /// <param name="signalID">The channel cluster's id.</param>
        /// <param name="signal">The signal value (-1 to 1; not enforced).</param>
		/// <param name="input">Whether to retrieve input IDs (true) or output IDs (false).</param>
        public static void SetSignalByID(uint signalID, float signal, bool input = true)
        {
            if (signalEngine.IsInGame && signalEngine.IsSimulationMode())
            {
                signalEngine.SetSignal(signalID, signal, input);
            }
        }

        /// <summary>
        /// Add a value to an electric block's signal.
        /// </summary>
        /// <param name="blockID">The block's id.</param>
        /// <param name="signal">The signal value to add.</param>
        /// <param name="clamp">Whether to clamp the resulting signal value between -1 and 1.</param>
		/// <param name="input">Whether to retrieve input IDs (true) or output IDs (false).</param>
		/// <param name="owned">Whether the block is in the owned group (true) or functional group (false)</param>
		/// <returns>The signal's new value.</returns>
        public static float AddSignalByBlock(uint blockID, float signal, bool clamp = true, bool input = true, bool owned = true)
        {
			EGID egid = new EGID(blockID, owned ? BlockIdentifiers.OWNED_BLOCKS : BlockIdentifiers.FUNCTIONAL_BLOCK_PARTS);
            if (signalEngine.IsInGame && signalEngine.IsSimulationMode())
            {
                return signalEngine.AddSignal(egid, signal, out uint _, clamp, input);
            }
			return 0f;
        }

		public static float AddSignalByBlock(EGID blockID, float signal, bool clamp = true, bool input = true)
        {
            if (signalEngine.IsInGame && signalEngine.IsSimulationMode())
            {
				return signalEngine.AddSignal(blockID, signal, out uint _, clamp, input);
            }
            return 0f;
        }

        /// <summary>
        /// Add a value to a conductive cluster channel.
        /// </summary>
        /// <param name="signalID">The channel cluster's id.</param>
        /// <param name="signal">The signal value to add.</param>
        /// <param name="clamp">Whether to clamp the resulting signal value between -1 and 1.</param>
		/// <param name="input">Whether to retrieve input IDs (true) or output IDs (false).</param>
		/// <returns>The signal's new value.</returns>
        public static float AddSignalByID(uint signalID, float signal, bool clamp = true, bool input = true)
        {
            if (signalEngine.IsInGame && signalEngine.IsSimulationMode())
            {
                return signalEngine.AddSignal(signalID, signal, clamp, input);
            }
			return 0f;
        }

        /// <summary>
        /// Get a electric block's signal's (first) value.
        /// </summary>
		/// <param name="blockID">The block's id.</param>
		/// <param name="input">Whether to retrieve input IDs (true) or output IDs (false).</param>
		/// <param name="owned">Whether the block is in the owned group (true) or functional group (false)</param>
        /// <returns>The signal's value.</returns>
        public static float GetSignalByBlock(uint blockID, bool input = true, bool owned = true)
        {
			EGID egid = new EGID(blockID, owned? BlockIdentifiers.OWNED_BLOCKS : BlockIdentifiers.FUNCTIONAL_BLOCK_PARTS);
            if (signalEngine.IsInGame && signalEngine.IsSimulationMode())
            {
                return signalEngine.GetSignal(egid, out uint _, input);
            }
            return 0f;
        }

		public static float GetSignalByBlock(EGID blockID, bool input = true)
        {
            if (signalEngine.IsInGame && signalEngine.IsSimulationMode())
            {
				return signalEngine.GetSignal(blockID, out uint _, input);
            }
            return 0f;
        }

        /// <summary>
        /// Get a signal's value.
        /// </summary>
        /// <param name="signalID">The signal's id.</param>
		/// <param name="input">Whether to retrieve input IDs (true) or output IDs (false).</param>
        /// <returns>The signal's value.</returns>
        public static float GetSignalByID(uint signalID, bool input = true)
        {
            if (signalEngine.IsInGame && signalEngine.IsSimulationMode())
            {
                return signalEngine.GetSignal(signalID, input);
            }
            return 0f;
        }

        /// <summary>
        /// Get the ID of every electric block in the game world.
        /// </summary>
        /// <returns>The block IDs.</returns>
        public static EGID[] GetElectricBlocks()
        {
            return signalEngine.GetElectricBlocks();
        }

        /// <summary>
        /// Get the unique identifiers for the input wires connected to an electric block.
        /// </summary>
        /// <param name="blockID">The block's id.</param>
		/// <param name="input">Whether to retrieve input IDs (true) or output IDs (false).</param>
		/// <param name="owned">Whether the block is in the owned group (true) or functional group (false)</param>
        /// <returns>The unique IDs.</returns>
        public static uint[] GetSignalIDs(uint blockID, bool input = true, bool owned = true)
        {
			EGID egid = new EGID(blockID, owned ? BlockIdentifiers.OWNED_BLOCKS : BlockIdentifiers.FUNCTIONAL_BLOCK_PARTS);
            return signalEngine.GetSignalIDs(egid, input);
        }

		public static uint[] GetSignalIDs(EGID blockID, bool input = true, bool owned = true)
        {
            return signalEngine.GetSignalIDs(blockID, input);
        }

        public static void Init()
        {
            GameEngineManager.AddGameEngine(signalEngine);
        }
    }
}