function [ transmission ] = REVS_gen_transmission( transmission_type, num_gears, rated_torque_Nm, varargin )
%function [ transmission ] = REVS_gen_transmission( transmission_type, num_gears, rated_torque_Nm, varargin )
% ex: (GM6T40 proxy)
% >> transmission_type = enum_transmission_type.automatic;
% >> transmission = REVS_gen_transmission(transmission_type, 6, 250, 'spread', 6.145, 'min_ratio', 0.746, 'auto_progression', 'include_neutral');
% ex: (2014 ZF proxy)
% >> transmission = REVS_gen_transmission(transmission_type, 8, 250, 'spread', 7.01, 'min_ratio', 0.67, 'auto_progression', 'include_neutral', 'eff_adj', 1.01, 'pmp_adj', 0.5, 'sl_adj', 0.66, 'lp_adj', 0.5);
%
% varargs:
% arithmetic          = parse_varargs(varargin, 'arithmetic', false, 'toggle');
% geometric           = parse_varargs(varargin, 'geometric', false, 'toggle');
% phi                 = parse_varargs(varargin, 'phi', [], 'numeric');
% auto_progression    = parse_varargs(varargin, 'auto_progression', false, 'toggle');
% min_ratio           = parse_varargs(varargin, 'min_ratio', [], 'numeric');
% max_ratio           = parse_varargs(varargin, 'max_ratio', [], 'numeric');
% spread              = parse_varargs(varargin, 'spread', [], 'numeric');
% include_neutral     = parse_varargs(varargin, 'include_neutral', false, 'toggle');
% drive_ratios        = parse_varargs(varargin, 'drive_ratios', [], 'numeric');
% engine              = parse_varargs(varargin, 'engine', [], 'numeric');
% name                = parse_varargs(varargin, 'name', '', 'string');
% shift_duration_secs = parse_varargs(varargin, 'shift_duration_secs', 1.5*6/num_gears, 'numeric');
% eff_adj             = parse_varargs(varargin, 'eff_adj', 1.0, 'numeric');
% pmp_adj             = parse_varargs(varargin, 'pmp_adj', 1.0, 'numeric');
% sl_adj              = parse_varargs(varargin, 'sl_adj', 1.0, 'numeric');
% lp_adj              = parse_varargs(varargin, 'lp_adj', 1.0, 'numeric');

arithmetic          = parse_varargs(varargin, 'arithmetic', false, 'toggle');
geometric           = parse_varargs(varargin, 'geometric', false, 'toggle');
phi                 = parse_varargs(varargin, 'phi', [], 'numeric');
auto_progression    = parse_varargs(varargin, 'auto_progression', false, 'toggle');
min_ratio           = parse_varargs(varargin, 'min_ratio', [], 'numeric');
max_ratio           = parse_varargs(varargin, 'max_ratio', [], 'numeric');
spread              = parse_varargs(varargin, 'spread', [], 'numeric');
include_neutral     = parse_varargs(varargin, 'include_neutral', false, 'toggle');
drive_ratios        = parse_varargs(varargin, 'drive_ratios', [], 'numeric');
engine              = parse_varargs(varargin, 'engine', [], 'numeric');
name                = parse_varargs(varargin, 'name', '', 'string');
shift_duration_secs = parse_varargs(varargin, 'shift_duration_secs', 1.5*6/num_gears, 'numeric');
eff_adj             = parse_varargs(varargin, 'eff_adj', 1.0, 'numeric');
pmp_adj             = parse_varargs(varargin, 'pmp_adj', 1.0, 'numeric');
sl_adj              = parse_varargs(varargin, 'sl_adj', 1.0, 'numeric');
lp_adj              = parse_varargs(varargin, 'lp_adj', 1.0, 'numeric');

if transmission_type == enum_transmission_type.automatic
    transmission = class_REVS_AT_transmission;
elseif transmission_type == enum_transmission_type.DCT
    transmission = class_REVS_DCT_transmission;
end

transmission.rated_torque_Nm = rated_torque_Nm;

if transmission_type == enum_transmission_type.CVT
    transmission.gear.shift_duration_secs = 0.01;
else
    transmission.gear.shift_duration_secs = shift_duration_secs;
end

