using System;
using System.Reflection;

using RobocraftX.Blocks;
using Gamecraft.Wires;

using GamecraftModdingAPI.Utility;
using Svelto.ECS;

namespace GamecraftModdingAPI.Blocks
{
	public class TweakableEngine : IApiEngine
	{
		public string Name { get; } = "GamecraftModdingAPITweakableGameEngine";

		public EntitiesDB entitiesDB { set; private get; }

		public bool IsInGame = false;

		public void Dispose()
		{
			IsInGame = false;
		}

		public void Ready()
		{
			IsInGame = true;
		}

		// Implementations for Tweakable static class

		public T GetStatAny<T>(EGID blockID, TweakableStat stat)
		{
			switch (stat)
            {
                case TweakableStat.TopSpeed:
                    if (entitiesDB.Exists<MotorReadOnlyStruct>(blockID))
                    {
                        return (T)(object)entitiesDB.QueryEntity<MotorReadOnlyStruct>(blockID).maxVelocity;
                    }
					break;
                case TweakableStat.Torque:
                    if (entitiesDB.Exists<MotorReadOnlyStruct>(blockID))
                    {
                        return (T)(object)entitiesDB.QueryEntity<MotorReadOnlyStruct>(blockID).maxForce;
                    }
					break;
                case TweakableStat.MaxExtension:
                    if (entitiesDB.Exists<PistonReadOnlyStruct>(blockID))
                    {
                        return (T)(object)entitiesDB.QueryEntity<PistonReadOnlyStruct>(blockID).maxDeviation;
                    }
					break;
                case TweakableStat.MinAngle:
                    if (entitiesDB.Exists<ServoReadOnlyStruct>(blockID))
                    {
                        return (T)(object)entitiesDB.QueryEntity<ServoReadOnlyStruct>(blockID).minDeviation;
                    }
					break;
                case TweakableStat.MaxAngle:
                    if (entitiesDB.Exists<ServoReadOnlyStruct>(blockID))
                    {
                        return (T)(object)entitiesDB.QueryEntity<ServoReadOnlyStruct>(blockID).maxDeviation;
                    }
					break;
                case TweakableStat.Reverse:
                    if (entitiesDB.Exists<MotorReadOnlyStruct>(blockID))
                    {
                        return (T)(object)entitiesDB.QueryEntity<MotorReadOnlyStruct>(blockID).reverse;
                    }
                    else if (entitiesDB.Exists<ServoReadOnlyStruct>(blockID))
                    {
                        return (T)(object)entitiesDB.QueryEntity<ServoReadOnlyStruct>(blockID).reverse;
                    }
					break;
                case TweakableStat.StartValue:
                    if (entitiesDB.Exists<SignalGeneratorEntityStruct>(blockID))
                    {
                        return (T)(object)entitiesDB.QueryEntity<SignalGeneratorEntityStruct>(blockID).startValue;
                    }
					break;
            }
            return default(T);
		}

		public T GetStatAny<T>(uint blockID, TweakableStat stat)
		{
			return GetStatAny<T>(new EGID(blockID, BlockIdentifiers.OWNED_BLOCKS), stat);
		}

