// Fill out your copyright notice in the Description page of Project Settings.


#include "JSBSimMovementComponent.h"
#include "JSBSimModule.h"

#pragma warning( push )

// UE treats warning as errors. JSBSim has some warnings in its include files, so if we don't catch them inside this push/pop pragma, we won't be able to build...

#pragma warning( disable : 4263 ) // FGOutputType.h(151): warning C4263: 'bool JSBSim::FGOutputType::Run(void)': member function does not override any base class virtual member function
#pragma warning( disable : 4264 ) // FGOutputType.h(215): warning C4264: 'bool JSBSim::FGModel::Run(bool)': no override available for virtual member function from base 'JSBSim::FGModel'; function is hidden --- And others
#pragma warning( disable : 4005 ) // compiler.h(58): warning C4005: 'DEPRECATED': macro redefinition with UE_5.0\Engine\Source\Runtime\Core\Public\Windows\WindowsPlatformCompilerPreSetup.h(55): note: see previous definition of 'DEPRECATED'
#pragma warning( disable : 4458 ) // FGXMLElement.h(369): error C4458: declaration of 'name' hides class member

#include "FGFDMExec.h"
#include "math/FGLocation.h"
#include "models/FGFCS.h"
#include "models/FGPropulsion.h"
#include "models/propulsion/FGEngine.h"
#include "models/atmosphere/FGWinds.h"
#include "models/FGAerodynamics.h"
#include "models/FGAircraft.h"
#include "models/FGAtmosphere.h"
#include "models/FGAuxiliary.h"
#include "models/FGLGear.h"
#include "models/FGMassBalance.h"
#include "models/propulsion/FGThruster.h"
#include "models/propulsion/FGPiston.h"
#include "models/propulsion/FGTurbine.h"
#include "models/propulsion/FGTurboProp.h"
#include "models/propulsion/FGTank.h"
#include "initialization/FGInitialCondition.h"
#include "initialization/FGTrim.h"
#include "Interfaces/IPluginManager.h"
#include "simgear/props/props.hxx"

#pragma warning( pop )

#include "UEGroundCallback.h"

#include "DrawDebugHelpers.h"
#include "GeoReferencingSystem.h"
#include "Components/ActorComponent.h"


// Utility class and static member to redirect cout to UE_LOG - see in UJSBSimMovementComponent::UJSBSimMovementComponent()
class LStream : public std::stringbuf {
protected:
  int sync() {
    UE_LOG(LogJSBSim, Log, TEXT("%s"), *FString(str().c_str()));
    str("");
    return std::stringbuf::sync();
  }
};
LStream Stream;


////// Constructor
UJSBSimMovementComponent::UJSBSimMovementComponent()
{
	// Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features off to improve performance if you don't need them.
	PrimaryComponentTick.bCanEverTick = true;

	// Uncomment to Add a stream redirector in order to output JSBSim debug info to Log
	// std::cout.rdbuf(&Stream);
}


////// Public functions
FString UJSBSimMovementComponent::GetAircraftScreenName() const
{
	FString ScreenName;
	if (AircraftLoaded && Aircraft)
	{
		ScreenName = FString(Aircraft->GetAircraftName().c_str());
	}

	return ScreenName;
}


void UJSBSimMovementComponent::PropertyManagerNode(TArray<FString>& Catalog)
{
  auto newlist = Exec->GetPropertyCatalog();

  //convert list to array
  for (auto iterate : newlist)
  {
    Catalog.Add(iterate.c_str());
  }
}

//TODO, check if this is optimized, as we are using strings for convenience and we could probably cache the propertynode once we have it
void UJSBSimMovementComponent::CommandConsole(FString Property, FString InValue, FString& OutValue)
{
  FGPropertyNode* node = PropertyManager->GetNode(TCHAR_TO_UTF8(*Property),false);
  if (node != NULL)
  {    
    //we skip setting values by using blank InValue.
    if (InValue != "")
    {
      node->setStringValue(TCHAR_TO_UTF8(*InValue));
    }
    OutValue = node->getStringValue();
  }
  ////check if property has READ(1) attribute
  //auto attribute = SGPropertyNode::Attribute(1);
  //if (! node->getAttribute(attribute))
  //{
  //  return;
  //}

}

//TODO, check if this is optimized, as we are using strings for convenience and we could probably cache the propertynode once we have it
void UJSBSimMovementComponent::CommandConsoleBatch(TArray<FString> Property, TArray<FString> InValue, TArray<FString>& OutValue)
{
  OutValue.SetNum(Property.Num());
  for (int i = 0; i < Property.Num(); i++)
  {
    FGPropertyNode* node = PropertyManager->GetNode(TCHAR_TO_UTF8(*(Property[i])),false);
    if (node != NULL)
    {
      //we skip setting values by using blank InValue.
      if (InValue[i] != "")
      {
        node->setStringValue(TCHAR_TO_UTF8(*InValue[i]));
      }
      OutValue[i] = node->getStringValue();
    }
  }
  
  ////check if property has READ(1) attribute
  //auto attribute = SGPropertyNode::Attribute(1);
  //if (! node->getAttribute(attribute))
  //{
  //  return;
  //}
}



void UJSBSimMovementComponent::LoadAircraft(bool ResetToDefaultSettings)
{
	UE_LOG(LogJSBSim, Display, TEXT("UJSBSimMovementComponent::LoadAircraft %s"), *AircraftModel);

	// It seems like we can only load the model once after having been initialized - So we have to Reinit JSBSim when changing the model.
	DeInitializeJSBSim();
	InitializeJSBSim();
	AircraftLoaded = Exec->LoadModel(std::string(TCHAR_TO_UTF8(*AircraftModel)));

	if (!AircraftLoaded)
	{
		UE_LOG(LogJSBSim, Error, TEXT("Error while loading Model %s - Please check for typo or your configurations files"), *AircraftModel);
		return;
	}
	{
		UE_LOG(LogJSBSim, Display, TEXT("Model %s Loaded successfully !"), *AircraftModel);
	}

	// Do basic sanity checks 
	if (GroundReactions->GetNumGearUnits() <= 0)
	{
		UE_LOG(LogJSBSim, Error, TEXT("Error - Num Gear Units = %d. This is a very bad thing because with 0 gear units, the ground trimming routine will core dump"), GroundReactions->GetNumGearUnits());
		AircraftLoaded = false;
		return;
	}

	UpdateLocalTransforms();

    // The Aircraft model has changed - Reset the Tank and Gear properties that can have been overriden by the user. 
	if (ResetToDefaultSettings)
	{
		InitTankDefaultProperties();
		InitGearDefaultProperties();
	}

	InitEnginesCommandAndStates();
}

