% Input:
% input: struct (name and path attribut, both string variables)

% Output:
% vars: cell array  of strings (containing the names of the variables)
% variables: integer (number of variables in the model)
% Input: cell array of strings (containing the names of the input
%                               variables)
% states: cell array of structs (containing for each location the invariant
%                                specifiers)
% q: vector of integers (containing state indices)
% qa: integer (start location)
% output_transitions: cell array of structs (describing the transitions)
% qq: matrix of integers (describing the transitions)

% Description:
% Loads the stateflow model specified by input returning the contained
% information in form of the output parameters.

% Beschreibung:
% läd das unter input befindliche stateflow Modell indem die dort
% enthaltenen Informationen in Form der Ausgabeparameter zurückgegeben
% werden. Var und Input enthalten die Benennnung der (Input-)Variablen im
% Modell

function [vars,variables,Input,states,q,qa,output_transitions,qq] = loadStateflow(input)

[~,filename] = sfopen_config(input.name,input.path);
model = sfroot();
% model name has to be equal to file name
model = model.find('-isa','Simulink.BlockDiagram','-and','Name',filename);

states_class = model.find('-isa','Stateflow.State');
states = cell(1);
transitions = model.find('-isa','Stateflow.Transition');
Input = cell(1);

% transformation
q = zeros(1,length(states_class));
for i=1:1:length(states_class)
    q(1,i) = states_class(i).Id;
    
    [vars,equ,input,InvC,Invd] = parseFlow(states_class(i).LabelString);
    states{i}.Var = vars;
    states{i}.Equ = equ;
    states{i}.C = InvC;
    states{i}.d = Invd;
    %states{i}.b = [];
    if isempty(Input{1})
        Input = input;
    else
        if ~isempty(input{1})
            Input = [Input input];
        end;
    end;
end;

minid = min(q)-1;
q = q - minid;

if ~isempty(Input{1})
    Input = unique(Input);
end;

% check number of variables and order
variables = length(states{1}.Var);
for i=1:1:length(states)
    if variables ~= length(states{i}.Var)
        error('Number of variables differ between states !');
    end;
    
    for j=1:1:variables
        if ~strcmp(states{1}.Var{j},states{i}.Var{j})
            error('Flow of variables unordered !')
        end;
    end;
end;

% define automaton for parsing operation
edges =         [1 3 2 0;...
                 1 4 3 0;...
                 1 2 5 0;...
                 1 1 7 0;...
                 2 7 7 0;...
                 2 2 5 1;...
                 2 4 3 0;...
                 3 3 2 1;...
                 3 4 3 0;...
                 3 1 4 0;...
                 3 2 5 1;...
                 3 6 6 1;...
                 4 5 7 0;...
                 4 4 8 0;...
                 5 3 2 2;...
                 5 8 5 0;...
                 5 6 6 2;...
                 5 1 7 0;...
                 8 3 2 1;...
                 8 2 5 1;...
                 8 6 6 1;...
                 8 1 7 0;...
                 8 4 8 0];
             
expr = {'\.' '[a-z]' '(+|-)' '[0-9]' '(+|-|\.|[a-z]|#)' '#' '(\.|#)' '\w'};
             
states = parseStates(states,expr,edges,variables,Input,vars);

%----------------------------------------------------------------------------------------
% Handle transitions
nbr_exp = '(-)?[0123456789]+(\.[0123456789]+)?';
nbr_exp2 = '(-)?([0123456789]+(\.[0123456789]+)?)?';
qq = NaN(2,length(transitions)-1); %there should be 1 initial state defined
qa = NaN;
realtransitions = [];
for i=1:1:length(transitions)
    if ~isempty(transitions(i).Source)
        qq(1,i) = transitions(i).Source.Id;
        qq(2,i) = transitions(i).Destination.Id;
        realtransitions = [realtransitions i];
    else
        qa = transitions(i).Destination.Id;
    end;       
end;
transitions = transitions(realtransitions);
qq = qq(:,~isnan(qq(1,:)));

% define initial values
expression = ',';
expfixpoint = 'fixpoint';
expspontan = 'time';