		public dynamic GetStatDynamic(EGID blockID, TweakableStat stat)
        {
            switch (stat)
            {
                case TweakableStat.TopSpeed:
                    if (entitiesDB.Exists<MotorReadOnlyStruct>(blockID))
                    {
                        return entitiesDB.QueryEntity<MotorReadOnlyStruct>(blockID).maxVelocity;
                    }
                    break;
                case TweakableStat.Torque:
                    if (entitiesDB.Exists<MotorReadOnlyStruct>(blockID))
                    {
                        return entitiesDB.QueryEntity<MotorReadOnlyStruct>(blockID).maxForce;
                    }
                    break;
                case TweakableStat.MaxExtension:
                    if (entitiesDB.Exists<PistonReadOnlyStruct>(blockID))
                    {
                        return entitiesDB.QueryEntity<PistonReadOnlyStruct>(blockID).maxDeviation;
                    }
                    break;
                case TweakableStat.MinAngle:
                    if (entitiesDB.Exists<ServoReadOnlyStruct>(blockID))
                    {
                        return entitiesDB.QueryEntity<ServoReadOnlyStruct>(blockID).minDeviation;
                    }
                    break;
                case TweakableStat.MaxAngle:
                    if (entitiesDB.Exists<ServoReadOnlyStruct>(blockID))
                    {
                        return entitiesDB.QueryEntity<ServoReadOnlyStruct>(blockID).maxDeviation;
                    }
                    break;
                case TweakableStat.Reverse:
                    if (entitiesDB.Exists<MotorReadOnlyStruct>(blockID))
                    {
                        return entitiesDB.QueryEntity<MotorReadOnlyStruct>(blockID).reverse;
                    }
                    else if (entitiesDB.Exists<ServoReadOnlyStruct>(blockID))
                    {
                        return entitiesDB.QueryEntity<ServoReadOnlyStruct>(blockID).reverse;
                    }
                    break;
                case TweakableStat.StartValue:
                    if (entitiesDB.Exists<SignalGeneratorEntityStruct>(blockID))
                    {
                        return entitiesDB.QueryEntity<SignalGeneratorEntityStruct>(blockID).startValue;
                    }
                    break;
            }
            return null;
        }

		public dynamic GetStatDynamic(uint blockID, TweakableStat stat)
		{
			return GetStatDynamic(new EGID(blockID, BlockIdentifiers.OWNED_BLOCKS), stat);
		}

		public T SetStatAny<T>(EGID blockID, TweakableStat stat, T value)
		{
			switch (stat)
            {
                case TweakableStat.TopSpeed:
                    if (entitiesDB.Exists<MotorReadOnlyStruct>(blockID))
                    {
                        ref MotorReadOnlyStruct refStruct = ref entitiesDB.QueryEntity<MotorReadOnlyStruct>(blockID);
                        refStruct.maxVelocity = (float)(object)value;
                        return (T)(object)refStruct.maxVelocity;
                    }
                    break;
                case TweakableStat.Torque:
                    if (entitiesDB.Exists<MotorReadOnlyStruct>(blockID))
                    {
                        ref MotorReadOnlyStruct refStruct = ref entitiesDB.QueryEntity<MotorReadOnlyStruct>(blockID);
                        refStruct.maxForce = (float)(object)value;
                        return (T)(object)refStruct.maxForce;
                    }
                    break;
                case TweakableStat.MaxExtension:
                    if (entitiesDB.Exists<PistonReadOnlyStruct>(blockID))
                    {
                        ref PistonReadOnlyStruct refStruct = ref entitiesDB.QueryEntity<PistonReadOnlyStruct>(blockID);
                        refStruct.maxDeviation = (float)(object)value;
                        return (T)(object)refStruct.maxDeviation;
                    }
                    break;
                case TweakableStat.MinAngle:
                    if (entitiesDB.Exists<ServoReadOnlyStruct>(blockID))
                    {
                        ref ServoReadOnlyStruct refStruct = ref entitiesDB.QueryEntity<ServoReadOnlyStruct>(blockID);
                        refStruct.minDeviation = (float)(object)value;
                        return (T)(object)refStruct.minDeviation;
                    }
                    break;
                case TweakableStat.MaxAngle:
                    if (entitiesDB.Exists<ServoReadOnlyStruct>(blockID))
                    {
                        ref ServoReadOnlyStruct refStruct = ref entitiesDB.QueryEntity<ServoReadOnlyStruct>(blockID);
                        refStruct.maxDeviation = (float)(object)value;
                        return (T)(object)refStruct.maxDeviation;
                    }
                    break;
                case TweakableStat.Reverse:
                    if (entitiesDB.Exists<MotorReadOnlyStruct>(blockID))
                    {
                        ref MotorReadOnlyStruct refStruct = ref entitiesDB.QueryEntity<MotorReadOnlyStruct>(blockID);
                        refStruct.reverse = (bool)(object)value;
                        return (T)(object)refStruct.reverse;
                    }
                    else if (entitiesDB.Exists<ServoReadOnlyStruct>(blockID))
                    {
						ref ServoReadOnlyStruct refStruct = ref entitiesDB.QueryEntity<ServoReadOnlyStruct>(blockID);
                        refStruct.reverse = (bool)(object)value;
                        return (T)(object)refStruct.reverse;
                    }
                    break;
                case TweakableStat.StartValue:
                    if (entitiesDB.Exists<SignalGeneratorEntityStruct>(blockID))
                    {
                        ref SignalGeneratorEntityStruct refStruct = ref entitiesDB.QueryEntity<SignalGeneratorEntityStruct>(blockID);
                        refStruct.startValue = (float)(object)value;
                        return (T)(object)refStruct.startValue;
                    }
                    break;
            }
            return default(T);
		}