double UJSBSimMovementComponent::GetAGLevel(const FVector& StartECEFLocation, FVector& ECEFContactPoint, FVector& ECEFNormal)
{
  if (!GeoReferencingSystem || !GetWorld())
  {
    return 0.0;
  }

  // Get local Up vector at the query ECEF Location
  FTransform TangentTransform = GeoReferencingSystem->GetTangentTransformAtECEFLocation(StartECEFLocation);
  FVector Up = TangentTransform.TransformVector(FVector::ZAxisVector);

  // Compute the raycast Origin point
  FVector StartEngineLocation;
  GeoReferencingSystem->ECEFToEngine(StartECEFLocation, StartEngineLocation);
  FVector LineCheckStart = StartEngineLocation + AGLThresholdMeters * 100 * Up; // slightly above the starting point

  // Compute the raycast end point
  // Estimate raycast length - Altitude + 5% of ellipsoid radius in case of negative altitudes
  FVector LineCheckEnd = StartEngineLocation - (AircraftState.AltitudeASLFt * FEET_TO_METER + 0.05 * GeoReferencingSystem->GetGeographicEllipsoidMaxRadius()) * Up; 

  // Prepare collision query  
  FHitResult HitResult = FHitResult();
  static const FName LineTraceSingleName(TEXT("AGLevelLineTrace"));
  FCollisionQueryParams CollisionParams(LineTraceSingleName);
  CollisionParams.bTraceComplex = true;
  CollisionParams.AddIgnoredActor(Parent);

  FCollisionObjectQueryParams ObjectParams = FCollisionObjectQueryParams(ECC_WorldStatic);
  ObjectParams.AddObjectTypesToQuery(ECC_WorldDynamic);
  ObjectParams.AddObjectTypesToQuery(ECC_Pawn);
  ObjectParams.AddObjectTypesToQuery(ECC_Visibility);

  //DrawDebugLine(GetWorld(), LineCheckStart, LineCheckEnd , FColor::Blue, false, -1, 0, 3);

  // Do Query
  double HAT = 0.0;
  if (GetWorld()->LineTraceSingleByObjectType(HitResult, LineCheckStart, LineCheckEnd, ObjectParams, CollisionParams))
  {
    //DrawDebugLine(GetWorld(), HitResult.ImpactPoint, HitResult.ImpactPoint + HitResult.ImpactNormal*100.0, FColor::Orange, false, -1, 0, 3);

    FVector DirectionToImpact = HitResult.ImpactPoint - StartEngineLocation;
    HAT = FVector::Dist(StartEngineLocation, HitResult.ImpactPoint) / 100.0 * -FMath::Sign(DirectionToImpact.Dot(Up)); // JSBSim expect a signed distance. Consider that! 
    GeoReferencingSystem->EngineToECEF(HitResult.ImpactPoint, ECEFContactPoint);

    // Georeferencing don't provide tools to transform a direction, or access the worldToECEF Matrix - Do it by hand
    FVector ECEFNormalEnd;
    GeoReferencingSystem->EngineToECEF(HitResult.ImpactPoint + HitResult.ImpactNormal * 100.0, ECEFNormalEnd);
    ECEFNormal = ECEFNormalEnd - ECEFContactPoint;
    ECEFNormal.Normalize();
  }
  else
  {
    ECEFContactPoint = FVector();
    ECEFNormal = FVector::ZAxisVector;
  }
  return HAT;
}

////// ActorComponent overridables
void UJSBSimMovementComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	double Start = FPlatformTime::Seconds();

	if (AircraftLoaded)
	{
		if (AircraftState.Crashed)
		{
			// TODO - Send event
			UE_LOG(LogJSBSim, Display, TEXT("Aircraft crashed..."));
		}
		else
		{

      //calculate sim rate to be 120hz independent of game tick rate(Pseudo fixed rate, dev/user needs to set game to a fixed rate too)
      simDtime = 120.f/(1.f / DeltaTime);
      remainder = remainder + fmodf(simDtime,1);
      simloops = truncf(simDtime) + truncf(remainder);
      remainder = fmodf(remainder,1);

      //jsbsim recommends to step sim at 120hz, eg 1/120 = 0.0083..
      Exec->Setdt(0.008333333333333333);

            // Send Commands and State to JSBSim
			CopyToJSBSim();
			
      //step sim x times per game tick
      for (int i = 0; i < simloops; i++)
      {
        Exec->Run();
      }

            // The CG location in the reference frame can vary over time, for instance when tanks get empty... 
            // Theoretically, we should update the local transforms. But maybe it's overkill to do it each frame...
            UpdateLocalTransforms(); 

            // Get the results from JSBSim
			CopyFromJSBSim();

            // Basic debugging string and symbols
			if (DrawDebug)
			{
				DrawDebugMessage();
				DrawDebugObjects();
			}

            // Transform the aircraft coordinates from ECEF Frame to UE Frame, using the georeferencing plugin.
			if (Parent)
			{
				// Computes Rotation in engine frame
				FTransform ENUTransform = GeoReferencingSystem->GetTangentTransformAtECEFLocation(AircraftState.ECEFLocation);
				FRotator LocalUERotation(AircraftState.LocalEulerAngles);
				LocalUERotation.Yaw = LocalUERotation.Yaw - 90.0; // JSBSim heading is aero heading (0 at north). We have to remove 90 because in UE, 0 is pointing east. 
				FQuat EngineRotationQuat = ENUTransform.TransformRotation(LocalUERotation.Quaternion());
				
				FMatrix EngineRotation;
				EngineRotationQuat.ToMatrix(EngineRotation);
				FVector CGOffsetWorld = EngineRotation.TransformPosition(CGLocalPosition);

				// Computes Location in engine frame
				FVector CGWorldPosition;
				GeoReferencingSystem->ECEFToEngine(AircraftState.ECEFLocation, CGWorldPosition);
				FVector EngineLocation = CGWorldPosition - CGOffsetWorld;

                // Update the ForwardHorizontal vector used for the PFD
				AircraftState.UEForwardHorizontal = ENUTransform.TransformVector(ECEFForwardHorizontal);

				// Apply the transform to the Parent actor			
				Parent->SetActorLocationAndRotation(EngineLocation, EngineRotationQuat);
			}
		}
	}

    // Get some stats - TODO - Use the UE Stats system
	double End = FPlatformTime::Seconds();
    TickTime = (End - Start) * 1000.0;
}