if transmission_type == enum_transmission_type.automatic
    % set up gear ratios
    transmission.gear.type = transmission_type;
    
    transmission.gear.number = (include_neutral - 1):num_gears;
    
    if isempty(drive_ratios)
        %% ratio input check
        if isempty(spread) && (isempty(min_ratio) || isempty(max_ratio))
            error('Must provide min or max and spread or min and max ratios');
        elseif ~isempty(min_ratio) && ~isempty(spread) && ~isempty(max_ratio)
            error('Transmission may be overspecified, please provide min and max ratios or min or max and spread');
        elseif ~isempty(min_ratio) && ~isempty(spread)
            max_ratio = min_ratio * spread;
        elseif ~isempty(max_ratio) && ~isempty(spread)
            min_ratio = max_ratio / spread;
        end
        
        if isempty(spread)
            spread = max_ratio/min_ratio;
        end
        
        %% gear ratios
        if auto_progression
            % default phi values roughly based on production transmissions...
            phi = interp1([3 4 5 6 7 8 10],[1.125 1.125 1.09 1.04 1.06 1.035 1.03],max(3,min(10,num_gears)));
            
            transmission.gear.ratio = REVS_gen_transmission_ratios(num_gears, 'phi', phi, varargin{:});
        else
            transmission.gear.ratio = REVS_gen_transmission_ratios(num_gears, varargin{:});
        end
        
        if include_neutral && (transmission.gear.ratio(1) ~= 0)
            transmission.gear.ratio = [0 transmission.gear.ratio];
        end
    else
        transmission.gear.ratio = [0 drive_ratios];
    end
    
    %% pump loss
    transmission.pump_loss_Nm.axis_1.signal = 'gb.gb_line_press_bar';
    transmission.pump_loss_Nm.axis_1.breakpoints = [5 15];
    
    transmission.pump_loss_Nm.axis_2.signal = 'tc_spd_in_radps';
    transmission.pump_loss_Nm.axis_2.breakpoints = [500  5000] * convert.rpm2radps;
    
    transmission.pump_loss_Nm.axis_3.signal =  'trans_temp_degC';
    transmission.pump_loss_Nm.axis_3.breakpoints = [35.0 93.0];
    
    transmission.pump_loss_Nm.table(:,:,1) = 5.3 * ones(2,2);
    transmission.pump_loss_Nm.table(:,:,2) = 3.8 * ones(2,2);
    
    transmission.pump_loss_Nm.table = pmp_adj * transmission.pump_loss_Nm.table * rated_torque_Nm / 250;
    
    %% gear efficiency
    transmission.gear.efficiency_norm.axis_1.signal      = 'gb_cmd_gear';
    transmission.gear.efficiency_norm.axis_1.breakpoints = transmission.gear.number;
    %        transmission.gear.efficiency_norm.table              = 0.98 * ones(size(transmission.gear.efficiency_norm.axis_1.breakpoints));
    transmission.gear.efficiency_norm.table              = eff_adj * linspace(0.965, 0.98, length(transmission.gear.number));
    
    %% spin loss
    transmission.gear.input_torque_loss_Nm.axis_1.signal = 'gb.gb_cmd_gear';
    transmission.gear.input_torque_loss_Nm.axis_1.breakpoints = transmission.gear.number;
    
    transmission.gear.input_torque_loss_Nm.axis_2.signal = 'gb.gb_spd_in_radps';
    transmission.gear.input_torque_loss_Nm.axis_2.breakpoints = convert.rpm2radps * [ 500 2500 5000 ];
    
    transmission.gear.input_torque_loss_Nm.axis_3.signal = 'ctrl.gb_line_press_bar';
    transmission.gear.input_torque_loss_Nm.axis_3.breakpoints = [ 5, 10 ];
    transmission.gear.input_torque_loss_Nm.axis_3.extrapolate_above = true;
    transmission.gear.input_torque_loss_Nm.axis_3.extrapolate_below = true;
    
    transmission.gear.input_torque_loss_Nm.axis_4.signal = 'trans_temp_degC';
    transmission.gear.input_torque_loss_Nm.axis_4.breakpoints = [ 37, 93 ];
    
    normalized_gear_number = max(0.2, (transmission.gear.number-1)/(max(transmission.gear.number)-1));
    
    %         % SPIN LOSS (5 bar) @ 37C
    %         transmission.gear.input_torque_loss_Nm.table(:,:,1,1) = repmat(8.017 * normalized_gear_number' + 0.2157, 1, 2);
    %         % SPIN LOSS (10 bar) @ 37C
    %         transmission.gear.input_torque_loss_Nm.table(:,:,2,1) = repmat(8.570 * normalized_gear_number' + 2.277, 1, 2);
    %
    %         % SPIN LOSS (5 bar) @ 93C
    %         transmission.gear.input_torque_loss_Nm.table(:,:,1,2)  = repmat(4.841 * normalized_gear_number' - 0.2810, 1, 2);
    %         % SPIN LOSS (10 bar) @ 93C
    %         transmission.gear.input_torque_loss_Nm.table(:,:,2,2) = repmat(5.158 * normalized_gear_number' + 1.26, 1, 2);
    transmission.gear.input_torque_loss_Nm.table = [];
    % SPIN LOSS (5 bar) @ 37C
	transmission.gear.input_torque_loss_Nm.table = [];
    transmission.gear.input_torque_loss_Nm.table(:,:,1,1) = 0.9 * [0.5 * (8.017 * normalized_gear_number' + 0.2157), sl_adj^3 * 1.25 * (8.017 * normalized_gear_number' + 0.2157), sl_adj * 2 * (8.017 * normalized_gear_number' + 0.2157)];
    % SPIN LOSS (10 bar) @ 37C
    transmission.gear.input_torque_loss_Nm.table(:,:,2,1) = [0.5 * (8.570 * normalized_gear_number' + 2.2770), sl_adj^3 * 1.25 * (8.570 * normalized_gear_number' + 2.277), sl_adj * 2 * (8.570 * normalized_gear_number' + 2.277)];
    
    % SPIN LOSS (5 bar) @ 93C
    transmission.gear.input_torque_loss_Nm.table(:,:,1,2) = 0.9 * [0.5 * (4.841 * normalized_gear_number' - 0.2810), sl_adj^3 * 1.25 * (4.841 * normalized_gear_number' - 0.2810), sl_adj * 2 * (4.841 * normalized_gear_number' - 0.2810)];
    % SPIN LOSS (10 bar) @ 93C
    transmission.gear.input_torque_loss_Nm.table(:,:,2,2) = [0.5 * (5.158 * normalized_gear_number' + 1.2600), sl_adj^3 * 1.25 * (5.158 * normalized_gear_number' + 1.260), sl_adj * 2 * (5.158 * normalized_gear_number' + 1.26)];
    
    transmission.gear.input_torque_loss_Nm.table = transmission.gear.input_torque_loss_Nm.table * rated_torque_Nm / 250;
    
    %% output torque loss
    transmission.gear.output_torque_loss_Nm.axis_1.signal = 'gb.gb_spd_out_radps';
    transmission.gear.output_torque_loss_Nm.axis_1.breakpoints = convert.rpm2radps * [ 0 5000 ];
    
    transmission.gear.output_torque_loss_Nm.table = [0,0];
    
    %% line pressure
    transmission.control.line_press_rate_limit_barps = 35;
    
    transmission.control.line_pressure_map_bar.axis_1.signal = 'ctrl.gear_num';
    transmission.control.line_pressure_map_bar.axis_1.breakpoints = [0 1];
    transmission.control.line_pressure_map_bar.axis_2.signal = 'eng_load_Nm';
    transmission.control.line_pressure_map_bar.axis_2.breakpoints = linspace(0, rated_torque_Nm, 10);
    transmission.control.line_pressure_map_bar.axis_3.signal = 'driver_brk_norm';
    transmission.control.line_pressure_map_bar.axis_3.breakpoints = [0 0.01];
    transmission.control.line_pressure_map_bar.axis_4.signal = 'veh_spd_mps';
    transmission.control.line_pressure_map_bar.axis_4.breakpoints = [0 0.1];
    
    torque_norm = linspace(0, 1, 10);
    transmission.control.line_pressure_map_bar.table = [];
	transmission.control.line_pressure_map_bar.table = [];
    transmission.control.line_pressure_map_bar.table(:,:,1,1) = [ 5.000 * ones(1,length(torque_norm));                 % neutral
        7.163 * torque_norm.^2 + 1.253 * torque_norm + 5 ];  % rest of gears
    
    transmission.control.line_pressure_map_bar.table(:,:,2,1) = 8; % on brakes, neutral and all gears
    
    transmission.control.line_pressure_map_bar.table(:,:,1,2) = transmission.control.line_pressure_map_bar.table(:,:,1,1); % no change with temperature
    transmission.control.line_pressure_map_bar.table(:,:,2,2) = transmission.control.line_pressure_map_bar.table(:,:,1,1);
    
    transmission.control.line_pressure_map_bar.table = transmission.control.line_pressure_map_bar.table * lp_adj;
    
    %% inertia
    transmission.gear.inertia_kgm2                  = 0.0125 * ones(size(transmission.gear.number)) * (rated_torque_Nm/250)^0.5;
    transmission.gear.common_input_inertia_kgm2     = 0.00625 * (rated_torque_Nm/250)^0.5;
    transmission.gear.common_output_inertia_kgm2    = 0.00625 * (rated_torque_Nm/250)^0.5;
    
    %% trans name
    if ~isempty(name)
        transmission.name = name;
    else
        transmission.name = [strrep(mat2str(transmission_type),'enum_transmission_type.','') '_' num2str(num_gears) 'g_' num2str(spread) 'spr'];
    end
end % if automatic

end