		public T SetStatAny<T>(uint blockID, TweakableStat stat, T value)
		{
			return SetStatAny<T>(new EGID(blockID, BlockIdentifiers.OWNED_BLOCKS), stat, value);
		}

		public dynamic SetStatDynamic(EGID blockID, TweakableStat stat, dynamic value)
        {
            switch (stat)
            {
                case TweakableStat.TopSpeed:
                    if (entitiesDB.Exists<MotorReadOnlyStruct>(blockID))
                    {
                        ref MotorReadOnlyStruct refStruct = ref entitiesDB.QueryEntity<MotorReadOnlyStruct>(blockID);
                        refStruct.maxVelocity = value;
                        return refStruct.maxVelocity;
                    }
                    break;
                case TweakableStat.Torque:
                    if (entitiesDB.Exists<MotorReadOnlyStruct>(blockID))
                    {
                        ref MotorReadOnlyStruct refStruct = ref entitiesDB.QueryEntity<MotorReadOnlyStruct>(blockID);
                        refStruct.maxForce = value;
                        return refStruct.maxForce;
                    }
                    break;
                case TweakableStat.MaxExtension:
                    if (entitiesDB.Exists<PistonReadOnlyStruct>(blockID))
                    {
                        ref PistonReadOnlyStruct refStruct = ref entitiesDB.QueryEntity<PistonReadOnlyStruct>(blockID);
                        refStruct.maxDeviation = value;
                        return refStruct.maxDeviation;
                    }
                    break;
                case TweakableStat.MinAngle:
                    if (entitiesDB.Exists<ServoReadOnlyStruct>(blockID))
                    {
                        ref ServoReadOnlyStruct refStruct = ref entitiesDB.QueryEntity<ServoReadOnlyStruct>(blockID);
                        refStruct.minDeviation = value;
                        return refStruct.minDeviation;
                    }
                    break;
                case TweakableStat.MaxAngle:
                    if (entitiesDB.Exists<ServoReadOnlyStruct>(blockID))
                    {
                        ref ServoReadOnlyStruct refStruct = ref entitiesDB.QueryEntity<ServoReadOnlyStruct>(blockID);
                        refStruct.maxDeviation = value;
                        return refStruct.maxDeviation;
                    }
                    break;
                case TweakableStat.Reverse:
                    if (entitiesDB.Exists<MotorReadOnlyStruct>(blockID))
                    {
                        ref MotorReadOnlyStruct refStruct = ref entitiesDB.QueryEntity<MotorReadOnlyStruct>(blockID);
                        refStruct.reverse = value;
                        return refStruct.reverse;
                    }
                    else if (entitiesDB.Exists<ServoReadOnlyStruct>(blockID))
                    {
                        ref ServoReadOnlyStruct refStruct = ref entitiesDB.QueryEntity<ServoReadOnlyStruct>(blockID);
                        refStruct.reverse = value;
                        return refStruct.reverse;
                    }
                    break;
                case TweakableStat.StartValue:
                    if (entitiesDB.Exists<SignalGeneratorEntityStruct>(blockID))
                    {
                        ref SignalGeneratorEntityStruct refStruct = ref entitiesDB.QueryEntity<SignalGeneratorEntityStruct>(blockID);
                        refStruct.startValue = value;
                        return refStruct.startValue;
                    }
                    break;
            }
            return null;
        }

