function mttWriteSystemMapping(model)

sympar = map_system_sympars(model) ;
input  = map_system_inputs(model) ;
invar  = map_system_invars(model) ;
outvar = map_system_outvars(model) ;
state  = map_system_states(model) ;

derivative = differentiate(state) ;

mapping.input = invar ;
if ~isempty(input)
    mapping.input = [mapping.input,input] ;
end
if ~isempty(sympar)
    mapping.input = [mapping.input,sympar] ;
end

mapping.output = outvar ;
mapping.state  = state ;
mapping.derivative = derivative ;

write_get_input(mapping,model) ;
write_get_state(mapping,model) ;

write_put_input(mapping,model) ;
write_put_state(mapping,model) ;
write_put_output(mapping,model) ;
write_put_derivative(mapping,model) ;



function map = map_system_sympars(model)
	mttNotify('...mapping system inputs (symbolic parameters)') ;
    mttWriteNewLine ;

    model_name = mttDetachText(model.source,'/') ;
    
    map = [] ;
    line = 0 ;
    
    number_of_variables = mttGetFieldLength(model,'sympar') ;
    
    for i = 1:number_of_variables
        variable_name = model.sympar{i} ;
        default_value = model.sympar_default{i} ;
        
        if isempty(default_value)
            line = line + 1 ;
            map{line} = [model_name,'___',variable_name] ;
        end
    end
    
    
function map = map_system_inputs(model,root,branch)
    is_root_model = nargin==1 ;
    
    if is_root_model
        mttNotify('...mapping system inputs (input variables)') ;
        mttWriteNewLine ;
        
        root = model ;
        current_branch = mttDetachText(model.source,'/') ;
    else
        current_branch = branch ;
    end

    map = [] ;
    line = 0 ;
    
    number_of_variables = mttGetFieldLength(model,'input') ;
    
    for i = 1:number_of_variables
        variable_name = model.input{i} ;
        
        line = line + 1 ;
        map{line} = [current_branch,'___',variable_name] ;
    end
    
    objects = mttGetFieldNames(model,'obj') ;
    for i = 1:length(objects)
        object_name = objects{i} ;
        object = getfield(model,'obj',object_name) ;
        
        next_branch = [current_branch,'__',object_name] ;
        
        additional_map = [] ;
        if ~isempty(object.abg)
            additional_map = map_system_inputs(object,root,next_branch) ;
        elseif ~isempty(object.cr)
            additional_map = map_system_inputs(object.cr,root,next_branch) ;
        end
        
        if isempty(map)
            map = additional_map ;
        else
            line = length(map) ;
            for j = 1:length(additional_map)
                if ~ismember(additional_map{j},map)
                    line = line + 1 ;
                    map{line} = additional_map{j} ;
                end
            end
        end
    end
    
    