output_transitions = cell(length(transitions),1);
for i=1:1:length(transitions)
    
    output_transitions{i}.ResetMatrix = eye(variables);
    output_transitions{i}.W = zeros(variables,1);
    done = false;

    if isempty(transitions(i).LabelString) || strcmp(transitions(i).LabelString,'?')
        error('Model contains unlabeled transition');
    end;
    
    label_string = strrep(transitions(i).LabelString, '[', '');
    label_string = strrep(label_string, ']', '');
    
        % split transitions in guard and reset part
        if ~isempty(findstr(expression, label_string))
            splitStr = regexp(label_string,expression,'split');
        else
            splitStr = {label_string};
        end;
    
        if length(splitStr) > 2
            error('Syntax error in transition label ! Too many commata.');
        else
 
        % --------------------------------------------------------------------------------------    
            fixpoint = regexp(splitStr{1},expfixpoint,'match');
            if ~isempty(fixpoint)
                % Fixpoint transition detected
                output_transitions{i}.spontaneous = true;
                output_transitions{i}.timeelapse = -1;
                done = true;
            else
                spontan = regexp(splitStr{1},expspontan,'match');
                if ~isempty(spontan)
                    % Spontaneous time transition detected -> read time
                    output_transitions{i}.spontaneous = true;
                    
                    exp = '[0123456789]+(\.[0123456789]+)?';
                    result = regexp(splitStr{1},exp,'match');
                    if length(result) > 1 || isempty(result{1})
                        error('Syntax error in fixpoint driven transition!');
                    else
                        output_transitions{i}.timeelapse = str2double(result);
                        done = true;
                    end;
                end;   
            end;
            
            output_transitions{i}.eguards_dir = [];
            output_transitions{i}.eguards_val = [];
            output_transitions{i}.iguards_dir = [];
            output_transitions{i}.iguards_val = [];
    
    if ~done 
    % no spontaneous transition, read regular transition

            output_transitions{i}.spontaneous = false;
            if ~isempty(findstr(splitStr{1},'&'))
                guards = splitInput(splitStr{1},'&');
            else
                guards = {splitStr{1}};
                if strcmp(splitStr{1},'?')  % no label at transition -> there is no guard
                    guards = [];
                end;
            end;

            nbr_equalityguards = length(regexp(splitStr{1},'==','match'));
            nbr_inequalityguards = length(guards)-nbr_equalityguards;
            if nbr_inequalityguards < 0
                nbr_inequalityguards = 0;
            end;           
            
            output_transitions{i}.eguards_val = NaN(1,nbr_equalityguards);
            output_transitions{i}.eguards_dir = NaN(variables,nbr_equalityguards);
            
            output_transitions{i}.iguards_val = NaN(1,nbr_inequalityguards);
            output_transitions{i}.iguards_dir = NaN(variables,nbr_inequalityguards);
            
            counterqq = 0;
            counterqqi = 0;
            for j=1:length(guards)  %for each guard on a transition
                
                guards{j} = simplesyntaxcheck(guards{j});
                
                if ~isempty(regexp(guards{j},'==', 'once'))
                    %handle equality guard
                    counterqq = counterqq+1;
                    
                    [eg_leftend,eg_rightstart] = regexp(guards{j},'==');
                    eg_leftend = eg_leftend -1;
                    eg_rightstart = eg_rightstart +1;
        
                    for k=1:1:variables
                        exp = sprintf('%s%s',nbr_exp2,states{1}.Var{k});
                        coef = regexp(guards{j}(1:eg_leftend),exp,'match');
             
                        if ~isempty(coef)
                            for l=1:1:length(coef)
                                coef{l} = coef{l}(1:length(coef{l})-length(states{1}.Var{k}));
                                if coef{l} == '-'
                                    coef{l} = '-1';
                                end;
                                if isempty(coef{l})
                                    coef{l} = '1';
                                end;
                                if isnan(output_transitions{i}.eguards_dir(k,counterqq))
                                    output_transitions{i}.eguards_dir(:,counterqq) = zeros(variables,1);
                                end;
                                output_transitions{i}.eguards_dir(k,counterqq) =  output_transitions{i}.eguards_dir(k,counterqq) + str2double(coef{l});
                            end;
                        end;
                    end;
            
                    value = regexp(guards{j}(eg_rightstart:length(guards{j})),nbr_exp,'match');
                    output_transitions{i}.eguards_val(counterqq) = str2double(value{1});
                    
                end;
                
                factor = 1;
                if ~isempty(regexp(guards{j},'>=', 'once'))
                    %handle greater or equal guard
                    
                    [eg_leftend,eg_rightstart] = regexp(guards{j},'>=');
                    factor = -1;
                    %proceed with less or equal guard
                end;
                
                if ~isempty(regexp(guards{j},'<=', 'once')) || factor == -1
                    %handle less or equal guard
                    counterqqi = counterqqi +1;
                    
                    if factor ~= -1
                        [eg_leftend,eg_rightstart] = regexp(guards{j},'<=');
                    end;
                    eg_leftend = eg_leftend -1;
                    eg_rightstart = eg_rightstart +1;
                    
                    for k=1:1:variables
                        exp = sprintf('%s%s',nbr_exp2,states{1}.Var{k});
                        coef = regexp(guards{j}(1:eg_leftend),exp,'match');
             
                        if ~isempty(coef)
                            for l=1:1:length(coef)
                                coef{l} = coef{l}(1:length(coef{l})-length(states{1}.Var{k}));
                                if coef{l} == '-'
                                    coef{l} = '-1';
                                end;
                                if isempty(coef{l})
                                    coef{l} = '1';
                                end;
                                if isnan(output_transitions{i}.iguards_dir(k,counterqqi))
                                    output_transitions{i}.iguards_dir(:,counterqqi) = zeros(variables,1);
                                end;
                                output_transitions{i}.iguards_dir(k,counterqqi) =  output_transitions{i}.iguards_dir(k,counterqqi) + str2double(coef{l});
                            end;
                        end;
                    end;
            
                    value = regexp(guards{j}(eg_rightstart:length(guards{j})),nbr_exp,'match');
                    output_transitions{i}.iguards_val(counterqqi) = factor*str2double(value{1});
                    output_transitions{i}.iguards_dir(:,counterqqi) = factor*output_transitions{i}.iguards_dir(:,counterqqi);
                end;
                
                guards{j} = strrep(guards{j}, ' ', '');
                if ~isempty(regexp(guards{j},'<[0123456789]', 'once'))
                    error('< operator not allowed, use <= instead');
                end;
                
                if ~isempty(regexp(guards{j},'>[0123456789]', 'once'))
                    error('> operator not allowed, use >= instead');
                end;
                
            end;
    end;
        
        if length(splitStr) >1 &&  ~isempty(splitStr{2})
            
            % transform reset (splitStr{2})
            resets = [];
            resets = splitInput(splitStr{2},'&');
            rowlist = zeros(1,length(resets));
            for j=1:1:length(resets)
            
                resets{j} = simplesyntaxcheck(resets{j});
                
                % read start of line
                start = [];
                leftend = -1;
                for k=1:1:length(resets{j})
                    if resets{j}(k) == ':'
                        leftend = k;
                        break;
                    else
                        start = [start, resets{j}(k)];
                    end;
                end;
            
                % search matching variable
                for k=1:1:variables
                    if strcmp(start,states{1}.Var{k})
                        rowlist(j) = k;
                        break;
                    end;
                end;
            
                if rowlist(j) == 0
                    error('Syntax error in transition label ! Unknown Variable name.');
                end;
            
                % cut irrelevant part
                resets{j} = resets{j}(leftend+2:end);  
                       
            end;
            
            % read reset equation
            temp = cell(1);
            temp.A = [];
            temp.B = [];
            temp.b = [];
            temp.Equ = resets;
            temp = parseStates({temp},expr,edges,variables,Input,vars);      
            
            % switch rows to match variable order (vars)
            for j=1:1:length(rowlist)
                output_transitions{i}.ResetMatrix(rowlist(j),:) = temp{1}.A(j,:);
                output_transitions{i}.W(rowlist(j),:) = temp{1}.b(j,:);
            end;
        
        else
            output_transitions{i}.ResetMatrix = eye(variables);
            output_transitions{i}.W = zeros(variables,1);
        end;
   
    end; 