void UJSBSimMovementComponent::BeginDestroy()
{
	Super::BeginDestroy();

    // Make sure we destroy JSBSim too
	DeInitializeJSBSim();
}

void UJSBSimMovementComponent::BeginPlay()
{
	Super::BeginPlay();

    // Init local variables from Level Objects
	Parent = GetOwner();

    // A GeoReferencingSystem Actor is mandatory! 
	if (!GeoReferencingSystem)
	{
		GeoReferencingSystem = AGeoReferencingSystem::GetGeoReferencingSystem(GetWorld());

		if (!GeoReferencingSystem)
		{
			UE_LOG(LogJSBSim, Error, TEXT("Impossible to use a UJSBSimMovementComponent without a GeoReferencingSystem."));
		}
	}

	if (Parent)
	{
		Parent->GetRootComponent()->SetMobility(EComponentMobility::Movable);
		LoadAircraft(false); // Start with a Fresh JSBSim object, but potentially with overridden properties
		PrepareJSBSim();
	}
}

void UJSBSimMovementComponent::OnRegister()
{
	Super::OnRegister();

	if (!GeoReferencingSystem)
	{
		GeoReferencingSystem = AGeoReferencingSystem::GetGeoReferencingSystem(GetWorld());

		if (!GeoReferencingSystem)
		{
			UE_LOG(LogJSBSim, Error, TEXT("Impossible to use a UJSBSimMovementComponent without a GeoReferencingSystem."));
		}
	}
}

/////////// JSBSim Protected methods

void UJSBSimMovementComponent::InitializeJSBSim()
{
	if (!JSBSimInitialized)
	{
		// Construct the JSBSim FDM
		Exec = new JSBSim::FGFDMExec();

		// Get pointers to main components
		Atmosphere = Exec->GetAtmosphere();
		Winds = Exec->GetWinds();
		FCS = Exec->GetFCS();
		MassBalance = Exec->GetMassBalance();
		Propulsion = Exec->GetPropulsion();
		Aircraft = Exec->GetAircraft();
		Propagate = Exec->GetPropagate();
		Auxiliary = Exec->GetAuxiliary();
		Inertial = Exec->GetInertial();
		Inertial->SetGroundCallback(new UEGroundCallback(this)); // Register ground callback.
		Aerodynamics = Exec->GetAerodynamics();
		GroundReactions = Exec->GetGroundReactions();
		Accelerations = Exec->GetAccelerations();
		IC = Exec->GetIC();
		PropertyManager = Exec->GetPropertyManager();

        // Initialize the Models location, relatively to this plugin

		// Get the base directory of this plugin
		FString BaseDir = IPluginManager::Get().FindPlugin("JSBSimFlightDynamicsModel")->GetBaseDir();
		// Add on the relative location of the third party dll and load it
		FString RootDirRelative = FPaths::Combine(*BaseDir, TEXT("Resources/JSBSim"));
		const FString& RootDir = IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*RootDirRelative);
		UE_LOG(LogJSBSim, Display, TEXT("Initializing JSBSimFlightDynamicsModel using Data in '%s'"), *RootDir);

		// Set data paths...
		FString AircraftPath(TEXT("aircraft"));
		FString EnginePath(TEXT("engine"));
		FString SystemPath(TEXT("systems"));
		Exec->SetRootDir(SGPath(*RootDir));
		Exec->SetAircraftPath(SGPath(*AircraftPath));
		Exec->SetEnginePath(SGPath(*EnginePath));
		Exec->SetSystemsPath(SGPath(*SystemPath));

		// Prepare Initial Conditions
		TrimNeeded = true;

		// Base setup done so far. The other part of initial setup will be done on begin play, in InitJSBSim. 
        JSBSimInitialized = true;
	}
}

void UJSBSimMovementComponent::PrepareJSBSim()
{
	UE_LOG(LogJSBSim, Display, TEXT("PrepareJSBSim - Setting Initial Conditiond and computing initial state"));

	// The Aircraft should have been loaded first
	if (!AircraftLoaded)
	{
		return;
	}

    // Reset the current Aircraft State
    AircraftState.Reset();

	// First, consider the aircraft Transform in UE to define the Location and Orientation in JSBSim
	FGeographicCoordinates GeographicCoordinates;
	if (Parent)
	{
		// Get CG location in Geographic
		FVector CGWorldPosition = Parent->GetTransform().TransformPosition(CGLocalPosition);
		GeoReferencingSystem->EngineToGeographic(CGWorldPosition, GeographicCoordinates);

		// Computes Rotation in engine frame
		FTransform ENUTransform = GeoReferencingSystem->GetTangentTransformAtGeographicLocation(GeographicCoordinates);
		FQuat LocalECEFRotation = ENUTransform.InverseTransformRotation(Parent->GetActorQuat());
		FRotator PsiThetaPhi = LocalECEFRotation.Rotator();

		// Set it as initial conditions
		IC->SetLongitudeDegIC(GeographicCoordinates.Longitude); 
		IC->SetGeodLatitudeDegIC(GeographicCoordinates.Latitude);
		IC->SetAltitudeASLFtIC(GeographicCoordinates.Altitude * METER_TO_FEET);
		IC->SetPhiDegIC(PsiThetaPhi.Roll);
		IC->SetPsiDegIC(PsiThetaPhi.Yaw + 90);
		IC->SetThetaDegIC(PsiThetaPhi.Pitch);
	}


	// Init our commands from the initial state
	Commands.Elevator = FCS->GetDeCmd();
	Commands.Aileron = FCS->GetDaCmd();
	Commands.Rudder = FCS->GetDrCmd();
	Commands.PitchTrim = FCS->GetPitchTrimCmd();
	Commands.YawTrim = FCS->GetYawTrimCmd();
	Commands.RollTrim = FCS->GetRollTrimCmd();
	// TODO - Others commands ?

	// Atmosphere
	if (ControlFDMAtmosphere)
	{
		Atmosphere->SetTemperature(TemperatureCelsius, GeographicCoordinates.Altitude * METER_TO_FEET, JSBSim::FGAtmosphere::eCelsius);
		Atmosphere->SetPressureSL(JSBSim::FGAtmosphere::ePascals, PressureSeaLevelhPa * 100.0);
		Winds->SetTurbType(JSBSim::FGWinds::ttNone);
		Winds->SetTurbGain(0.0);
		Winds->SetTurbRate(0.0);
		Winds->SetWindspeed20ft(0.0);
		Winds->SetProbabilityOfExceedence(0.0);
	}
	
	// Wind Speed
	IC->SetWindDirDegIC(WindHeading);
	IC->SetWindMagKtsIC(WindIntensityKts);
	
	// Aircraft Speed
	IC->SetVcalibratedKtsIC(InitialCalibratedAirSpeedKts);


	// Aircraft State
	// Flaps position
    Commands.Flap = FlapPositionAtStart;
	FCS->SetDfPos(ofNorm, FlapPositionAtStart);

	// Gear position
	if (bStartWithGearDown)
	{
		FCS->SetGearPos(1.0);
	}
	else
	{
		FCS->SetGearPos(0.0);
	}

    // Run IC to pre-initialize the JSBSim Initial Conditions for the model
	CopyToJSBSim();
	Exec->RunIC();
	UpdateLocalTransforms();

	if (bStartWithEngineRunning)
	{
		Propulsion->InitRunning(-1);

		int32 EngineCount = EngineCommands.Num();
		for (int32 i = 0; i < EngineCount; i++)
		{
			EngineCommands[i].Throttle = 0.0;
			EngineCommands[i].Mixture = 1.0;
            EngineCommands[i].Magnetos = EMagnetosMode::Both;
			EngineCommands[i].Running = true;
		}
	}

	if (TrimNeeded)
	{
		const JSBSim::FGLocation& cart = IC->GetPosition();
		//double cart_pos[3], contact[3], d[3], vel[3], agl;
		// Make sure the ground below is loaded... Not needed in our case...
		//update_ground_cache(cart, cart_pos, 0.01);

		// Get Above Ground Level -- TODO
		//get_agl_ft(fdmex->GetSimTime(), cart_pos, SG_METER_TO_FEET * 2, contact, d, vel, d, &agl);
		double terrain_elevation = 0;
		//double terrain_alt = sqrt(contact[0] * contact[0] + contact[1] * contact[1] + contact[2] * contact[2]) - cart.GetSeaLevelRadius();

		//UE_LOG(LogJSBSim, Display, TEXT("Ready to trim, terrain elevation is: %f"), terrain_elevation);
		double vel[3] = {0,0,0};

		if (StartOnGround) {
			FGColumnVector3 gndVelNED = cart.GetTec2l() * FGColumnVector3(vel[0], vel[1], vel[2]);
			IC->SetVNorthFpsIC(gndVelNED(1));
			IC->SetVEastFpsIC(gndVelNED(2));
			IC->SetVDownFpsIC(gndVelNED(3));
		}
		DoTrim();
		TrimNeeded = false;
	}

    // Aircraft Trim done - Get result state
	CopyFromJSBSim();
}