function map = map_system_invars(model,root,branch,env)
    is_root_model = nargin==1 ;
    
    if is_root_model
        mttNotify('...mapping system inputs (input covariables)') ;
        mttWriteNewLine ;
        
        root = model ;
        current_branch = mttDetachText(model.source,'/') ;
        env = model.env ;
    else
        current_branch = branch ;
    end

    map = [] ;
    line = 0 ;
    
    objects = mttGetFieldNames(model,'obj') ;
    for i = 1:length(objects)
        object_name = objects{i} ;
        object = getfield(model,'obj',object_name) ;
        
        next_branch = [current_branch,'__',object_name] ;
        
        switch object.class
        case {'SS','Se','Sf','De','Df'}
            inbond_number = object.interface.in ;
            outbond_number = object.interface.out ;
            
            inbond = model.bond(inbond_number) ;
            outbond = model.bond(outbond_number) ;
            
            if ~isempty(inbond)
                covariables = mttGetCovariables(env,inbond.domain,inbond.domain_item) ;
                covar = [] ;
                
                if ~inbond.effort
                    if ~strcmp(object.class,'Df')
                        covar = covariables.effort ;
                    end
                elseif inbond.flow
                    if ~strcmp(object.class,'De')
                        covar = covariables.flow ;
                    end
                end
                
                for j = 1:length(covar)
                    line = line + 1 ;
                    map{line} = [current_branch,'__',object_name,'.',strrep(covar{j},'.','__')] ;
                end
            end
            
            if ~isempty(outbond)
                covariables = mttGetCovariables(env,outbond.domain,outbond.domain_item) ;
                covar = [] ;
                
                if outbond.effort
                    covar = covariables.effort ;
                elseif ~outbond.flow
                    covar = covariables.flow ;
                end
                
                for j = 1:length(covar)
                    line = line + 1 ;
                    map{line} = [current_branch,'__',object_name,'.',strrep(covar{j},'.','__')] ;
                end
            end
        end
        
        additional_map = [] ;
        if ~isempty(object.abg)
            additional_map = map_system_invars(object,root,next_branch,env) ;
        end
        
        if isempty(map)
            map = additional_map ;
        else
            line = length(map) ;
            for j = 1:length(additional_map)
                if ~ismember(additional_map{j},map)
                    line = line + 1 ;
                    map{line} = additional_map{j} ;
                end
            end
        end
    end
    
    
function map = map_system_outvars(model,root,branch,env)
    is_root_model = nargin==1 ;
    
    if is_root_model
        mttNotify('...mapping system outputs (output covariables)') ;
        mttWriteNewLine ;
        
        root = model ;
        current_branch = mttDetachText(model.source,'/') ;
        env = model.env ;
    else
        current_branch = branch ;
    end

    map = [] ;
    line = 0 ;
    
    objects = mttGetFieldNames(model,'obj') ;
    for i = 1:length(objects)
        object_name = objects{i} ;
        object = getfield(model,'obj',object_name) ;
        
        next_branch = [current_branch,'__',object_name] ;
        
        switch object.class
        case {'SS','Se','Sf','De','Df'}
            inbond_number = object.interface.in ;
            outbond_number = object.interface.out ;
            
            inbond = model.bond(inbond_number) ;
            outbond = model.bond(outbond_number) ;
            
            if ~isempty(inbond)
                covariables = mttGetCovariables(env,inbond.domain,inbond.domain_item) ;
                covar = [] ;
                
                if inbond.effort
                    covar = covariables.effort ;
                elseif ~inbond.flow
                    covar = covariables.flow ;
                end
                
                for j = 1:length(covar)
                    line = line + 1 ;
                    map{line} = [current_branch,'__',object_name,'.',strrep(covar{j},'.','__')] ;
                end
            end
            
            if ~isempty(outbond)
                covariables = mttGetCovariables(env,outbond.domain,outbond.domain_item) ;
                covar = [] ;
                
                if ~outbond.effort
                    if ~strcmp(object.class,'Sf')
                        covar = covariables.effort ;
                    end
                elseif outbond.flow
                    if ~strcmp(object.class,'Se')
                        covar = covariables.flow ;
                    end
                end
                
                for j = 1:length(covar)
                    line = line + 1 ;
                    map{line} = [current_branch,'__',object_name,'.',strrep(covar{j},'.','__')] ;
                end
            end
        end
        
        additional_map = [] ;
        if ~isempty(object.abg)
            additional_map = map_system_outvars(object,root,next_branch,env) ;
        end
        
        if isempty(map)
            map = additional_map ;
        else
            line = length(map) ;
            for j = 1:length(additional_map)
                if ~ismember(additional_map{j},map)
                    line = line + 1 ;
                    map{line} = additional_map{j} ;
                end
            end
        end
    end
    
    
