# RCAIDE/Methods/Energy/Propulsors/Electric_Rotor_Propulsor/compute_electric_rotor_performance.py
# 
# 
# Created:  Jul 2023, M. Clarke

# ----------------------------------------------------------------------------------------------------------------------
#  IMPORT
# ----------------------------------------------------------------------------------------------------------------------
# RCAIDE imports   
from RCAIDE.Library.Methods.Powertrain.Modulators.Electronic_Speed_Controller.compute_esc_performance  import * 
from RCAIDE.Library.Methods.Powertrain.Converters.Motor.compute_motor_performance                      import *
from RCAIDE.Library.Methods.Powertrain.Converters.Rotor.compute_rotor_performance                      import * 

# pacakge imports  
import numpy as np 
from copy import deepcopy

# ----------------------------------------------------------------------------------------------------------------------
# compute_electric_rotor_performance
# ----------------------------------------------------------------------------------------------------------------------  
def compute_electric_rotor_performance(propulsor, state, center_of_gravity=[[0.0, 0.0, 0.0]]):
    """
    Computes the performance of an electric rotor propulsion system.
    
    Parameters
    ----------
    propulsor : RCAIDE.Library.Components.Propulsors.Electric_Rotor
        Electric rotor propulsor component with the following attributes:
            - tag : str
                Identifier for the propulsor
            - motor : Data
                Electric motor component
                - tag : str
                    Identifier for the motor
            - rotor : Data
                Rotor component
                    - tag : str
                        Identifier for the rotor
                    - origin : list of lists
                        Origin coordinates [[x, y, z]] [m]
            - electronic_speed_controller : Data
                ESC component
                    - tag : str
                        Identifier for the ESC
    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
                            - modulators : dict
                                Modulator 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]
    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 an electric rotor propulsion system
    by linking the electronic speed controller (ESC), motor, and rotor. It performs 
    the following steps:
        * Sets the ESC throttle based on the propulsor throttle
        * Computes the ESC output voltage
        * Transfers the ESC output voltage to the motor
        * Computes the motor performance (angular velocity and torque)
        * Transfers the motor angular velocity to the rotor
        * Computes the rotor performance
        * Calculates the moment generated by the rotor thrust
        * Computes the ESC input current
        * Stores the results in the conditions data structure
    
    **Major Assumptions**
        * The ESC throttle directly controls the output voltage
        * The motor angular velocity directly drives the rotor
        * Moment is calculated relative to the aircraft center of gravity
        * Moment from motor rotation is not included
    
    See Also
    --------
    RCAIDE.Library.Methods.Powertrain.Converters.Motor.compute_motor_performance
    RCAIDE.Library.Methods.Powertrain.Converters.Rotor.compute_rotor_performance
    RCAIDE.Library.Methods.Powertrain.Modulators.Electronic_Speed_Controller
    """
     
    conditions                 = state.conditions    
    motor                      = propulsor.motor 
    rotor                      = propulsor.rotor 
    esc                        = propulsor.electronic_speed_controller   
    electric_rotor_conditions  = conditions.energy.propulsors[propulsor.tag]
    eta                        = electric_rotor_conditions.throttle
     
    conditions.energy.modulators[esc.tag].throttle         = eta 
    compute_voltage_out_from_throttle(esc,conditions)

    # Assign conditions to the rotor
    conditions.energy.converters[motor.tag].inputs.voltage = conditions.energy.modulators[esc.tag].outputs.voltage  
    compute_motor_performance(motor,conditions) 
    
    # Spin the rotor 
    conditions.energy.converters[rotor.tag].omega           = conditions.energy.converters[motor.tag].outputs.omega
    conditions.energy.converters[rotor.tag].throttle        = conditions.energy.modulators[esc.tag].throttle      
    conditions.energy.converters[rotor.tag].commanded_thrust_vector_angle =  conditions.energy.propulsors[propulsor.tag].commanded_thrust_vector_angle
    compute_rotor_performance(rotor,conditions)
 
    # Compute moment 
    moment_vector           = 0*state.ones_row(3)
    moment_vector[:,0]      = rotor.origin[0][0]  -  center_of_gravity[0][0] 
    moment_vector[:,1]      = rotor.origin[0][1]  -  center_of_gravity[0][1] 
    moment_vector[:,2]      = rotor.origin[0][2]  -  center_of_gravity[0][2]
    moment                  =  np.cross(moment_vector, conditions.energy.converters[rotor.tag].thrust)     
    
    # Detemine esc current 
    conditions.energy.modulators[esc.tag].outputs.current = conditions.energy.converters[motor.tag].inputs.current
    compute_current_in_from_throttle(esc,conditions)
    
    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)
    electric_rotor_conditions.thrust      = conditions.energy.converters[rotor.tag].thrust 
    electric_rotor_conditions.power       = conditions.energy.converters[rotor.tag].power 
    electric_rotor_conditions.moment      = moment
    
    return electric_rotor_conditions.thrust,electric_rotor_conditions.moment,electric_rotor_conditions.power,conditions.energy.modulators[esc.tag].inputs.power,stored_results_flag,stored_propulsor_tag 
                
def reuse_stored_electric_rotor_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    [-] 
    voltage              - system voltage                         [V]
    bus                  - bus                                    [-] 
    propulsors           - propulsor data structure               [-] 
    total_thrust         - thrust of propulsor group              [N]
    total_power          - power of propulsor group               [W]
    total_current        - current of propulsor group             [A]

    Outputs:  
    total_thrust         - thrust of propulsor group              [N]
    total_power          - power of propulsor group               [W]
    total_current        - current of propulsor group             [A] 
    
    Properties Used: 
    N.A.        
    ''' 
    conditions                 = state.conditions 
    motor                      = propulsor.motor 
    rotor                      = propulsor.rotor 
    esc                        = propulsor.electronic_speed_controller  
    motor_0                    = network.propulsors[stored_propulsor_tag].motor 
    rotor_0                    = network.propulsors[stored_propulsor_tag].rotor 
    esc_0                      = network.propulsors[stored_propulsor_tag].electronic_speed_controller
    
    conditions.energy.converters[motor.tag]        = deepcopy(conditions.energy.converters[motor_0.tag])
    conditions.energy.converters[rotor.tag]        = deepcopy(conditions.energy.converters[rotor_0.tag])
    conditions.energy.modulators[esc.tag]          = deepcopy(conditions.energy.modulators[esc_0.tag])
  
    thrust_vector           = conditions.energy.converters[rotor.tag].thrust 
    P_mech                  = conditions.energy.converters[rotor.tag].power 
    P_elec                  = conditions.energy.modulators[esc.tag].inputs.power    
    
    moment_vector           = 0*state.ones_row(3) 
    moment_vector[:,0]      = rotor.origin[0][0]  -  center_of_gravity[0][0] 
    moment_vector[:,1]      = rotor.origin[0][1]  -  center_of_gravity[0][1] 
    moment_vector[:,2]      = rotor.origin[0][2]  -  center_of_gravity[0][2]
    moment                  =  np.cross(moment_vector, thrust_vector)
     
    conditions.energy.propulsors[propulsor.tag].power             = P_mech  
    conditions.energy.propulsors[propulsor.tag].thrust            = thrust_vector  
    conditions.energy.propulsors[propulsor.tag].moment            = moment          
    return thrust_vector,moment,P_mech,P_elec