void UJSBSimMovementComponent::DeInitializeJSBSim()
{
	if (JSBSimInitialized)
	{
		UE_LOG(LogJSBSim, Display, TEXT("DeInitializeJSBSim"));

		if (Exec)
		{
			delete Exec;
			Exec = nullptr;

			Atmosphere = nullptr;
			Winds = nullptr;
			FCS = nullptr;
			MassBalance = nullptr;
			Propulsion = nullptr;
			Aircraft = nullptr;
			Propagate = nullptr;
			Auxiliary = nullptr;
			Inertial = nullptr;
			Aerodynamics = nullptr;
			GroundReactions = nullptr;
			Accelerations = nullptr;
			IC = nullptr;
		}

		JSBSimInitialized = false;
	}
}

void UJSBSimMovementComponent::CopyToJSBSim()
{
	// Basic flight controls
	FCS->SetDaCmd(Commands.Aileron);
	FCS->SetRollTrimCmd(Commands.RollTrim);
	FCS->SetDeCmd(Commands.Elevator);
	FCS->SetPitchTrimCmd(Commands.PitchTrim);
	FCS->SetDrCmd(-Commands.Rudder); // Rudder
	FCS->SetDsCmd(Commands.Rudder); // Steering
	FCS->SetYawTrimCmd(-Commands.YawTrim);
	FCS->SetDfCmd(Commands.Flap);
	FCS->SetDsbCmd(Commands.SpeedBrake);
	FCS->SetDspCmd(Commands.Spoiler);
	  
	// Gears and Brake controls
	FCS->SetLBrake(FMath::Max(Commands.LeftBrake, Commands.ParkingBrake));
	FCS->SetRBrake(FMath::Max(Commands.RightBrake, Commands.ParkingBrake));
	FCS->SetCBrake(FMath::Max(Commands.CenterBrake, Commands.ParkingBrake));
	FCS->SetGearCmd(Commands.GearDown); 

	ApplyEnginesCommands(); 

	// TODO - Update atmosphere
/*	Atmosphere->SetTemperature(temperature->getDoubleValue(), get_Altitude(), FGAtmosphere::eCelsius);
	Atmosphere->SetPressureSL(FGAtmosphere::eInchesHg, pressureSL->getDoubleValue());

	Winds->SetTurbType((FGWinds::tType)TURBULENCE_TYPE_NAMES[turbulence_model->getStringValue()]);
	switch (Winds->GetTurbType()) {
	case FGWinds::ttStandard:
	case FGWinds::ttCulp: {
		double tmp = turbulence_gain->getDoubleValue();
		Winds->SetTurbGain(tmp * tmp * 100.0);
		Winds->SetTurbRate(turbulence_rate->getDoubleValue());
		break;
	}
	case FGWinds::ttMilspec:
	case FGWinds::ttTustin: {
		// milspec turbulence: 3=light, 4=moderate, 6=severe turbulence
		// turbulence_gain normalized: 0: none, 1/3: light, 2/3: moderate, 3/3: severe
		double tmp = turbulence_gain->getDoubleValue();
		Winds->SetProbabilityOfExceedence(
			SGMiscd::roundToInt(TurbulenceSeverityTable.GetValue(tmp))
		);
		Winds->SetWindspeed20ft(ground_wind->getDoubleValue());
		break;
	}

	default:
		break;
	}

	Winds->SetWindNED(-wind_from_north->getDoubleValue(),
		-wind_from_east->getDoubleValue(),
		-wind_from_down->getDoubleValue()); 
*/

	CopyTankPropertiesToJSBSim();
	CopyGearPropertiesToJSBSim();
}