function map = map_system_states(model,root,branch,env)
    is_root_model = nargin==1 ;
    
    if is_root_model
        mttNotify('...mapping system states') ;
        mttWriteNewLine ;
        
        root = model ;
        current_branch = mttDetachText(model.source,'/') ;
        env = model.env ;
    else
        current_branch = branch ;
    end
    
    map = [] ;
    line = 0 ;
    
    if isfield(model,'state')
        assigned_states = [] ;
        counter = 0 ;
        
        port_names = mttGetFieldNames(model.interface,'port') ;
        for j = 1:length(port_names)
            port_name = port_names{j} ;
            port = getfield(model,'interface','port',port_name) ;
            
            if ~isempty(port.assign)
                assignment = port.assign ;
                
                if port.was_generic & ~isempty(port.domain)
                    covariables = mttGetCovariables(env,port.domain,port.domain_item) ;
                    if port.is_effort_state
                        covar = covariables.effort ;
                    else
                        covar = covariables.flow ;
                    end
                    
                    block_size = length(covar) ;
                    for k = 1:length(assignment.state)
                        counter = counter + 1 ;
                        assigned_states{counter} = assignment.state{k} ;
                        
                        for var = 1:block_size
                            line = line + 1 ;
                            map{line} = [current_branch,'___',assignment.state{k},'___',covar{var},'.state'] ;
                        end                        
                    end
                end
            end
        end
        
        number_of_variables = mttGetFieldLength(model,'state') ;
        
        for i = 1:number_of_variables
            variable_name = model.state{i} ;
            
            if ~ismember(variable_name,assigned_states)
                line = line + 1 ;
                map{line} = [current_branch,'___',variable_name,'.state'] ;
            end
        end
    end
    
    
    objects = mttGetFieldNames(model,'obj') ;
    for i = 1:length(objects)
        object_name = objects{i} ;
        object = getfield(model,'obj',object_name) ;
        
        next_branch = [current_branch,'__',object_name] ;
        
        additional_map = [] ;
        if ~isempty(object.abg)
            additional_map = map_system_states(object,root,next_branch,env) ;
        elseif ~isempty(object.cr)
            additional_map = map_system_states(object.cr,root,next_branch,env) ;
        end
        
        if isempty(map)
            map = additional_map ;
        else
            line = length(map) ;
            for j = 1:length(additional_map)
                if ~ismember(additional_map{j},map)
                    line = line + 1 ;
                    map{line} = additional_map{j} ;
                end
            end
        end
    end
    
    
function derivative = differentiate(state)
    mttNotify('...mapping system derivatives') ;
    mttWriteNewLine ;
    
    for i = 1:length(state)
        derivative{i} = strrep(state{i},'.state','.derivative') ;
    end
    
    
function write_get_input(mapping,model)
    filename = [model.source,'_include_get_input.h'] ;
    fid = fopen(filename,'w') ;
    
    mttNotify(['...creating ',filename]) ;
    mttWriteNewLine ;
    
    fprintf(fid,['// Input get-mapping for Ordinary Differential Equations\n']) ;
    fprintf(fid,'\n') ;
    fprintf(fid,['// file: ',filename,'\n']) ;
    fprintf(fid,['// written by MTT on ',datestr(now),'\n']) ;
    
    model_name = mttDetachText(model.source,'/') ;
    
    write_mapping(fid,'_mttInput',mapping.input,model_name,1) ;
   
    fclose(fid) ;
    
    
function write_put_input(mapping,model)
    filename = [model.source,'_include_put_input.h'] ;
    fid = fopen(filename,'w') ;
    
    mttNotify(['...creating ',filename]) ;
    mttWriteNewLine ;
    
    fprintf(fid,['// Input put-mapping for Ordinary Differential Equations\n']) ;
    fprintf(fid,'\n') ;
    fprintf(fid,['// file: ',filename,'\n']) ;
    fprintf(fid,['// written by MTT on ',datestr(now),'\n']) ;
    
    model_name = mttDetachText(model.source,'/') ;
    
    write_mapping(fid,'_mttInput',mapping.input,model_name,0) ;
   
    fclose(fid) ;
    
    