		public dynamic SetStatDynamic(uint blockID, TweakableStat stat, dynamic value)
		{
			return SetStatDynamic(new EGID(blockID, BlockIdentifiers.OWNED_BLOCKS), stat, value);
		}

		public T AddStatAny<T>(EGID blockID, TweakableStat stat, T value)
		{
			switch (stat)
            {
                case TweakableStat.TopSpeed:
                    if (entitiesDB.Exists<MotorReadOnlyStruct>(blockID))
                    {
                        ref MotorReadOnlyStruct refStruct = ref entitiesDB.QueryEntity<MotorReadOnlyStruct>(blockID);
                        refStruct.maxVelocity += (float)(object)value;
                        return (T)(object)refStruct.maxVelocity;
                    }
                    break;
                case TweakableStat.Torque:
                    if (entitiesDB.Exists<MotorReadOnlyStruct>(blockID))
                    {
                        ref MotorReadOnlyStruct refStruct = ref entitiesDB.QueryEntity<MotorReadOnlyStruct>(blockID);
                        refStruct.maxForce += (float)(object)value;
                        return (T)(object)refStruct.maxForce;
                    }
                    break;
                case TweakableStat.MaxExtension:
                    if (entitiesDB.Exists<PistonReadOnlyStruct>(blockID))
                    {
                        ref PistonReadOnlyStruct refStruct = ref entitiesDB.QueryEntity<PistonReadOnlyStruct>(blockID);
                        refStruct.maxDeviation += (float)(object)value;
                        return (T)(object)refStruct.maxDeviation;
                    }
                    break;
                case TweakableStat.MinAngle:
                    if (entitiesDB.Exists<ServoReadOnlyStruct>(blockID))
                    {
                        ref ServoReadOnlyStruct refStruct = ref entitiesDB.QueryEntity<ServoReadOnlyStruct>(blockID);
                        refStruct.minDeviation += (float)(object)value;
                        return (T)(object)refStruct.minDeviation;
                    }
                    break;
                case TweakableStat.MaxAngle:
                    if (entitiesDB.Exists<ServoReadOnlyStruct>(blockID))
                    {
                        ref ServoReadOnlyStruct refStruct = ref entitiesDB.QueryEntity<ServoReadOnlyStruct>(blockID);
                        refStruct.maxDeviation += (float)(object)value;
                        return (T)(object)refStruct.maxDeviation;
                    }
                    break;
                case TweakableStat.Reverse:
                    // '+' is associated with logical OR in some fields, so it technically isn't invalid to "add" booleans
                    if (entitiesDB.Exists<MotorReadOnlyStruct>(blockID))
                    {
                        ref MotorReadOnlyStruct refStruct = ref entitiesDB.QueryEntity<MotorReadOnlyStruct>(blockID);
						refStruct.reverse = refStruct.reverse || (bool)(object)value;
                        return (T)(object)refStruct.reverse;
                    }
                    else if (entitiesDB.Exists<ServoReadOnlyStruct>(blockID))
                    {
                        ref ServoReadOnlyStruct refStruct = ref entitiesDB.QueryEntity<ServoReadOnlyStruct>(blockID);
						refStruct.reverse = refStruct.reverse || (bool)(object)value;
                        return (T)(object)refStruct.reverse;
                    }
                    break;
                case TweakableStat.StartValue:
                    if (entitiesDB.Exists<SignalGeneratorEntityStruct>(blockID))
                    {
                        ref SignalGeneratorEntityStruct refStruct = ref entitiesDB.QueryEntity<SignalGeneratorEntityStruct>(blockID);
                        refStruct.startValue += (float)(object)value;
                        return (T)(object)refStruct.startValue;
                    }
                    break;
            }
            return default(T);
		}