void UJSBSimMovementComponent::CopyFromJSBSim()
{
	// Collect JSBSim data
	Propagate->DumpState();
	
	// Keep Former Location in ECEF
	FVector FormerECEFLocation = AircraftState.ECEFLocation;

	// Get Aircraft forward vector in local (ECEF tangent) space. 
	// TODO - IDK if for the horizon indicator I should use the forward vector or the aircraft speed. 
	// Maybe the aircraft speed would include some kind of lateral slip ---> May one expert fix it if needed...
	JSBSim::FGColumnVector3 ForwardLocal = Propagate->GetTb2l() * JSBSim::FGColumnVector3(1, 0, 0);
	ECEFForwardHorizontal = FVector(ForwardLocal(2), -ForwardLocal(1), 0);

	// Update Moving part state
	AircraftState.ElevatorPosition = FCS->GetDePos(JSBSim::ofDeg);
	AircraftState.LeftAileronPosition = FCS->GetDaLPos(JSBSim::ofDeg);
	AircraftState.RightAileronPosition = FCS->GetDaRPos(JSBSim::ofDeg);
	AircraftState.RudderPosition = -1 * FCS->GetDrPos(JSBSim::ofDeg);
	AircraftState.FlapPosition = FCS->GetDfPos(JSBSim::ofDeg);
	AircraftState.SpeedBrakePosition = FCS->GetDsbPos(JSBSim::ofDeg);
	AircraftState.SpoilersPosition = FCS->GetDspPos(JSBSim::ofDeg);

	// Speed
	AircraftState.CalibratedAirSpeedKts = Auxiliary->GetVcalibratedKTS();
	AircraftState.GroundSpeedKts = Auxiliary->GetVground() * FEET_PER_SEC_TO_KNOT;
	AircraftState.TotalVelocityKts = Auxiliary->GetVt() * FEET_PER_SEC_TO_KNOT;
	AircraftState.VelocityNEDfps.Set(Propagate->GetVel(JSBSim::FGJSBBase::eNorth), Propagate->GetVel(JSBSim::FGJSBBase::eEast), -Propagate->GetVel(JSBSim::FGJSBBase::eDown));
	AircraftState.AltitudeASLFt = Propagate->GetAltitudeASL();
	AircraftState.AltitudeRateFtps = Propagate->Gethdot();;
	AircraftState.StallWarning = Aerodynamics->GetStallWarn();

	// Transformation
	JSBSim::FGLocation LocationVRP = Propagate->GetLocation();
	AircraftState.ECEFLocation = FVector(LocationVRP(1), LocationVRP(2), LocationVRP(3)) * FEET_TO_METER;
	AircraftState.Latitude = LocationVRP.GetGeodLatitudeDeg();
	AircraftState.Longitude = LocationVRP.GetLongitudeDeg();
	AircraftState.LocalEulerAngles.Yaw = FMath::RadiansToDegrees(Propagate->GetEuler(JSBSim::FGJSBBase::ePsi));
	AircraftState.LocalEulerAngles.Pitch = FMath::RadiansToDegrees(Propagate->GetEuler(JSBSim::FGJSBBase::eTht));
	AircraftState.LocalEulerAngles.Roll = FMath::RadiansToDegrees(Propagate->GetEuler(JSBSim::FGJSBBase::ePhi));
	AircraftState.EulerRates.Set(Auxiliary->GetEulerRates(JSBSim::FGJSBBase::ePhi), Auxiliary->GetEulerRates(JSBSim::FGJSBBase::eTht), Auxiliary->GetEulerRates(JSBSim::FGJSBBase::ePsi));
	AircraftState.AltitudeAGLFt = Propagate->GetDistanceAGL();
	// force a sim crashed if crashed (altitude AGL < 0)
	if (AircraftState.AltitudeAGLFt < -10.0 || AircraftState.AltitudeASLFt < -10.0) {
		Exec->SuspendIntegration();
		AircraftState.Crashed = true;
	  AircraftCrashed.Broadcast();
	}
	
	// Copy the fuel levels from JSBSim if fuel
	// freeze not enabled.
	CopyTankPropertiesFromJSBSim();
	CopyGearPropertiesFromJSBSim();
	GetEnginesStates();
}

/////////// JSBSim Private methods
void UJSBSimMovementComponent::DoTrim()
{
	JSBSim::FGTrim* Trim;

	if (StartOnGround)
	{
		Trim = new JSBSim::FGTrim(Exec, JSBSim::tGround);
	}
	else {
		Trim = new JSBSim::FGTrim(Exec, JSBSim::tFull);
	}

	if (!Trim->DoTrim()) 
	{
		Trim->Report();
		Trim->TrimStats();

        UE_LOG(LogJSBSim, Error, TEXT("Trim Failed!!!"));
	}
	else 
	{
		Trimmed = true;
	}
	delete Trim;

	Commands.PitchTrim = FCS->GetPitchTrimCmd();
	Commands.Aileron = FCS->GetDaCmd();
	Commands.Rudder = -FCS->GetDrCmd(); // TODO - Why this minus sign? Is it from FlightGear logic ?

	UE_LOG(LogJSBSim, Display, TEXT("Trim Complete"));
}

void UJSBSimMovementComponent::UpdateLocalTransforms()
{
    if (MassBalance == nullptr || Aircraft == nullptr || GroundReactions == nullptr)
      return;

	// Structural Frame To Actor Frame
	FMatrix StructuralToActorMatrix(FMatrix::Identity);
	StructuralToActorMatrix.SetAxis(0, FVector(-1, 0, 0));
	StructuralToActorMatrix.SetAxis(1, FVector(0, 1, 0));
	StructuralToActorMatrix.SetAxis(2, FVector(0, 0, 1));
	StructuralToActorMatrix.SetOrigin(StructuralFrameOrigin);
	StructuralToActor.SetFromMatrix(StructuralToActorMatrix);

	// Get Gravity Center
	JSBSim::FGColumnVector3 CGLocationStructural = MassBalance->StructuralToBody(JSBSim::FGColumnVector3()) * FEET_TO_CENTIMETER;
	CGLocalPosition = StructuralToActor.TransformPosition(FVector(CGLocationStructural(1), CGLocationStructural(2), CGLocationStructural(3)));

	// Body Frame to Actor Frame
	FMatrix BodyToActorMatrix(FMatrix::Identity);
	BodyToActorMatrix.SetAxis(0, FVector(1, 0, 0));
	BodyToActorMatrix.SetAxis(1, FVector(0, 1, 0));
	BodyToActorMatrix.SetAxis(2, FVector(0, 0, -1));
	BodyToActorMatrix.SetOrigin(CGLocalPosition);
	BodyToActor.SetFromMatrix(BodyToActorMatrix);

	// Eye Position
	JSBSim::FGColumnVector3 EPLocationStructural = Aircraft->GetXYZep() * INCH_TO_CENTIMETER;
	EPLocalPosition = StructuralToActor.TransformPosition(FVector(EPLocationStructural(1), EPLocationStructural(2), EPLocationStructural(3)));

	// Visual Reference Position
	JSBSim::FGColumnVector3 VRPLocationStructural = Aircraft->GetXYZvrp() * INCH_TO_CENTIMETER;
	VRPLocalPosition = StructuralToActor.TransformPosition(FVector(VRPLocationStructural(1), VRPLocationStructural(2), VRPLocationStructural(3)));

    // Gear Locations
    for (int i = 0; i < GroundReactions->GetNumGearUnits(); i++)
    {
      std::shared_ptr<JSBSim::FGLGear> Gear = GroundReactions->GetGearUnit(i);
      if ((int32)i < Gears.Num())
      {
        JSBSim::FGColumnVector3 GearBodyLocation = Gear->GetBodyLocation() * FEET_TO_CENTIMETER;
        Gears[i].RelativeLocation = BodyToActor.TransformPosition(FVector(GearBodyLocation(1), GearBodyLocation(2), GearBodyLocation(3)));
      }
    }
}