function write_get_state(mapping,model)
    filename = [model.source,'_include_get_state.h'] ;
    fid = fopen(filename,'w') ;
    
    mttNotify(['...creating ',filename]) ;
    mttWriteNewLine ;
    
    fprintf(fid,['// State get-mapping for Ordinary Differential Equations\n']) ;
    fprintf(fid,'\n') ;
    fprintf(fid,['// file: ',filename,'\n']) ;
    fprintf(fid,['// written by MTT on ',datestr(now),'\n']) ;
    
    model_name = mttDetachText(model.source,'/') ;
    
    write_mapping(fid,'_mttState',mapping.state,model_name,1) ;
   
    fclose(fid) ;
    
    
function write_put_state(mapping,model)
    filename = [model.source,'_include_put_state.h'] ;
    fid = fopen(filename,'w') ;
    
    mttNotify(['...creating ',filename]) ;
    mttWriteNewLine ;
    
    fprintf(fid,['// State put-mapping for Ordinary Differential Equations\n']) ;
    fprintf(fid,'\n') ;
    fprintf(fid,['// file: ',filename,'\n']) ;
    fprintf(fid,['// written by MTT on ',datestr(now),'\n']) ;
    
    model_name = mttDetachText(model.source,'/') ;
    
    write_mapping(fid,'_mttState',mapping.state,model_name,0) ;
   
    fclose(fid) ;
    
    
function write_put_output(mapping,model)
    filename = [model.source,'_include_put_output.h'] ;
    fid = fopen(filename,'w') ;
    
    mttNotify(['...creating ',filename]) ;
    mttWriteNewLine ;
    
    fprintf(fid,['// Output put-mapping for Ordinary Differential Equations\n']) ;
    fprintf(fid,'\n') ;
    fprintf(fid,['// file: ',filename,'\n']) ;
    fprintf(fid,['// written by MTT on ',datestr(now),'\n']) ;
    
    model_name = mttDetachText(model.source,'/') ;
    
    write_mapping(fid,'_mttOutput',mapping.output,model_name,0) ;
   
    fclose(fid) ;
    
    
function write_put_derivative(mapping,model)
    filename = [model.source,'_include_put_derivative.h'] ;
    fid = fopen(filename,'w') ;
    
    mttNotify(['...creating ',filename]) ;
    mttWriteNewLine ;
    
    fprintf(fid,['// Derivative put-mapping for Ordinary Differential Equations\n']) ;
    fprintf(fid,'\n') ;
    fprintf(fid,['// file: ',filename,'\n']) ;
    fprintf(fid,['// written by MTT on ',datestr(now),'\n']) ;
    
    model_name = mttDetachText(model.source,'/') ;
    
    write_mapping(fid,'_mttDerivative',mapping.derivative,model_name,0) ;
   
    fclose(fid) ;
    
    
function write_mapping(fid,array_name,var_list,model_name,is_inward_mapping)
    if ~isempty(var_list)
        var_list = sort(var_list) ;
        
        width = 0 ;
        for i = 1:length(var_list)
            width = max(width,length(var_list{i})) ;
        end
    
        tab = char(32*ones(1,3)) ;
        
        fprintf(fid,'\n') ;
        for i = 1:length(var_list)
            variable = var_list{i} ;
            
            if is_inward_mapping
                nominal_width = width ;
                actual_width = length(variable) ;
                
                gap = nominal_width - actual_width ;
                whitespace = char(32*ones(1,gap)) ;
                
                formatted_mapping = [tab,variable,whitespace,' = ',array_name,'[',num2str(i-1),'] ;\n'] ;
            else
                formatted_mapping = [tab,array_name,'[',num2str(i-1),'] = ',variable,' ;\n'] ;
            end
                
            fprintf(fid,formatted_mapping) ;
        end        
    end