end;

qa = qa -minid;
qq = qq -minid;

for i=1:length(output_transitions)
    output_transitions{i}.from = qq(1,i);
    output_transitions{i}.to = qq(2,i);
    
    output_transitions{i}.eguards_dir = output_transitions{i}.eguards_dir';
    output_transitions{i}.iguards_dir = output_transitions{i}.iguards_dir';
end;

fprintf('Stateflow model loaded.\n');

% Annahmen: 
% - nur 1 Startzustand modelliert
% - Syntax von Flow ist korrekt (insbesondere Variablennamen sind
% eindeutig und stimmen überein)

% Spezifikation nicht möglich: I,U,N,r
% U muss so festgelegt werden, dass anordnung der Dimensionen mit denen des
% generierten B übereinstimmt ! I auch (bezüglich A) !

function [states] = parseStates(states, expr,edges,variables,Input,vars)
for i=1:1:length(states)    % for each state
    
    states{i}.A = zeros(variables,variables);
    states{i}.b = zeros(variables,1);
    states{i}.B = zeros(variables,length(Input));
        
    for j =1:1:length(states{i}.Equ) % for each flow equation

        [memory] = executeAutomaton(states{i}.Equ{j},edges,expr);
        
        % continue with analysis of memory        
        variable = 0;
        for index=1:length(memory)
            k = length(memory)-(index-1);
      
            if(strcmp(memory{k}.type,'var'))
                  
                variable = 0;
                for l=1:length(vars)
                    if strcmp(vars{l},memory{k}.value)
                        variable = l;
                    end;
                end;
                
                if variable == 0
                    for l=1:length(Input)
                        if strcmp(Input{l},memory{k}.value)
                            variable = -l;
                        end;
                    end;
                end;
                
                if variable == 0
                    error('Internal parsing error: memory contains invalid variable');
                end;
                
                if k == 1   % there will be no constant to the left
                    memory{k}.type='const';
                    memory{k}.value = 1;
                end;
            end;
 
            if (strcmp(memory{k}.type,'const'))
                if variable == 0 %there is no dimension to insert the constant
                    states{i}.b(j,1) = states{i}.b(j,1) + memory{k}.value;
                    variable = 0;
                elseif variable >0
                    % insert constant to correspondent dimension in A
                    states{i}.A(j,variable) = states{i}.A(j,variable) + memory{k}.value;
                    variable = 0;
                else
                    % insert constant to correspondent dimension in B
                    states{i}.B(j,-variable) = states{i}.B(j,-variable) + memory{k}.value;
                    variable = 0;
                end;
            end; 
        end;   
    end; 
end;
