# RCAIDE/Methods/Energy/Propulsors/Constant_Speed_ICE_Propulsor/compute_cs_ice_performance.py
# 
# 
# Created:  Jul 2023, M. Clarke

# ----------------------------------------------------------------------------------------------------------------------
#  IMPORT
# ----------------------------------------------------------------------------------------------------------------------
# RCAIDE imports  
from RCAIDE.Framework.Core import Units  
from RCAIDE.Library.Methods.Powertrain.Converters.Engine import compute_throttle_from_power
from RCAIDE.Library.Methods.Powertrain.Converters.Rotor.compute_rotor_performance import  compute_rotor_performance
 
# pacakge imports  
from copy import deepcopy
import numpy as np  

# ----------------------------------------------------------------------------------------------------------------------
# internal_combustion_engine_constant_speed_propulsor
# ----------------------------------------------------------------------------------------------------------------------  
def compute_constant_speed_internal_combustion_engine_performance(propulsor, state, center_of_gravity=[[0.0, 0.0, 0.0]]):
    """
    Computes the performance of a constant speed internal combustion engine.
    
    Parameters
    ----------
    propulsor : RCAIDE.Library.Components.Propulsors.Constant_Speed_Internal_Combustion_Engine
        Constant speed internal combustion engine component with the following attributes:
            - tag : str
                Identifier for the propulsor
            - engine : Data
                Engine component
                    - tag : str
                        Identifier for the engine
            - propeller : Data
                Propeller component
                    - tag : str
                        Identifier for the propeller
                    - origin : list of lists
                        Origin coordinates [[x, y, z]] [m]
    state : RCAIDE.Framework.Mission.Common.State
        State object containing:
            - conditions : Data
                Flight conditions
                    - energy : Data
                        Energy conditions
                        - propulsors : dict
                            Propulsor energy conditions indexed by tag
                        - converters : dict
                            Converter energy conditions indexed by tag
            - ones_row : function
                Function to create array of ones with specified length
    center_of_gravity : list of lists, optional
        Center of gravity coordinates [[x, y, z]] [m]
        Default: [[0.0, 0.0, 0.0]]
    
    Returns
    -------
    thrust : numpy.ndarray
        Thrust force vector [N]
    moment : numpy.ndarray
        Moment vector [N·m]
    power : numpy.ndarray
        Mechanical power [W]
    power_elec : numpy.ndarray
        Electrical power [W] (zero for non-hybrid engines)
    stored_results_flag : bool
        Flag indicating if results are stored
    stored_propulsor_tag : str
        Tag of the propulsor with stored results
    
    Notes
    -----
    This function computes the performance of a constant speed internal combustion engine
    propulsion system by linking the engine to the propeller. Unlike variable speed engines,
    the constant speed engine maintains a fixed angular velocity regardless of flight condition.
    
    The function performs the following steps:
        - Sets the propeller's angular velocity to match the engine's constant speed
        - Sets the propeller's blade pitch command based on the throttle setting
        - Computes the propeller performance
        - Calculates the moment generated by the propeller thrust
        - Computes the engine power required to match the propeller power demand
        - Calculates the fuel flow rate based on the power demand
        - Stores the results in the conditions data structure
    
    The constant speed is maintained by varying the propeller blade pitch rather than
    the engine RPM, which is common in generator applications and some aircraft engines.
    
    **Major Assumptions**
        * Engine operates at a constant angular velocity
        * Propeller blade pitch is adjusted to control thrust
        * No electrical power generation or consumption (non-hybrid configuration)
        * Moment is calculated relative to the aircraft center of gravity
    
    See Also
    --------
    RCAIDE.Library.Methods.Powertrain.Converters.Engine.compute_throttle_from_power
    RCAIDE.Library.Methods.Powertrain.Converters.Rotor.compute_rotor_performance
    """
    conditions              = state.conditions  
    ice_cs_conditions       = conditions.energy.propulsors[propulsor.tag] 
    engine                  = propulsor.engine 
    propeller               = propulsor.propeller 

    # Run the propeller to get the power 
    conditions.energy.converters[propeller.tag].omega                = conditions.energy.converters[engine.tag].omega  
    conditions.energy.converters[propeller.tag].blade_pitch_command  = ice_cs_conditions.throttle - 0.5
    conditions.energy.converters[propeller.tag].throttle             = ice_cs_conditions.throttle
    compute_rotor_performance(propeller,conditions)

    # Compute moment 
    moment_vector           = 0*state.ones_row(3)
    moment_vector[:,0]      = propeller.origin[0][0]  -  center_of_gravity[0][0] 
    moment_vector[:,1]      = propeller.origin[0][1]  -  center_of_gravity[0][1] 
    moment_vector[:,2]      = propeller.origin[0][2]  -  center_of_gravity[0][2]
    moment                  =  np.cross(moment_vector, conditions.energy.converters[propeller.tag].thrust)       
        

    # Run the engine to calculate the throttle setting and the fuel burn
    conditions.energy.converters[engine.tag].power        = conditions.energy.converters[propeller.tag].power 
    compute_throttle_from_power(engine,conditions) 
    
    # Create the outputs
    ice_cs_conditions.fuel_flow_rate         = conditions.energy.converters[engine.tag].fuel_flow_rate  
    stored_results_flag                      = True
    stored_propulsor_tag                     = propulsor.tag  

    # compute total forces and moments from propulsor (future work would be to add moments from motors)
    ice_cs_conditions.thrust      = conditions.energy.converters[propeller.tag].thrust 
    ice_cs_conditions.moment      = moment
    ice_cs_conditions.power       = conditions.energy.converters[propeller.tag].power  

    # currently, no hybridization
    power_elec =  0*state.ones_row(1)
    
    return ice_cs_conditions.thrust ,ice_cs_conditions.moment,ice_cs_conditions.power,power_elec,stored_results_flag,stored_propulsor_tag 
    