		public T AddStatAny<T>(uint blockID, TweakableStat stat, T value)
		{
			return AddStatAny<T>(new EGID(blockID, BlockIdentifiers.OWNED_BLOCKS), stat, value);
		}

		public dynamic AddStatDynamic(EGID blockID, TweakableStat stat, dynamic value)
        {
            switch (stat)
            {
                case TweakableStat.TopSpeed:
                    if (entitiesDB.Exists<MotorReadOnlyStruct>(blockID))
                    {
                        ref MotorReadOnlyStruct refStruct = ref entitiesDB.QueryEntity<MotorReadOnlyStruct>(blockID);
                        refStruct.maxVelocity += value;
                        return refStruct.maxVelocity;
                    }
                    break;
                case TweakableStat.Torque:
                    if (entitiesDB.Exists<MotorReadOnlyStruct>(blockID))
                    {
                        ref MotorReadOnlyStruct refStruct = ref entitiesDB.QueryEntity<MotorReadOnlyStruct>(blockID);
                        refStruct.maxForce += value;
                        return refStruct.maxForce;
                    }
                    break;
                case TweakableStat.MaxExtension:
                    if (entitiesDB.Exists<PistonReadOnlyStruct>(blockID))
                    {
                        ref PistonReadOnlyStruct refStruct = ref entitiesDB.QueryEntity<PistonReadOnlyStruct>(blockID);
                        refStruct.maxDeviation += value;
                        return refStruct.maxDeviation;
                    }
                    break;
                case TweakableStat.MinAngle:
                    if (entitiesDB.Exists<ServoReadOnlyStruct>(blockID))
                    {
                        ref ServoReadOnlyStruct refStruct = ref entitiesDB.QueryEntity<ServoReadOnlyStruct>(blockID);
                        refStruct.minDeviation += value;
                        return refStruct.minDeviation;
                    }
                    break;
                case TweakableStat.MaxAngle:
                    if (entitiesDB.Exists<ServoReadOnlyStruct>(blockID))
                    {
                        ref ServoReadOnlyStruct refStruct = ref entitiesDB.QueryEntity<ServoReadOnlyStruct>(blockID);
                        refStruct.maxDeviation += value;
                        return refStruct.maxDeviation;
                    }
                    break;
                case TweakableStat.Reverse:
                    // '+' is associated with logical OR in some fields, so it technically isn't invalid to "add" booleans
                    if (entitiesDB.Exists<MotorReadOnlyStruct>(blockID))
                    {
                        ref MotorReadOnlyStruct refStruct = ref entitiesDB.QueryEntity<MotorReadOnlyStruct>(blockID);
                        refStruct.reverse = refStruct.reverse || value;
                        return refStruct.reverse;
                    }
                    else if (entitiesDB.Exists<ServoReadOnlyStruct>(blockID))
                    {
                        ref ServoReadOnlyStruct refStruct = ref entitiesDB.QueryEntity<ServoReadOnlyStruct>(blockID);
                        refStruct.reverse = refStruct.reverse || value;
                        return refStruct.reverse;
                    }
                    break;
                case TweakableStat.StartValue:
                    if (entitiesDB.Exists<SignalGeneratorEntityStruct>(blockID))
                    {
                        ref SignalGeneratorEntityStruct refStruct = ref entitiesDB.QueryEntity<SignalGeneratorEntityStruct>(blockID);
                        refStruct.startValue += value;
                        return refStruct.startValue;
                    }
                    break;
            }
            return null;
        }

		public dynamic AddStatDynamic(uint blockID, TweakableStat stat, dynamic value)
		{
			return AddStatDynamic(new EGID(blockID, BlockIdentifiers.OWNED_BLOCKS), stat, value);
		}
	}
}