// Gears
void UJSBSimMovementComponent::InitGearDefaultProperties()
{
	uint32 GearsCount = GroundReactions->GetNumGearUnits();
	Gears.Empty();
	if (GearsCount > 0)
	{
		Gears.AddZeroed(GearsCount);

		for (unsigned int i = 0; i < GearsCount; i++)
		{
			std::shared_ptr<JSBSim::FGLGear> Gear = GroundReactions->GetGearUnit(i); // TODO - Test indices

			Gears[i].NormalizedPosition = Gear->GetGearUnitPos();
			Gears[i].IsBogey = Gear->IsBogey();
			Gears[i].HasWeightOnWheel = Gear->GetWOW();
			Gears[i].WheelRollLinearVelocityMetersPerSec = Gear->GetWheelRollVel() * FEET_TO_METER;
			Gears[i].IsUp = Gear->GetGearUnitUp();
			Gears[i].IsDown = Gear->GetGearUnitDown();
			Gears[i].Name = FString(Gear->GetName().c_str());
		}
	}
}

void UJSBSimMovementComponent::CopyGearPropertiesToJSBSim()
{
	// TODO - What can be changed from the default values? 
    // Maybe the initial extension, but not sure it can be done...
}

void UJSBSimMovementComponent::CopyGearPropertiesFromJSBSim()
{
	for (int i = 0; i < GroundReactions->GetNumGearUnits(); i++)
	{
		std::shared_ptr<JSBSim::FGLGear> Gear = GroundReactions->GetGearUnit(i);
		if ((int32)i < Gears.Num())
		{
			Gears[i].NormalizedPosition = Gear->GetGearUnitPos();
			Gears[i].IsBogey = Gear->IsBogey();
			Gears[i].HasWeightOnWheel = Gear->GetWOW();
			Gears[i].WheelRollLinearVelocityMetersPerSec = Gear->GetWheelRollVel() * FEET_TO_METER;
			Gears[i].IsUp = Gear->GetGearUnitUp();
			Gears[i].IsDown = Gear->GetGearUnitDown();

			JSBSim::FGColumnVector3 GearBodyLocation = Gear->GetBodyLocation() * FEET_TO_CENTIMETER;
			Gears[i].RelativeLocation = BodyToActor.TransformPosition(FVector(GearBodyLocation(1), GearBodyLocation(2), GearBodyLocation(3)));

			JSBSim::FGColumnVector3 GearBodyForce = Gear->GetBodyForces() * FEET_TO_CENTIMETER;
			Gears[i].Force = BodyToActor.TransformPosition(FVector(GearBodyForce(1), GearBodyForce(2), GearBodyForce(3)));
		}
	}
}

// Tanks
void UJSBSimMovementComponent::InitTankDefaultProperties()
{
	// Set initial fuel levels if overridden by the user.
	uint32 TanksCount = Propulsion->GetNumTanks();
	Tanks.Empty();
	if (TanksCount > 0)
	{
		Tanks.AddZeroed(TanksCount);
		for (unsigned int i = 0; i < TanksCount; i++)
		{
			std::shared_ptr<JSBSim::FGTank> Tank = Propulsion->GetTank(i);

			Tanks[i].FuelDensityPoundsPerGallon = Tank->GetDensity();
			Tanks[i].ContentGallons = Tank->GetContentsGallons();
			Tanks[i].CapacityGallons = Tank->GetCapacityGallons();
			Tanks[i].FillPercentage = Tank->GetPctFull();
			Tanks[i].TemperatureCelcius = Tank->GetTemperature_degC();
		}
	}
}

void UJSBSimMovementComponent::CopyTankPropertiesToJSBSim()
{
	for (unsigned i = 0; i < Propulsion->GetNumTanks(); i++)
	{
		std::shared_ptr<JSBSim::FGTank> tank = Propulsion->GetTank(i);

		if ((int32)i < Tanks.Num())
		{
			FTank UETank = Tanks[i];
			double fuelDensity = UETank.FuelDensityPoundsPerGallon;

			if (fuelDensity < 0.1)
				fuelDensity = 6.0; // Use average fuel value

			// Only editable properties
			tank->SetDensity(fuelDensity);
			tank->SetContentsGallons(UETank.ContentGallons);
		}
	}
}

void UJSBSimMovementComponent::CopyTankPropertiesFromJSBSim()
{
	FuelFreeze = Propulsion->GetFuelFreeze();
	for (unsigned int i = 0; i < Propulsion->GetNumTanks(); i++)
	{
		std::shared_ptr<JSBSim::FGTank> Tank = Propulsion->GetTank(i);

		if ((int32)i < Tanks.Num())
		{
			Tanks[i].FuelDensityPoundsPerGallon = Tank->GetDensity();
			Tanks[i].ContentGallons = Tank->GetContentsGallons();
			Tanks[i].CapacityGallons = Tank->GetCapacityGallons();
			Tanks[i].FillPercentage = Tank->GetPctFull();
			Tanks[i].TemperatureCelcius = Tank->GetTemperature_degC();
		}
	}
}


// Engines
void UJSBSimMovementComponent::InitEnginesCommandAndStates()
{
	EngineCommands.Empty();
	EngineStates.Empty();

	uint32 EngineCount = Propulsion->GetNumEngines();
	if (EngineCount > 0)
	{
		// Allocate UE equivalent structures
		EngineCommands.AddZeroed(EngineCount);
		EngineStates.AddZeroed(EngineCount);

		// Apply default properties
		// TODO - Not sure there are to apply. it will be done by command/states
	}
}