def reuse_stored_constant_speed_internal_combustion_engine_data(propulsor,state,network,stored_propulsor_tag,center_of_gravity= [[0.0, 0.0,0.0]]):
    '''Reuses results from one propulsor for identical propulsors
    
    Assumptions: 
    N/A

    Source:
    N/A

    Inputs:  
    conditions           - operating conditions data structure    [-]  
    fuel_line            - fuel_line                              [-] 
    propulsor            - propulsor data structure     [-] 
    total_thrust         - thrust of propulsor group       [N]
    total_power          - power of propulsor group        [W] 
     
    Outputs:      
    total_thrust         - thrust of propulsor group       [N]
    total_power          - power of propulsor group        [W] 
    
    Properties Used: 
    N.A.        
    '''
    # unpack 
    conditions                 = state.conditions
    engine                     = propulsor.engine
    propeller                  = propulsor.propeller 
    engine_0                   = network.propulsors[stored_propulsor_tag].engine
    propeller_0                = network.propulsors[stored_propulsor_tag].propeller 
    
    # deep copy results
    conditions.energy.propulsors[propulsor.tag]     = deepcopy(conditions.energy.propulsors[stored_propulsor_tag.tag])
    conditions.energy.converters[engine.tag]        = deepcopy(conditions.energy.converters[engine_0.tag])
    conditions.energy.converters[propeller.tag]     = deepcopy(conditions.energy.converters[propeller_0.tag])

    # compute moment    
    thrust                  = conditions.energy.converters[propeller.tag].thrust 
    power                   = conditions.energy.converters[propeller.tag].power  
    moment_vector           = 0*state.ones_row(3) 
    moment_vector[:,0]      = propeller.origin[0][0]  -  center_of_gravity[0][0] 
    moment_vector[:,1]      = propeller.origin[0][1]  -  center_of_gravity[0][1] 
    moment_vector[:,2]      = propeller.origin[0][2]  -  center_of_gravity[0][2]
    moment                  =  np.cross(moment_vector, thrust)
    
    # pack results 
    conditions.energy.converters[propeller.tag].moment = moment  
    conditions.energy.propulsors[propulsor.tag].thrust = thrust   
    conditions.energy.propulsors[propulsor.tag].moment = moment  
    conditions.energy.propulsors[propulsor.tag].power  = power
    
    power_elec =  0*state.ones_row(1)  
    return thrust,moment,power, power_elec
 