void UJSBSimMovementComponent::ApplyEnginesCommands()
{
	// Global to all engines
	Propulsion->SetFuelFreeze(FuelFreeze);

	// For each engine
	int32 EngineCount = EngineCommands.Num();
	for (int32 i = 0; i < EngineCount; i++)
	{
		FEngineCommand EngineCommand = EngineCommands[i];

		// Global FCS Commands
		FCS->SetThrottleCmd(i, EngineCommand.Throttle);
		FCS->SetMixtureCmd(i, EngineCommand.Mixture);
		FCS->SetPropAdvanceCmd(i, EngineCommand.PropellerAdvance);
		FCS->SetFeatherCmd(i, EngineCommand.PropellerFeather);

		// Common FGEngine code block
		std::shared_ptr < JSBSim::FGEngine> CommonEngine = Propulsion->GetEngine(i);
		CommonEngine->SetStarter(EngineCommand.Starter);
		CommonEngine->SetRunning(EngineCommand.Running);

		switch (Propulsion->GetEngine(i)->GetType())
		{
		case JSBSim::FGEngine::etPiston:
		{
			// FGPiston code block
			std::shared_ptr < JSBSim::FGPiston> PistonEngine = std::static_pointer_cast<JSBSim::FGPiston>(Propulsion->GetEngine(i));
			PistonEngine->SetMagnetos((int)EngineCommand.Magnetos);
			break;
		}
		case JSBSim::FGEngine::etTurbine:
		{
			// FGTurbine code block
			std::shared_ptr < JSBSim::FGTurbine> TurbineEngine = std::static_pointer_cast<JSBSim::FGTurbine>(Propulsion->GetEngine(i));
			TurbineEngine->SetReverse(EngineCommand.Reverse);
			TurbineEngine->SetCutoff(EngineCommand.CutOff);
			TurbineEngine->SetIgnition(EngineCommand.Ignition);
			TurbineEngine->SetAugmentation(EngineCommand.Augmentation);
			TurbineEngine->SetInjection(EngineCommand.Injection);
			break;
		}
		case JSBSim::FGEngine::etRocket:
		{
			// FGRocket code block
			// FGRocket* RocketEngine = (FGRocket*)Propulsion->GetEngine(i);
			break;
		}
		case JSBSim::FGEngine::etTurboprop:
		{
			// FGTurboProp code block
			std::shared_ptr < JSBSim::FGTurboProp> TurboPropEngine = std::static_pointer_cast<JSBSim::FGTurboProp>(Propulsion->GetEngine(i));
			TurboPropEngine->SetReverse(EngineCommand.Reverse);
			TurboPropEngine->SetCutoff(EngineCommand.CutOff);
			TurboPropEngine->SetGeneratorPower(EngineCommand.GeneratorPower);
			TurboPropEngine->SetCondition(EngineCommand.Condition);
			break;
		}
		default:
			break;
		}
	}
}

void UJSBSimMovementComponent::GetEnginesStates()
{
	// For each engine
	int32 EngineCount = EngineStates.Num();
	for (int32 i = 0; i < EngineCount; i++)
	{
		std::shared_ptr < JSBSim::FGEngine> Engine = Propulsion->GetEngine(i);

		EngineStates[i].EngineType = (EEngineType) Engine->GetType();
		EngineStates[i].Starter = Engine->GetStarter();
		EngineStates[i].Running = Engine->GetRunning();
		EngineStates[i].Thrust = Engine->GetThrust();
		EngineStates[i].EngineRPM = Engine->GetThruster()->GetEngineRPM();

		switch (Engine->GetType())
		{
		case JSBSim::FGEngine::etPiston:
		{
			// TODO
			// FGPiston code block
            std::shared_ptr < JSBSim::FGPiston> PistonEngine = std::static_pointer_cast<JSBSim::FGPiston>(Engine);
            EngineStates[i].Magnetos = (EMagnetosMode) PistonEngine->GetMagnetos();
			break;
		}
		case JSBSim::FGEngine::etTurbine:
		{
			// TODO
			// FGTurbine code block
			std::shared_ptr < JSBSim::FGTurbine> TurbineEngine = std::static_pointer_cast<JSBSim::FGTurbine>(Engine);
			EngineStates[i].N1 = TurbineEngine->GetN1();
			EngineStates[i].N2 = TurbineEngine->GetN2();
			EngineStates[i].Augmentation = TurbineEngine->GetAugmentation();
			EngineStates[i].Reversed = TurbineEngine->GetReversed();
			EngineStates[i].Injection = TurbineEngine->GetInjection();
			EngineStates[i].CutOff = TurbineEngine->GetCutoff();
			EngineStates[i].Ignition = TurbineEngine->GetIgnition();
			break;
		}
		case JSBSim::FGEngine::etRocket:
		{
			// TODO
			// FGRocket code block
			// FGRocket* RocketEngine = (FGRocket*)Propulsion->GetEngine(i);
			break;
		}
		case JSBSim::FGEngine::etTurboprop:
		{
			// TODO
			//// FGTurboProp code block
			//JSBSim::FGTurboProp* TurboPropEngine = (JSBSim::FGTurboProp*)Propulsion->GetEngine(i);
			//TurboPropEngine->SetReverse(EngineCommand.Reverse);
			//TurboPropEngine->SetCutoff(EngineCommand.CutOff);
			//TurboPropEngine->SetGeneratorPower(EngineCommand.GeneratorPower);
			//TurboPropEngine->SetCondition(EngineCommand.Condition);
			break;
		}
		default:
			break;
		}
	}
}

/////////// Logging and Debugging Methods

void UJSBSimMovementComponent::LogInitialization()
{
	UE_LOG(LogJSBSim, Display, TEXT("Initialized JSB Sim with : "));

	// Speed
	switch (IC->GetSpeedSet())
	{
		//typedef enum { setvt, setve, setvg } speedset; ??? 
	case JSBSim::setned: // North East Down
		UE_LOG(LogJSBSim, Display, TEXT("  Vn,Ve,Vd= %f, $f, $f  ft/s"), Propagate->GetVel(JSBSim::FGJSBBase::eNorth), Propagate->GetVel(JSBSim::FGJSBBase::eEast), Propagate->GetVel(JSBSim::FGJSBBase::eDown));
		break;
	case JSBSim::setuvw:
		UE_LOG(LogJSBSim, Display, TEXT("  U,V,W= %f, $f, $f  ft/s"), Propagate->GetUVW(1), Propagate->GetUVW(2), Propagate->GetUVW(3));
		break;
	case JSBSim::setmach:
		UE_LOG(LogJSBSim, Display, TEXT("  Mach: %f, $f, $f  ft/s"), Auxiliary->GetMach());
		break;
	case JSBSim::setvc:
	default:
		UE_LOG(LogJSBSim, Display, TEXT("  Indicated Airspeed: %f knots"), Auxiliary->GetVcalibratedKTS());
		break;
	}
	
	// Angles
	UE_LOG(LogJSBSim, Display, TEXT("  Bank: %f, Pitch: %f, True Heading: %f"), 
		FMath::RadiansToDegrees(Propagate->GetEuler(JSBSim::FGJSBBase::ePhi)),
		FMath::RadiansToDegrees(Propagate->GetEuler(JSBSim::FGJSBBase::eTht)),
		FMath::RadiansToDegrees(Propagate->GetEuler(JSBSim::FGJSBBase::ePsi)));

	// Lat/Long
	UE_LOG(LogJSBSim, Display, TEXT("  Latitude: %f, Longitude: %f deg, Altitude: %f feet"), Propagate->GetLocation().GetGeodLatitudeDeg(),	Propagate->GetLocation().GetLongitudeDeg(),	Propagate->GetAltitudeASL());
}

void UJSBSimMovementComponent::DrawDebugMessage()
{
	// Build message string before displaying it at once
	FString DebugMessage;

	// Commands
	DebugMessage += Commands.GetDebugMessage();

	// Engines 
	int32 NumEngines = EngineCommands.Num();
	// Engine Commands
	DebugMessage += LINE_TERMINATOR;
	DebugMessage += FString::Printf(TEXT("Engines Commands (%d) : "), NumEngines) + LINE_TERMINATOR;
	for (int32 i = 0; i < NumEngines; i++)
	{
		DebugMessage += FString::Printf(TEXT("    #%d    "), i) + EngineCommands[i].GetDebugMessage();
	}
	// Engine States
	DebugMessage += LINE_TERMINATOR;
	DebugMessage += FString::Printf(TEXT("Engines States (%d) : "), NumEngines) + LINE_TERMINATOR;
	for (int32 i = 0; i < NumEngines; i++)
	{
		DebugMessage += FString::Printf(TEXT("    #%d    "), i) + EngineStates[i].GetDebugMessage();
	}

	// Tanks
	DebugMessage += LINE_TERMINATOR;
	int32 NumTanks = Tanks.Num();
	DebugMessage += FString::Printf(TEXT("Tanks (%d) : "), NumTanks) + LINE_TERMINATOR;
	for (int32 i = 0; i < NumTanks; i++)
	{
		DebugMessage += FString::Printf(TEXT("    #%d    "), i) + Tanks[i].GetDebugMessage();
	}

	// Gears
	DebugMessage += LINE_TERMINATOR;
	int32 NumGears = Gears.Num();
	DebugMessage += FString::Printf(TEXT("Landing Gears (%d) : "), NumGears) + LINE_TERMINATOR;
	for (int32 i = 0; i < NumGears; i++)
	{
		if (Gears[i].IsBogey)
		{
			DebugMessage += FString::Printf(TEXT("    #%d    "), i) + Gears[i].GetDebugMessage();
		}
	}

	// Aircraft State 
	DebugMessage += LINE_TERMINATOR;
	DebugMessage += AircraftState.GetDebugMessage();

	// Draw
    FVector2D TextScale = FVector2D::UnitVector;
	GEngine->AddOnScreenDebugMessage(1, 0, FColor::Green, *DebugMessage, false, TextScale);
}

void UJSBSimMovementComponent::DrawDebugObjects()
{
	for (FGear Gear : Gears)
	{
		FVector WorldPosition = GetOwner()->GetTransform().TransformPosition(Gear.RelativeLocation);
		
		if (Gear.IsBogey)
		{
			if (Gear.HasWeightOnWheel)
			{
				DrawDebugPoint(GetWorld(), WorldPosition, 8, FColor::Red, false);
				DrawDebugLine(GetWorld(), WorldPosition, WorldPosition + Gear.Force, FColor::Red, false, -1, 0, 3);
			}
			else
			{
				DrawDebugPoint(GetWorld(), WorldPosition, 8, FColor::Green, false);
				
			}
		}
		else
		{
			DrawDebugPoint(GetWorld(), WorldPosition, 8, FColor(128,128,128), false);
		}
	}


	//for (int i = 0; i < GroundReactions->GetNumGearUnits(); i++)
	//{
	//	JSBSim::FGLGear* Gear = GroundReactions->GetGearUnit(i);

	//	if (Gear->IsBogey())
	//	{
	//		JSBSim::FGColumnVector3 BodyLocation = Gear->GetBodyLocation();

	//		double XOffset = BodyLocation(1);
	//		double YOffset = BodyLocation(2);
	//		double ZOffset = -BodyLocation(3);

	//		FVector WorldPosition = GetOwner()->GetTransform().TransformPosition(FVector(XOffset, YOffset, ZOffset) * FEET_TO_METER * 100);
	//		DrawDebugPoint(GetWorld(), WorldPosition, 8, FColor::Red, false);
	//	}
	//	
	//}
}


/////////// In-Editor Specific
#if WITH_EDITOR

void UJSBSimMovementComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
	static const FName NAME_AircraftModel = GET_MEMBER_NAME_CHECKED(UJSBSimMovementComponent, AircraftModel);
	if (PropertyChangedEvent.Property != nullptr && PropertyChangedEvent.Property->GetFName() == NAME_AircraftModel)
	{
		// Load the aircraft, and make sure we recreate the Component properties for this new one. 
		LoadAircraft(true);
	}

	static const FName NAME_StructuralFrameOrigin = GET_MEMBER_NAME_CHECKED(UJSBSimMovementComponent, StructuralFrameOrigin);
	if (PropertyChangedEvent.Property != nullptr && PropertyChangedEvent.MemberProperty->GetFName() == NAME_StructuralFrameOrigin)
	{
		UpdateLocalTransforms();
	}

	Super::PostEditChangeProperty(PropertyChangedEvent);
}

void UJSBSimMovementComponent::PrepareModelForCompVisualizer()
{
	if (!IsReadyForCompVisualizer)
	{
		if (Exec == nullptr || !AircraftModel.Compare(FString(Exec->GetModelName().c_str()), ESearchCase::IgnoreCase))
		{
			LoadAircraft(false);
			PrepareJSBSim();
			IsReadyForCompVisualizer = true;
		}
	}
}

#endif
