%% THE OPENMAS CYCLE PROCESSOR (OMAS_process.m) %%%%%%%%%%%%%%%%%%%%%%%%%%%
% This function computes the progressive cycles of the simulation, for the
% environment, obstacles, agents and objects withing the simulation space.

% Author: James A. Douthwaite 06/10/2016

% //////////////////////// MAIN WRAPPER SCRIPT ////////////////////////////
function [DATA,SIM,EVENTS,objectIndex]  = OMAS_process(META,objectIndex)
% INPUTS:
% objectIndex - The cell array of object classes
% OUTPUTS:
% DATA        - The output DATA structure with timeseries data
% SIM         - The terminal META structure copy

%% PRE-SIMULATION DECLARATIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
META.phase = 'META'; % Declare the simuation active phase
EVENTS = [];         % Reset the EVENT log container

% PREPARE THE OUTPUT DATA CONTAINER
[DATA] = GetOutputStructure(META);

%%%%%%%%%%%%%%%%%%%%%% BEGIN TIME-STEP INTERATIONS %%%%%%%%%%%%%%%%%%%%%%%%
fprintf('[%s]\n[%s]\tLAUNCHING SIMULATION...\n[%s]\n',META.phase,META.phase,META.phase);
% EXECUTE OMAS MAIN CYCLE
[META,objectIndex,DATA,EVENTS] = ProcessGlobalTimeSeries(META,objectIndex,DATA,EVENTS);
% DATA.computationTime = toc; % Get total elapsed simulation time
% fprintf('\n[%s]\tSIMULATION COMPLETE (time elapsed: %ss)\n',META.phase,num2str(DATA.computationTime));
fprintf('[%s]\n[%s]\t...SIMULATION COMPLETE\n[%s]\n',META.phase,META.phase,META.phase);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% FINALISE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
META.phase = 'OUTPUT';                                   % Change to OUTPUT phase
% clearvars -except META EVENTS DATA objectIndex         % Clear loose simulation variables
fprintf('[%s]\tDumping initial simulation variables to output directory\n[%s]\n',META.phase,META.phase);
% sendOutputToFiles(META,EVENTS,DATA,objectIndex);
fprintf('[%s]\tClosing simulation.\n',META.phase);
SIM = META;
end

% //////////////////////// MAIN CYCLE FUNCTIONS ///////////////////////////
% PROCESS TIME VECTOR
function [META,objectIndex,DATA,EVENTS] = ProcessGlobalTimeSeries(META,objectIndex,DATA,EVENTS)
% This function computes the simulation loop cycle across the given
% descrete META.timevector:
% INPUTS:
% META - The initialised META data-structure
% objectIndex - The cell vector of objects (and/or agents)
% DATA        - The initialised output DATA structure
% EVENTS      - The initialised EVENTS structure list

% PROCESS TIME CYCLE
step = META.TIME.currentStep;
exitFlag = 0;
while step <= META.TIME.numSteps

    % 0. /////////////////// CHECK AGENT IDLE STATUS //////////////////////
    % If there are agents in the simulation, then break if all are idle,
    % else allow run to full duration
    idleCondition = sum([META.OBJECTS([META.OBJECTS.type] == OMAS_objectType.agent).idleStatus]) == META.totalAgents && META.totalAgents > 0;
    if idleCondition == 1 && ~exitFlag
        exitFlag = 1;
        referenceTime = META.TIME.currentTime;
        fprintf('[%s]\t... All agents are idle, aborting in t=%0.2fs.\n',META.phase,META.TIME.idleTimeOut);
    elseif exitFlag == 1
        timeOut = META.TIME.idleTimeOut - (META.TIME.currentTime - referenceTime);
        if timeOut <= 0
           fprintf('[%s]\t... Aborting.\n',META.phase);
           break
        end
        fprintf('[%s]\t... All agents are idle, aborting in t=%0.2fs.\n',META.phase,timeOut);
    end
    % /////////////////////////////////////////////////////////////////////

    % 1. ////////////// UPDATE TIMESTEP PARAMETERS (@t=k) /////////////////
    META.TIME.currentTime = META.TIME.timeVector(step);                    % The current sim-time
    META.TIME.currentStep = step;                                          % The current sim-step
    if META.verbosity >= 1
        fprintf('[%s]\tStep: %.0f\tTime: %.2fs\n',META.phase,META.TIME.currentStep,META.TIME.currentTime);
    end
    % /////////////////////////////////////////////////////////////////////

    % 2. ///////////////// RECORD GLOBAL STATE (@t=k) /////////////////////
    for ID1 = 1:META.totalObjects
        % Collect the META.OBJECTS.state data (as the simulations understanding of the global states)
        % and save to the output DATA.globalTrajectories set, must be done synchronously).
        DATA.globalTrajectories(DATA.stateIndex(1,ID1):DATA.stateIndex(2,ID1),META.TIME.currentStep) = META.OBJECTS(ID1).globalState;
    end
    % /////////////////////////////////////////////////////////////////////

    % 3. ////////////// UPDATE THE GLOBAL STATES (@t=k) ////////////////////
    for ID1 = 1:META.totalObjects                                          % For each META object element
        % Update META.OBJECT the structure
%         META.OBJECTS(ID1) = OMAS_updateGlobalStates_mex(...
%             META,...
%             objectIndex{ID1}.objectID,...
%             objectIndex{ID1}.GetGLOBAL('velocity'),...
%             objectIndex{ID1}.GetGLOBAL('quaternion'),...
%             objectIndex{ID1}.GetGLOBAL('idleStatus'));
        META.OBJECTS(ID1) = OMAS_updateGlobalStates(...
            META,...
            objectIndex{ID1}.objectID,...
            objectIndex{ID1}.GetGLOBAL('velocity'),...
            objectIndex{ID1}.GetGLOBAL('quaternion'),...
            objectIndex{ID1}.GetGLOBAL('idleStatus'));
    end
    % /////////////////////////////////////////////////////////////////////

%     % 4. ////////// UPDATE THE VISUAL REPRESENTATIONS (@t=k) //////////////
%     for ID1 = 1:META.totalObjects
%         if ~isstruct(META.OBJECTS(ID1).geometry)
%             continue
%         end
%         % MODIFY THE ASSOCIATED GEOMETRY
%         META.OBJECTS(ID1) = updateVisuals(META.OBJECTS(ID1));
%     end

    % 5. //////// UPDATE SIMULATION/ENVIRONMENTAL META DATA (@t=k) ////////
    [META,metaEVENTS] = UpdateSimulationMeta(META,objectIndex);            % Update META snapshot with equivilant objectIndex.state
    % LOG THE META EVENTS
    if ~isempty(metaEVENTS)                                                % If META events occurred this timestep
        EVENTS = vertcat(EVENTS,metaEVENTS);                               % Append to global EVENTS
    end
    % /////////////////////////////////////////////////////////////////////

    % 6. //// COMPUTE AGENT CYCLES (UPDATE GLOBAL REPRESENTATION (@t=k) ///
    if META.threadPool ~= 0
        objectSnapshot = objectIndex;                                      % Make a temporary record of the object set
        parfor (ID1 = 1:META.totalObjects)
            % MOVE THROUGH OBJECT INDEX AND UPDATE EACH AGENT
            [objectIndex{ID1},objectEVENTS] = UpdateObjects(META,objectSnapshot,objectIndex{ID1});
            % LOG THE OBJECT EVENTS
            if ~isempty(objectEVENTS)
                EVENTS = vertcat(EVENTS,objectEVENTS);
            end
        end
    else
        for ID1 = 1:META.totalObjects
            % MOVE THROUGH OBJECT INDEX AND UPDATE EACH AGENT
            [objectIndex{ID1},objectEVENTS] = UpdateObjects(META,objectIndex,objectIndex{ID1}); % Update objectIndex snapshot with new META data
            % LOG THE OBJECT EVENTS
            if ~isempty(objectEVENTS)                                      % If objectEVENTS occur in this timestep
                EVENTS = vertcat(EVENTS,objectEVENTS);                     % Append to global EVENTS
            end
        end
    end
    % /////////////////////////////////////////////////////////////////////

    % 7. /// THE 'OBJECT.VIRTUAL' PROPERTIES IS NOW UPDATED FOR (t=k+1) ///
    step = step + 1;
end
% CREATE TERMINAL VALUES, FOR CLARITY
META.TIME.endStep = META.TIME.currentStep;
META.TIME.endTime = META.TIME.currentTime;
end
% UPDATE THE OBJECT META PROPERTIES
function [SIM,metaEVENTS]	= UpdateSimulationMeta(SIM,objectIndex)
% In this function we wish to update the global separations and event
% conditions based on the new 'globalState' properties.

% INPUTS:
% SIM         - A local copy of the global META structure
% objectIndex - The cell array of object classes.
% OUTPUTS:
% SIM         - The updated META structure
% metaEVENTS  - A vector of new event objects.

% We want to move through the object set an update both the object being
% updated, but also the object it is being updated against.

% //// ASSESS THE COLLISIONS AND RELATIVE POSITIONS (SYMMETRICAL CHECK) ///
collisionLogicals = zeros(SIM.totalObjects);
warningLogicals   = zeros(SIM.totalObjects);
for entityA = 1:SIM.totalObjects                                           % Object A's position in the META.OBJECTS

    % NOTE:
    % - We only need to consider the objects whos separations have yet to be
    %   evaluated. If we update 1&2 and 2&1 simultaneously, then we must only
    %   examine the unique ID permutations to assess their conditions.

    for entityB = (entityA+1):(SIM.totalObjects)                           % Object B's position in the META.OBJECTS
        % THE OBJECTS ASSOCIATED objectIndex
        object_A = objectIndex{SIM.globalIDvector == SIM.OBJECTS(entityA).objectID};
        object_B = objectIndex{SIM.globalIDvector == SIM.OBJECTS(entityB).objectID};  % Determine the associated geometries

        % UPDATE RELATIVE POSITIONS  (centroid separation based)
        SIM.OBJECTS(entityA).relativePositions(entityB,:) = (SIM.OBJECTS(entityB).globalState(1:3,1) - SIM.OBJECTS(entityA).globalState(1:3,1))';   % AB vector
        SIM.OBJECTS(entityB).relativePositions(entityA,:) = (SIM.OBJECTS(entityA).globalState(1:3,1) - SIM.OBJECTS(entityB).globalState(1:3,1))';   % BA vector

        % GET THE WARNING CONDITIONS (centroid separation based)
        separationDistance = norm(SIM.OBJECTS(entityA).relativePositions(entityB,:)) - (SIM.OBJECTS(entityA).radius + SIM.OBJECTS(entityB).radius);
        warningCondition = SIM.warningDistance >= separationDistance;
        warningLogicals(entityA,entityB) = warningCondition;
        warningLogicals(entityB,entityA) = warningCondition;               % Assign the warning condition

        % EVALUATE COLLISIONS BETWEEN THE OBJECTS
        collisionCondition = OMAS_collisionDetection(...
            SIM.OBJECTS(entityA),object_A.GEOMETRY,...
            SIM.OBJECTS(entityB),object_B.GEOMETRY,...
            SIM.conditionTolerance);
        collisionLogicals(entityA,entityB) = collisionCondition;
        collisionLogicals(entityB,entityA) = collisionCondition;           % Assign the collision condition

        % ERROR CHECKING (Should never happen)
        if separationDistance > 0 && collisionCondition
            error('[ERROR] A collision occurred at distance %f without violating the minimum separation of %f',...
                    norm(SIM.OBJECTS(entityA).relativePositions(entityB,:)),(SIM.OBJECTS(entityA).radius + SIM.OBJECTS(entityB).radius));
        end
    end
end

% //////////// ASSESS DETECTION CONDITIONS (ASYMMETRICAL CHECK) ///////////
% DEFAULT DETECTION CONDITION
detectionLogicals = zeros(SIM.totalObjects);
for entityA = 1:SIM.totalObjects
    % Check objects have the capacity to observe other objects
    if SIM.OBJECTS(entityA).type ~= OMAS_objectType.agent
        continue
    end

    % Check all agent observations
    for entityB = 1:SIM.totalObjects                                       % Object B's position in the META.OBJECTS
        % SKIP SELF-REFERENCE
        if SIM.OBJECTS(entityA).objectID == SIM.OBJECTS(entityB).objectID
            continue                                                       % Only agents can generate notifications
        end
        % NOTE:
        % - We also need to determine which agents are able to observe other
        %   agents in the field. However, this is NOT A SYMMETRIC operation
        %   as A may be able to detect B without B detecting A.

        % Detection check #1 - Authorisation
        isWaypoint = SIM.OBJECTS(entityB).type == OMAS_objectType.waypoint;
        if isWaypoint
            isAuthorised = objectIndex{SIM.globalIDvector == SIM.OBJECTS(entityB).objectID}.IDAssociationCheck(SIM.OBJECTS(entityA).objectID);
            if ~isAuthorised
                continue
            end
        end

        % Detection check #2 - Spherical
        isDetected = OMAS_geometry.intersect_spheres(...
            SIM.OBJECTS(entityA).globalState(1:3),SIM.OBJECTS(entityA).detectionRadius + 0.5*SIM.conditionTolerance,...
            SIM.OBJECTS(entityB).globalState(1:3),SIM.OBJECTS(entityB).radius + 0.5*SIM.conditionTolerance);

        % Object is not detected
        if ~isDetected
            continue
        end

        % This check is sufficient for way-points
        if isWaypoint
            detectionLogicals(entityA,entityB) = 1;
            continue
        end

        % ///////////////// OBJECT MAY BE OBSERVED ////////////////////////
        % NOTE:
        % - If the distance(less the radius) is less than detection radius
        %   then its worth checking for more a more complex representation.
        % - We want to check if any vertices/edges can be observed. If any
        %   edges are visible, then they are to be sent to the first object.

        % Get the geometry of the second object (GEOMETRY OF B)
        geometryB = objectIndex{SIM.globalIDvector == SIM.OBJECTS(entityB).objectID}.GEOMETRY;

        % Check if a better check is necessary
        hasGeometry = isstruct(geometryB) && size(geometryB.vertices,1) > 0;
        if isDetected && ~hasGeometry
            detectionLogicals(entityA,entityB) = 1;                        % Detection occurred
            break
        end
        isDetected = 0; % Reset, potential false positive when checking against polygon

        % Assess the polygon
        relativeR = SIM.OBJECTS(entityA).R'*SIM.OBJECTS(entityB).R;        % Rotate second geometry orientated relative to
        % Design the vertex set
        geometryB.vertices = geometryB.vertices*relativeR + SIM.OBJECTS(entityB).globalState(1:3)';

        % Assess each face and vertex for detection
        for face = 1:size(geometryB.vertices,1)
            % GET THE MEMBER IDs OF THE FACE
            faceMembers  = geometryB.faces(face,:);
            faceVertices = geometryB.vertices(faceMembers,:);

            % CHECK THE GEOMETRY AGAINST THE SPHERICAL CONSTRAINT (with mex)
%             isDetected = CheckSphereTriangleIntersection_mex(...
%                 SIM.OBJECTS(entityA).globalState(1:3),...
%                 SIM.OBJECTS(entityA).detectionRadius,...
%                 faceVertices(1,:)',faceVertices(2,:)',faceVertices(3,:)');
            % CHECK THE GEOMETRY AGAINST THE SPHERICAL CONSTRAINT
            isDetected = CheckSphereTriangleIntersection(...
                SIM.OBJECTS(entityA).globalState(1:3),...
                SIM.OBJECTS(entityA).detectionRadius,...
                faceVertices(1,:)',faceVertices(2,:)',faceVertices(3,:)');

            % IF THE FACE IS DETECTED
            if isDetected
                detectionLogicals(entityA,entityB) = 1;
                break
            end
        end

        % NOTE:
        % - If any faces are within the constraint, then the detection
        %   logical is set true and then exited.
        % - Otherwise, if the none of the contraints are met, then it
        %   remains undetected.
    end
end

% NOTES:
% - The fields geometry based fields of SIM.OBJECTS are updated to this
%   time-step. The event conditions can now be re-evaluted.
% - CHECKS MUST BE ASYMMETRICAL - A may detect B without B detecting A

metaEVENTS = [];                                                           % Reset meta Events container
for entityA = 1:SIM.totalObjects
    % CHECK OBJECTS HAVE THE CAPACITY TO OBSERVE OTHER OBJECTS
    if SIM.OBJECTS(entityA).type ~= OMAS_objectType.agent
        continue
    end

    for entityB = 1:SIM.totalObjects
        % OMIT SELF-CHECK
        if SIM.OBJECTS(entityA).objectID == SIM.OBJECTS(entityB).objectID
            continue                                                       % Only agents can generate notifications
        end

        % //////////// ASSESS EVENT CONDITIONS FOR THE AGENTS /////////////
        % NOTES:
        % - The agent set is only capable of generating events associated
        %   with the detections, warnings and collisions.

        % ///////////////// ASSESS DETECTIONS CONDITIONS //////////////////
        % DETECTION EVENT LOGIC
        ABdetectionConstraint       =  detectionLogicals(entityA,entityB);
        novelDetectionConstraint    =  SIM.OBJECTS(entityA).objectStatus(entityB,eventType.detection) == 0;
        detectionEventCondition     =  ABdetectionConstraint &&  novelDetectionConstraint;   % Is within a range and a detection event has not been issued.
        detectionEventNullCondition = ~ABdetectionConstraint && ~novelDetectionConstraint;   % Is outside a range and a detection condition is still toggled.
        % EVALUATE OCCURANCE
        if detectionEventCondition
            % UPDATE DETECTION STATUS
            SIM.OBJECTS(entityA).objectStatus(entityB,eventType.detection) = 1;              % Ammend status of the META object
            % GENERATE DETECTION EVENT
            [EVENT] = OMAS_eventHandler(SIM.TIME.currentTime,SIM.OBJECTS(entityA),SIM.OBJECTS(entityB),eventType.detection);
            metaEVENTS = vertcat(metaEVENTS,EVENT);                                          % Add new META EVENTS to global structure
        elseif detectionEventNullCondition
            % AMEND THE META OBJECT
            SIM.OBJECTS(entityA).objectStatus(entityB,eventType.detection) = 0;              % Ammend status of the META object
            % GENERATE THE DETECTION NULLIFICATION EVENT
            [EVENT] = OMAS_eventHandler(SIM.TIME.currentTime,SIM.OBJECTS(entityA),SIM.OBJECTS(entityB),eventType.null_detection);
            metaEVENTS = vertcat(metaEVENTS,EVENT);                                          % Add new META EVENTS to global structure
        end

        % ///////////////// ASSESS WAYPOINT CONDITIONS ////////////////////
        if SIM.OBJECTS(entityB).type == OMAS_objectType.waypoint
            % Check A is allowed to get B
            waypointObj = objectIndex{SIM.globalIDvector == SIM.OBJECTS(entityB).objectID};
            hasAssociation = waypointObj.IDAssociationCheck(SIM.OBJECTS(entityA).objectID);
            % WAYPOINT EVENT LOGIC
            ABwaypointCondition        =  collisionLogicals(entityA,entityB) && hasAssociation;
            novelWaypointGetConstraint =  SIM.OBJECTS(entityA).objectStatus(entityB,eventType.waypoint) == 0;
            waypointGetEventCondition  =  ABwaypointCondition &&  novelWaypointGetConstraint;    % Is within a range and a collision event has not been issued.
            waypointEventNullCondition = ~ABwaypointCondition && ~novelWaypointGetConstraint;    % Is outside a range and a waypoint-get condition is still toggled.
            % EVALUATE OCCURANCE
            if waypointGetEventCondition
                % AMEND THE META OBJECT
                SIM.OBJECTS(entityA).objectStatus(entityB,eventType.waypoint) = 1;
                % GENERATE THE WAYPOINT ACHIEVED EVENT
                [EVENT] = OMAS_eventHandler(SIM.TIME.currentTime,SIM.OBJECTS(entityA),SIM.OBJECTS(entityB),eventType.waypoint);
                metaEVENTS = vertcat(metaEVENTS,EVENT);                                          % Add new META EVENTS to global structure
            elseif waypointEventNullCondition
                % AMEND THE META OBJECT
                SIM.OBJECTS(entityA).objectStatus(entityB,eventType.waypoint) = 0;
                % GENERATE THE WAYPOINT NULLIFICATION EVENT
                %[EVENT] = OMAS_eventHandler(SIM.TIME.currentTime,SIM.OBJECTS(entityA),SIM.OBJECTS(entityB),eventType.null_waypoint);
                %metaEVENTS = vertcat(metaEVENTS,EVENT);                                        % Add new META EVENTS to global structure
            end
            continue    % Skip collision check
        end

        % ////////// CONFIRM THE OBJECT CAN BE COLLIDED WITH //////////////
        if SIM.OBJECTS(entityB).hitBox == OMAS_hitBoxType.none
            % Check hit-box definition:
            % -If no hitbox is assigned, no need to evaluate warnings or
            % collisions.
            continue
        end

        % /////////////////// ASSESS WARNING CONDITIONS ///////////////////
        % WARNING EVENT LOGIC
        ABwarningConstraint       =  warningLogicals(entityA,entityB);
        novelWarningConstraint    =  SIM.OBJECTS(entityA).objectStatus(entityB,eventType.warning) == 0;
        warningEventCondition     =  ABwarningConstraint &&  novelWarningConstraint;         % Is within a range and a warning event has not been issued.
        warningEventNullCondition = ~ABwarningConstraint && ~novelWarningConstraint;         % Is outside a range and a warning condition is still toggled.
        % EVALUATE OCCURANCE
        if warningEventCondition
            % UPDATE WARNING STATUS
            SIM.OBJECTS(entityA).objectStatus(entityB,eventType.warning) = 1;
            % GENERATE THE WARNING EVENT
            [EVENT] = OMAS_eventHandler(SIM.TIME.currentTime,SIM.OBJECTS(entityA),SIM.OBJECTS(entityB),eventType.warning);
            metaEVENTS = vertcat(metaEVENTS,EVENT);                                          % Add new META EVENTS to global structure
        elseif warningEventNullCondition
            % UPDATE WARNING-NULL STATUS
            SIM.OBJECTS(entityA).objectStatus(entityB,eventType.warning) = 0;
            % GENERATE THE WARNING NULLIFICATION EVENT
            [EVENT] = OMAS_eventHandler(SIM.TIME.currentTime,SIM.OBJECTS(entityA),SIM.OBJECTS(entityB),eventType.null_warning);
            metaEVENTS = vertcat(metaEVENTS,EVENT);                                          % Add new META EVENTS to global structure
        end

        % ////////////////// ASSESS COLLISION CONDITIONS //////////////////
        % COLLISION EVENT LOGIC
        ABcollisionConstraint       =  collisionLogicals(entityA,entityB);
        novelCollisionConstraint    =  SIM.OBJECTS(entityA).objectStatus(entityB,eventType.collision) == 0;
        collisionEventCondition     =  ABcollisionConstraint &&  novelCollisionConstraint;   % Is within a range and a collision event has not been issued.
        collisionEventNullCondition = ~ABcollisionConstraint && ~novelCollisionConstraint;   % Is outside a range and a collision condition is still toggled.
        % EVALUATE OCCURANCE
        if collisionEventCondition
            % UPDATE COLLISIONL STATUS
            SIM.OBJECTS(entityA).objectStatus(entityB,eventType.collision) = 1;
            % GENERATE THE COLLISION EVENT
            [EVENT] = OMAS_eventHandler(SIM.TIME.currentTime,SIM.OBJECTS(entityA),SIM.OBJECTS(entityB),eventType.collision);
            metaEVENTS = vertcat(metaEVENTS,EVENT);                                          % Add new META EVENTS to global structure
        elseif collisionEventNullCondition
        	% AMEND THE META OBJECT
            SIM.OBJECTS(entityA).objectStatus(entityB,eventType.collision) = 0;
            % GENERATE THE COLLISION NULLIFICATION EVENT
            [EVENT] = OMAS_eventHandler(SIM.TIME.currentTime,SIM.OBJECTS(entityA),SIM.OBJECTS(entityB),eventType.null_collision);
            metaEVENTS = vertcat(metaEVENTS,EVENT);                                          % Add new META EVENTS to global structure
        end
    end
end

end
% UPDATE THE OBJECT PROPERTIES
function [referenceObject,objectEVENTS] = UpdateObjects(SIM,objectIndex,referenceObject)
% This function updates a referenceObject class against the rest of the
% objectIndex, independantly of the SIM.OBJECTS META data.
% INPUTS:
% SIM             - Current META structure
% objectIndex     - The current object list
% referenceObject - The agent object being updated

% OUTPUTS:
% referenceObject - The updated agent class

objectEVENTS = []; % Container for object based events

% GET THE OBJECTS EQUIVALENT SIM OBJECT
SIMfirstObject = SIM.OBJECTS(SIM.globalIDvector == referenceObject.objectID);

% If the observing object is "2D"
if referenceObject.Is3D()
    dimensionIndices = 1:3;
else
    dimensionIndices = 1:2;
end

if SIM.verbosity >= 2
    fprintf('[UPDATE]\tCycling ID:%d\t name: %s\n',SIMfirstObject.objectID,referenceObject.name);

end

% Default timing parameters
ENV = SIM.TIME;
ENV.outputPath = SIM.outputPath;

if SIM.verbosity >= 2
    fprintf('[UPDATE]\tCycling ID:%d\t name: %s\n',SIMfirstObject.objectID,referenceObject.name);
end

% SWITCH BEHAVIOUR BASED ON OBJECT TYPE
switch SIMfirstObject.type
    case OMAS_objectType.agent
        % AGENT - SIMULATION/ENVIROMENTAL FEEDBACK REQUIRED %%%%%%%%%%%%%%
        % Container for agent detection packet
        observationPacket = [];

        % //// PARSE THE ENVIRONMENTAL OBJECTS OBSERVABLE TO THE AGENT ////
        for ID2 = 1:SIM.totalObjects
            % GET THE SIM OBJECT OF THE EVALUATION OBJECT
            SIMsecondObject = SIM.OBJECTS(ID2);

            % Skip condition - Get the current status of this object
            isDetected = 1 == SIMfirstObject.objectStatus(ID2,eventType.detection);
            isSameObject = SIMfirstObject.objectID == SIMsecondObject.objectID;
            if ~isDetected || isSameObject
                continue
            end

            % GET THE EQUIVALENT OBJECT TO EVALUATE AGAINST THE REFERENCE
            secondObject = objectIndex{SIM.globalIDvector == SIMsecondObject.objectID};

            % ///////////////// ELSE AGENT IS DETECTED ////////////////////
            % ////// (GENERATE A LOCALISED PROXIMITY DESCIPTION) //////////
            % The second agent is within global seperation conditions. A
            % communication packet is therefore assembled containing the
            % data on the second object. rotated into the agent-local
            % coordinate frame.

            % GET THE RELATIVE POSITIONS OF THE AGENT AND OBJECT IN THE
            % GLOBAL COORDINATE SYSTEM (x_rel = x_B - x_A)
            relativePosition = SIMfirstObject.relativePositions(ID2,:)';
            relativeVelocity = SIMsecondObject.globalState(4:6) - SIMfirstObject.globalState(4:6);
            % ROTATE THE GLOBAL STATE OF THE OBJECT INTO THE AGENT FRAME
            observedPosition = SIMfirstObject.R*relativePosition;            % Rotate the from the global into the body frame of the simReference
            observedVelocity = SIMfirstObject.R*relativeVelocity;
            % SPHERICAL REPRESENTATION
            observedRange     = norm(observedPosition);
            observedElevation = asin(observedPosition(3)/observedRange);
            observedHeading   = atan2(observedPosition(2),observedPosition(1));
            % PRESENT SIZE PROPERTIES
            observedRadius    = SIMfirstObject.radius;
            observedAngularWidth = 2*asin(observedRadius/(observedRange + observedRadius));

            % OBJECT GEOMETRY PREPARATION
            % In order for the agent to observe the object correctly. The
            % geometry must be translated and rotated into the relative
            % frame of the detecting agent.
            % ASSUMPTION:
            % - The geometry is normalised to the body axes of the object.
            % TO DO:
            % - If the object is on the edge of vision.. only part of the
            %   object will be visable to the agent. a process must be in
            %   place to create a subset of the geometry.

            % THE GEOMETRIC PARAMETERS
            if size(secondObject.GEOMETRY.vertices,1) > 0
                % Rotate evaluation object geometry into the global space,
                % then rotate it back into the local space of the reference.
                % THE RELATIVE ROTATIONS OF THE SECOND BODY
                relativeR = SIMfirstObject.R'*SIMsecondObject.R;
                % THE CONTAINER FOR THE RELATIVE GEOMETRY
                observedGeometry = struct('vertices',secondObject.GEOMETRY.vertices*relativeR + observedPosition',...
                                          'normals', secondObject.GEOMETRY.normals*relativeR,...
                                          'faces',   secondObject.GEOMETRY.faces,...
                                          'centroid',secondObject.GEOMETRY.centroid + observedPosition');
                % [TO-DO] Reduce the structure sent to match the exposed geometry

%                 % PROCESS THE VISIBLE GEOMETRY (The sub-set of the geometry that is visible to the agent.)
%                 observedGeometry = OMAS_restrictedGeometry(zeros(3,1),SIMfirstObject.detectionRadius,relativeGeometry);
            else
                % THE OBSERVED GEOMETRY IS EMPTY
                observedGeometry = secondObject.GEOMETRY;                  % Pass the empty structure
            end

            % OBJECT PRIORITY (IF WAYPOINT)
            observedPriority = NaN;
            if SIMsecondObject.type == OMAS_objectType.waypoint
                association = objectIndex{ID2}.GetAgentAssociation(referenceObject);
                observedPriority = association.priority;
            end

            % A DEBUG STUCTURE
            DEBUG = struct('globalPosition',SIMsecondObject.globalState(dimensionIndices),...     % Added for simplicity
                           'globalVelocity',SIMsecondObject.globalState(dimensionIndices + 3),... % Added for simplicity
                           'priority',observedPriority);                                          % The objects global priority for that agent

            % ///////////////// PREPARE DETECTION PACKET //////////////////
            detectionObject = struct('objectID',SIMsecondObject.objectID,...            % The object ID (observed)
                                     'name',SIMsecondObject.name,...                    % The object name tag (observed)
                                     'type',SIMsecondObject.type,...                    % The objects sim-type enum
                                     'radius',observedRadius,...                        % The objects true size
                                     'position',observedPosition(dimensionIndices,1),...% The apparent position in the relative frame
                                     'velocity',observedVelocity(dimensionIndices,1),...% The apparent velocity in the relative frame
                                     'range',observedRange,...                          % The apparent range
                                     'elevation',observedElevation,...                  % The apparent inclination angle
                                     'heading',observedHeading,...                      % The apparent Azimuth angle
                                     'width',observedAngularWidth,...                   % The apparent angular width at that range
                                     'geometry',observedGeometry,...                    % The observable geometrical components
                                     'colour',SIMsecondObject.colour,...                % Finally the simulation's colourID
                                     'DEBUG',DEBUG);                                    % Pass additional parameters (not otherwise known)
            observationPacket = vertcat(observationPacket,detectionObject);             % Append object to packet to agent
        end

        % /////////////// COMPUTE THE LOCAL AGENT CYCLE ///////////////////
        % Given the detection object defined for this agent, compute the
        % agents cycle with this information.
        try
            % SEND OBJECT OBSERVERATION PACKET TO AGENT
            referenceObject = referenceObject.main(ENV,observationPacket);                      % Hand the current sim TIME object and complete observation packet
        catch objectProcessError
            warning('[%s]: Cycle failed, there was a problem with the object (%s) file, please check: %s',...
                     SIM.phase,referenceObject.name,objectProcessError.stack(1).name);
            rethrow(objectProcessError);
        end
    case OMAS_objectType.obstacle
        % PASSIVE - NO FEEDBACK REQUIRED %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        referenceObject = referenceObject.main(ENV);
    case OMAS_objectType.waypoint
        % WAYPOINTS ARE CONSIDERED PASSIVE - NO FEEDBACK REQUIRED %%%%%%%%%
        referenceObject = referenceObject.main(ENV);
    otherwise
        % CONSIDERED PASSIVE - NO FEEDBACK REQUIRED %%%%%%%%%%%%%%%%%%%%%%%
        % DO NOT UPDATE
end
end

% /////////////////// SIMULATION OUTPUT OPERATIONS ////////////////////////
% GET INITIAL OUTPUT DATA STRUCTURE
function [DATA] = GetOutputStructure(SIM)
% This function assembles the initial output DATA structure from the
% simulation input variables and it back as a simulation output container.
% INPUT:
% SIM  - A local reference to the META structure
% OUPUT:
% DATA - The output data structure

% CALCULATE THE INITIAL DATA PARAMETERS
globalStateNum = size(SIM.OBJECTS(1).globalState,1);
systemStates = globalStateNum*SIM.totalObjects;                            % The total number of system states
% DEFINE THE GLOBAL STATE INDICES
indexSet = zeros(2,length(SIM.OBJECTS));                                   % Indices are defined [start;end]*n
indexSet(1,:) = (0:globalStateNum:systemStates-globalStateNum) + 1;        % Declare the state indices
indexSet(2,:) = globalStateNum:globalStateNum:systemStates;

% GENERATE THE OUTPUT STRUCTURE FROM THE SIMULATION INPUT PARAMETERS
DATA = struct('outputPath',[SIM.outputPath,'DATA.mat'],...
               'outputDir',SIM.outputPath,...
            'totalObjects',SIM.totalObjects,...
             'totalAgents',SIM.totalAgents,...
          'totalObstacles',SIM.totalObstacles,...
          'totalWaypoints',SIM.totalWaypoints,...
              'timeVector',SIM.TIME.timeVector,...
                      'dt',SIM.TIME.dt,...
              'stateIndex',indexSet,...
      'globalTrajectories',NaN(systemStates,SIM.TIME.numSteps));         % Prepare the output container
%       'globalTrajectories',zeros(systemStates,SIM.TIME.numSteps));         % Prepare the output container
end


function add_io(hRD,design)

% add AXI4 and AXI4-Lite slave interfaces
hRD.addAXI4SlaveInterface( ...
    'InterfaceConnection', 'axi_cpu_interconnect/M05_AXI', ...
    'BaseAddress',         '0x43C00000', ...
    'MasterAddressSpace',  'sys_ps7/Data');

if ~strcmp(design,'Tx')
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % Rx Reference design interfaces
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    hRD.addInternalIOInterface( ...
        'InterfaceID',    'IP Data Valid OUT', ...
        'InterfaceType',  'OUT', ...
        'PortName',       'dut_data_valid', ...
        'PortWidth',      1, ...
        'InterfaceConnection', 'util_ad9361_adc_pack/adc_valid_0', ...
        'IsRequired',     false);

    hRD.addInternalIOInterface( ...
        'InterfaceID',    'IP Data 0 OUT', ...
        'InterfaceType',  'OUT', ...
        'PortName',       'dut_data_0', ...
        'PortWidth',      16, ...
        'InterfaceConnection', 'util_ad9361_adc_pack/adc_data_0', ...
        'IsRequired',     false);

    hRD.addInternalIOInterface( ...
        'InterfaceID',    'IP Data 1 OUT', ...
        'InterfaceType',  'OUT', ...
        'PortName',       'dut_data_1', ...
        'PortWidth',      16, ...
        'InterfaceConnection', 'util_ad9361_adc_pack/adc_data_1', ...
        'IsRequired',     false);

    hRD.addInternalIOInterface( ...
        'InterfaceID',    'IP Data 2 OUT', ...
        'InterfaceType',  'OUT', ...
        'PortName',       'dut_data_2', ...
        'PortWidth',      16, ...
        'InterfaceConnection', 'util_ad9361_adc_pack/adc_data_2', ...
        'IsRequired',     false);

    hRD.addInternalIOInterface( ...
        'InterfaceID',    'IP Data 3 OUT', ...
        'InterfaceType',  'OUT', ...
        'PortName',       'dut_data_3', ...
        'PortWidth',      16, ...
        'InterfaceConnection', 'util_ad9361_adc_pack/adc_data_3', ...
        'IsRequired',     false);

    hRD.addInternalIOInterface( ...
        'InterfaceID',    'AD9361 ADC Data I0', ...
        'InterfaceType',  'IN', ...
        'PortName',       'sys_wfifo_0_dma_wdata', ...
        'PortWidth',      16, ...
        'InterfaceConnection', 'util_ad9361_adc_fifo/dout_data_0', ...
        'IsRequired',     false);

    hRD.addInternalIOInterface( ...
        'InterfaceID',    'AD9361 ADC Data Q0', ...
        'InterfaceType',  'IN', ...
        'PortName',       'sys_wfifo_1_dma_wdata', ...
        'PortWidth',      16, ...
        'InterfaceConnection', 'util_ad9361_adc_fifo/dout_data_1', ...
        'IsRequired',     false);

    hRD.addInternalIOInterface( ...
        'InterfaceID',    'AD9361 ADC Data I1', ...
        'InterfaceType',  'IN', ...
        'PortName',       'sys_wfifo_2_dma_wdata', ...
        'PortWidth',      16, ...
        'InterfaceConnection', 'util_ad9361_adc_fifo/dout_data_2', ...
        'IsRequired',     false);

    hRD.addInternalIOInterface( ...
        'InterfaceID',    'AD9361 ADC Data Q1', ...
        'InterfaceType',  'IN', ...
        'PortName',       'sys_wfifo_3_dma_wdata', ...
        'PortWidth',      16, ...
        'InterfaceConnection', 'util_ad9361_adc_fifo/dout_data_3', ...
        'IsRequired',     false);
end

if ~strcmp(design,'Rx')
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % Tx Reference design interfaces
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    hRD.addInternalIOInterface( ...
        'InterfaceID',    'AD9361 DAC Data I0', ...
        'InterfaceType',  'OUT', ...
        'PortName',       'axi_ad9361_dac_data_i0', ...
        'PortWidth',      16, ...
        'InterfaceConnection', 'axi_ad9361_dac_fifo/din_data_0', ...
        'IsRequired',     false);

    hRD.addInternalIOInterface( ...
        'InterfaceID',    'AD9361 DAC Data Q0', ...
        'InterfaceType',  'OUT', ...
        'PortName',       'axi_ad9361_dac_data_q0', ...
        'PortWidth',      16, ...
        'InterfaceConnection', 'axi_ad9361_dac_fifo/din_data_1', ...
        'IsRequired',     false);

    hRD.addInternalIOInterface( ...
        'InterfaceID',    'AD9361 DAC Data I1', ...
        'InterfaceType',  'OUT', ...
        'PortName',       'axi_ad9361_dac_data_i1', ...
        'PortWidth',      16, ...
        'InterfaceConnection', 'axi_ad9361_dac_fifo/din_data_2', ...
        'IsRequired',     false);

    hRD.addInternalIOInterface( ...
        'InterfaceID',    'AD9361 DAC Data Q1', ...
        'InterfaceType',  'OUT', ...
        'PortName',       'axi_ad9361_dac_data_q1', ...
        'PortWidth',      16, ...
        'InterfaceConnection', 'axi_ad9361_dac_fifo/din_data_3', ...
        'IsRequired',     false);

    hRD.addInternalIOInterface( ...
        'InterfaceID',    'IP Load Tx Data OUT', ...
        'InterfaceType',  'OUT', ...
        'PortName',       'util_dac_unpack_dac_valid_00', ...
        'PortWidth',      1, ...
        'InterfaceConnection', 'axi_ad9361_dac_fifo/din_valid_in_0', ...
        'IsRequired',     false);

    hRD.addInternalIOInterface( ...
        'InterfaceID',    'IP Data 0 IN', ...
        'InterfaceType',  'IN', ...
        'PortName',       'util_dac_unpack_dac_data_00', ...
        'PortWidth',      16, ...
        'InterfaceConnection', 'util_ad9361_dac_upack/dac_data_0', ...
        'IsRequired',     false);

    hRD.addInternalIOInterface( ...
        'InterfaceID',    'IP Data 1 IN', ...
        'InterfaceType',  'IN', ...
        'PortName',       'util_dac_unpack_dac_data_01', ...
        'PortWidth',      16, ...
        'InterfaceConnection', 'util_ad9361_dac_upack/dac_data_1', ...
        'IsRequired',     false);

    hRD.addInternalIOInterface( ...
        'InterfaceID',    'IP Data 2 IN', ...
        'InterfaceType',  'IN', ...
        'PortName',       'util_dac_unpack_dac_data_02', ...
        'PortWidth',      16, ...
        'InterfaceConnection', 'util_ad9361_dac_upack/dac_data_2', ...
        'IsRequired',     false);

    hRD.addInternalIOInterface( ...
        'InterfaceID',    'IP Data 3 IN', ...
        'InterfaceType',  'IN', ...
        'PortName',       'util_dac_unpack_dac_data_03', ...
        'PortWidth',      16, ...
        'InterfaceConnection', 'util_ad9361_dac_upack/dac_data_3', ...
        'IsRequired',     false);

    hRD.addInternalIOInterface( ...
        'InterfaceID',    'IP Valid Tx Data IN', ...
        'InterfaceType',  'IN', ...
        'PortName',       'util_dac_unpack_upack_valid_00', ...
        'PortWidth',      1, ...
        'InterfaceConnection', 'util_ad9361_dac_upack/dac_valid_out_0', ...
        'IsRequired',     false);

end


function f = compose(f, op, g, data, pref)
%COMPOSE   Compose with a DELTAFUN is not allowed.
%
% Copyright 2017 by The University of Oxford and The Chebfun Developers.
% See http://www.chebfun.org/ for Chebfun information.

error('CHEBFUN:DELTAFUN:compose:notSupported', ...
    'Composition with DELTAFUN is not supported.');

end


% Demonstrates how to use the property pane.
%
% See also: PropertyGrid

% Copyright 2010 Levente Hunyadi
function example_propertygrid

properties = [ ...
    PropertyGridField('double', pi, ...
        'Category', 'Primitive types', ...
        'DisplayName', 'real double', ...
        'Description', 'Standard MatLab type.') ...
    PropertyGridField('single', pi, ...
        'Category', 'Primitive types', ...
        'DisplayName', 'real single', ...
        'Description', 'Single-precision floating point number.') ...
    PropertyGridField('integer', int32(23), ...
        'Category', 'Primitive types', ...
        'DisplayName', 'int32', ...
        'Description', 'A 32-bit integer value.') ...
    PropertyGridField('interval', int32(2), ...
        'Type', PropertyType('int32', 'scalar', [0 6]), ...
        'Category', 'Primitive types', ...
        'DisplayName', 'int32', ...
        'Description', 'A 32-bit integer value with an interval domain.') ...
    PropertyGridField('enumerated', int32(-1), ...
        'Type', PropertyType('int32', 'scalar', {int32(-1), int32(0), int32(1)}), ...
        'Category', 'Primitive types', ...
        'DisplayName', 'int32', ...
        'Description', 'A 32-bit integer value with an enumerated domain.') ...
    PropertyGridField('logical', true, ...
        'Category', 'Primitive types', ...
        'DisplayName', 'logical', ...
        'Description', 'A Boolean value that takes either true or false.') ...
    PropertyGridField('doublematrix', [], ...
        'Type', PropertyType('denserealdouble', 'matrix'), ...
        'Category', 'Compound types', ...
        'DisplayName', 'real double matrix', ...
        'Description', 'Matrix of standard MatLab type with empty initial value.') ...
    PropertyGridField('string', 'a sample string', ...
        'Category', 'Compound types', ...
        'DisplayName', 'string', ...
        'Description', 'A row vector of characters.') ...
    PropertyGridField('rowcellstr', {'a sample string','spanning multiple','lines'}, ...
        'Category', 'Compound types', ...
        'DisplayName', 'cell row of strings', ...
        'Description', 'A row cell array whose every element is a string (char array).') ...
    PropertyGridField('colcellstr', {'a sample string';'spanning multiple';'lines'}, ...
        'Category', 'Compound types', ...
        'DisplayName', 'cell column of strings', ...
        'Description', 'A column cell array whose every element is a string (char array).') ...
    PropertyGridField('season', 'spring', ...
        'Type', PropertyType('char', 'row', {'spring','summer','fall','winter'}), ...
        'Category', 'Compound types', ...
        'DisplayName', 'string', ...
        'Description', 'A row vector of characters that can take any of the predefined set of values.') ...
    PropertyGridField('set', [true false true], ...
        'Type', PropertyType('logical', 'row', {'A','B','C'}), ...
        'Category', 'Compound types', ...
        'DisplayName', 'set', ...
        'Description', 'A logical vector that serves an indicator of which elements from a universe are included in the set.') ...
    PropertyGridField('root', [], ...  % [] (and no type explicitly set) indicates that value is not editable
        'Category', 'Compound types', ...
        'DisplayName', 'root node') ...
    PropertyGridField('root.parent', int32(23), ...
        'Category', 'Compound types', ...
        'DisplayName', 'parent node') ...
    PropertyGridField('root.parent.child', int32(2007), ...
        'Category', 'Compound types', ...
        'DisplayName', 'child node') ...
];

% arrange flat list into a hierarchy based on qualified names
properties = properties.GetHierarchy();

% create figure
f = figure( ...
    'MenuBar', 'none', ...
    'Name', 'Property grid demo - Copyright 2010 Levente Hunyadi', ...
    'NumberTitle', 'off', ...
    'Toolbar', 'none');

% procedural usage
g = PropertyGrid(f, ...            % add property pane to figure
    'Properties', properties, ...  % set properties explicitly
    'Position', [0 0 0.5 1]);
h = PropertyGrid(f, ...
    'Position', [0.5 0 0.5 1]);

% declarative usage, bind object to grid
obj = SampleObject;  % a value object
h.Item = obj;        % bind object, discards any previously set properties

% update the type of a property assigned with type autodiscovery
userproperties = PropertyGridField.GenerateFrom(obj);
userproperties.FindByName('IntegerMatrix').Type = PropertyType('denserealdouble', 'matrix');
disp(userproperties.FindByName('IntegerMatrix').Type);
h.Bind(obj, userproperties);

% wait for figure to close
uiwait(f);

% display all properties and their values on screen
disp('Left-hand property grid');
disp(g.GetPropertyValues());
disp('Right-hand property grid');
disp(h.GetPropertyValues());
disp('SampleObject (modified)');
disp(h.Item);
disp('SampleNestedObject (modified)');
disp(h.Item.NestedObject);


% ------------------------------------------------------------------------
%  Copyright (C)
%  Universitat Politecnica de Catalunya BarcelonaTech (UPC) - Spain
%  University of California Berkeley (UCB) - USA
%
%  Jordi Pont-Tuset <jordi.pont@upc.edu>
%  Pablo Arbelaez <arbelaez@berkeley.edu>
%  June 2014
% ------------------------------------------------------------------------
% This file is part of the MCG package presented in:
%    Arbelaez P, Pont-Tuset J, Barron J, Marques F, Malik J,
%    "Multiscale Combinatorial Grouping,"
%    Computer Vision and Pattern Recognition (CVPR) 2014.
% Please consider citing the paper if you use this code.
% ------------------------------------------------------------------------
function params = nyud_params(varargin)
  params = [];
  % Clobber with overrides passed in as arguments
  for i = 1:2:length(varargin)
    key = varargin{i};
    val = varargin{i+1};
    eval(['params.' key ' = val;']);
  end

  params = cv(params, 'gt_set_test', 'test');
  params = cv(params, 'database', 'nyud40Obj');

  % GT sets for training
  params = cv(params, 'gt_set_pareto', 'trainval');
  params = cv(params, 'gt_set_ranking', 'trainval');

  % String identifying the raw hierarchy files
  params = cv(params, 'root_cache_dir', '.');
  params = cv(params, 'raw_hiers', 'hiers');

  % String identifying the current setup (hiers and n_r_cand)
  % to use at different file names
  params = cv(params, 'hiers_id', 'multi_3sc_u_3r');  % multi and 3 scales 'up'

  % String identifying the current Pareto chosen point
  params = cv(params, 'pareto_id', '20k');

  % Maximum number of regions per candidate
  params = cv(params, 'n_r_cand', 3);

  % To identify whether we want to use depth based features or not
  params = cv(params, 'feature_id', 'no_depth');
  params = cv(params, 'depth_features', false);
  params = cv(params, 'camera_matrix', []);

  % Hierarchies to combine
  params.hiers    = {'multi','scale_2.0', 'scale_1.0', 'scale_0.5'};

  % Level of overlap to erase duplicates
  params = cv(params, 'J_th', 0.95);

  % Number of samples per image that the ranking training will get
  params = cv(params, 'n_samples', 30);

  % Max margin parameter
  params = cv(params, 'theta', 0.7);

  %% Do not edit the lines below
  % Where to find the hierarchies
  for ii=1:length(params.hiers)
      params.hier_dirs{ii} = fullfile(params.root_cache_dir, 'output', 'ucm', params.hiers{ii});
  end

  % Cache directory
  params.cacheDir = fullfile(params.root_cache_dir, 'regions', 'cache');
  exists_or_mkdir(params.cacheDir);

  % String identifying the current final global setup at MCG
  params.mcg_id = [params.raw_hiers '_' params.hiers_id '_' params.pareto_id '_' params.feature_id];

  % ----- Results files -----
  % Global Pareto file
  params.files.pareto_full = fullfile(params.root_cache_dir, 'regions', 'pareto', [params.raw_hiers '_' params.hiers_id '_nr_' num2str(params.n_r_cand) '_' params.gt_set_pareto '_pareto_full.mat']);

  % Single-scale results
  for ii=1:length(params.hiers)
      params.files.pareto_singles{ii} = fullfile(params.root_cache_dir, 'regions', 'pareto', [params.raw_hiers '_' params.hiers{ii} '_nr_' num2str(params.n_r_cand) '_' params.gt_set_pareto '_pareto_single.mat']);
  end

  % Parameters of chosen Pareto point
  params.files.output_dir = fullfile(params.root_cache_dir, 'output', 'regions', params.mcg_id);
  exists_or_mkdir(params.files.output_dir);

  params.files.pareto_point = fullfile(params.root_cache_dir, 'regions', 'pareto', [params.mcg_id '_' params.gt_set_pareto '_pareto_point.mat']);
  exists_or_mkdir(fullfile(params.root_cache_dir, 'regions', 'pareto'));

  % File where full features will be stored
  params.files.features_file = fullfile(params.root_cache_dir, 'regions', 'features', [params.mcg_id '_features_' params.gt_set_ranking  '.mat']);
  exists_or_mkdir(fullfile(params.root_cache_dir, 'regions', 'features'));

  % File where the trained classifier will be stored
  params.files.trained_classifier = fullfile(params.root_cache_dir, 'regions', 'classifiers',[params.mcg_id '_rand_forest_' params.gt_set_ranking  '.mat']);
  exists_or_mkdir(fullfile(params.root_cache_dir, 'regions', 'classifiers'));
end

% -------------------------------------------------------------------
% Does nothing if conf.key exists, otherwise sets conf.key to val
function conf = cv(conf, key, val)
  try
    eval(['conf.' key ';']);
  catch
    eval(['conf.' key ' = val;']);
  end
end


function markdown2matlab(infile,outfile,varargin)

% MARKDOWN2MATLAB converts a Markdown file to a MATLAB script or function. All text
% is converted to comments, headings are converted to comment lines starting with %%
% sections with code are properly formatted, and words that appear in bold, italic or
% monospace are converted.
%
% Use as
%   markdown2matlab(infile, outfile)
%
% If no outfile is specified, it will write it to a .m file with the same name as
% the infile. In case the file exists, it will be written with a numeric suffix.
%
% The best is to provide the full filepath, otherwise it will look for the file within
% the current path.
%
% Optional input arguments can be specified as key-value pairs and can include
%   ...
%
% See also MATLAB2MARKDOWN

% Copyright (C) 2018 Sophie Arana and Robert Oostenveld
%
% This file is part of FieldTrip.
%
% megconnectome is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% megconnectome is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with megconnectome.  If not, see <http://www.gnu.org/licenses/>.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% The following links are useful during development
% https://regexr.com
% https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if nargin>2 && ismember(outfile, {})
  varargin = [outfile varargin];
  outfile = [];
end

% parse the optional input arguments
% val = ft_getopt(varargin, 'key', default);

% check input
[inpath, inname, inext] = fileparts(infile);
if isempty(inpath), [inpath, inname, inext] = fileparts(which([inname inext])); end
if ~strcmp(inext,'.md')
  error('please specify a Markdown file')
end

if nargin < 2 || isempty(outfile)
  outfile = infile;
end

% check output
[outpath, outname,outext] = fileparts(outfile);
if isempty(outpath), outpath = inpath; end
if ~strcmp(outext,'.m')
  outext = '.m';
end

% add a suffix to avoid overwriting files
suffix = 1;
newname = outname;
while exist(fullfile(outpath,[newname,outext]),'file') == 2
  newname = [outname sprintf('-%0d',suffix)];
  suffix = suffix+1;
end
outname = newname;

infile = fullfile(inpath,[inname,inext]);
outfile = fullfile(outpath,[outname,outext]);

% read & convert file line by line
infid = fopen(infile,'r');
outfid = fopen(outfile,'w');

state = 'comment';    % keeps track of multi-line formatting decisions
linenumber = 0;       % keep track of the line number

while ~feof(infid)

  % reset the state, unless explicitly specified
  reset_state = true;

  line = fgetl(infid);
  linenumber = linenumber + 1;

  if ~ischar(line), break, end

  % replace each tab by two spaces
  line = strrep(line, sprintf('\t'), '  ');

  if match(line, '^---$') && linenumber==1
    state = 'jekyllheader';
    reset_state = false;

  elseif strcmp(state, 'jekyllheader') && ~match(line, '^---$')
    % do not output the header
    reset_state = false;

  elseif strcmp(state, 'jekyllheader') && match(line, '^---$')
    % do not output the end of the header

  elseif strcmp(state, 'codeblock') && match(line, '^```$')
    % do not output the end of the code block
    reset_state = true;

  elseif match(line, '^```')
    state = 'codeblock';
    reset_state = false;

  elseif strcmp(state, 'codeblock') && ~match(line, '^```')
    % output the code as-is
    fprintf(outfid, '%s\n', line);
    reset_state = false;

  elseif match(line, '{%[ -~]*%}')
    % jekyll code should not be converted into the MATLAB script

  elseif match(line, '!\[[ -~]*\]([ -~]*)')
    % inline images should not be converted into the MATLAB script

  elseif match(line, '^ *[-+*] ')
    % unordered list
    [dum,endIndex] = regexp(line, '^ *[-+*] ');
    [dum,level] = regexp(line, '^ *');
    if isempty(level)
      level = 0;
    end
    level = repmat(' ', 1, level);
    remainder = reformat(line((endIndex+1):end));
    fprintf(outfid, '%%%s* %s\n', level, remainder);

  elseif match(line, '^    ')
    % normal code
    [dum,endIndex] = regexp(line, '^    ');
    remainder = line((endIndex+1):end);
    fprintf(outfid, '%s\n', remainder);
    state = 'code';
    % assume that the next line will be in the same format
    reset_state = false;

  elseif match(line, '^[A-Za-z]')
    % normal text
    remainder = reformat(line);
    fprintf(outfid, '%% %s\n', remainder);

  elseif match(line, '^ *[0-9]*\.')
    % ordered list
    [dum,endIndex] = regexp(line, '^ *[0-9]*\. ');
    remainder = reformat(line((endIndex+1):end));
    fprintf(outfid, '%% # %s\n', remainder);

  elseif match(line, '^#')
    % heading
    [dum,endIndex] = regexp(line, '^# *');
    remainder = reformat(line((endIndex+1):end));
    fprintf(outfid, '%%%% %s\n', remainder);

  elseif isempty(line)
    % keep the complete line as it is
    if strcmp(state, 'comment')
      fprintf(outfid, '%%\n');
    else
      fprintf(outfid, '\n');
    end

  end % if the line matches some pattern

  if reset_state
    state = 'comment';
  end

end % while not feof

fclose(infid);
fclose(outfid);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function status = match(str, pattern)
status = ~isempty(regexp(str, pattern, 'once'));

function str = reformat(str)
str = formatbold(str);
str = formatitalic(str);
str = formatunderline(str);
str = formatmonospace(str);

function str = formatbold(str)
[startIndex,endIndex] = regexp(str, '\*\*[a-zA-Z0-9]*\*\*');
for i=1:length(startIndex)
  part1 = str(1:startIndex(i)-1);
  part2 = str(startIndex(i):endIndex(i));
  part3 = str((endIndex(i)+1):end);
  str = [part1 '*' part2(3:end-2) '*' part3];
  % correct the subsequent indices
  startIndex(i+1:end) = startIndex(i+1:end)-2;
  endIndex(i+1:end) = endIndex(i+1:end)-2;
end

function str = formatitalic(str)
% the format is actually the same
[startIndex,endIndex] = regexp(str, '_[a-zA-Z0-9]*_');
for i=1:length(startIndex)
  part1 = str(1:startIndex(i)-1);
  part2 = str(startIndex(i):endIndex(i));
  part3 = str((endIndex(i)+1):end);
  str = [part1 '_' part2(2:end-1) '_' part3];
end

function str = formatunderline(str)
% this is not supported in the conversion from MATLAB live scripts to normal MATLAB code.

function str = formatmonospace(str)
[startIndex,endIndex] = regexp(str, '`[a-zA-Z0-9]*`');
for i=1:length(startIndex)
  part1 = str(1:startIndex(i)-1);
  part2 = str(startIndex(i):endIndex(i));
  part3 = str((endIndex(i)+1):end);
  str = [part1 '|' part2(2:end-1) '|' part3];
  % correct the subsequent indices
  startIndex(i+1:end) = startIndex(i+1:end);
  endIndex(i+1:end) = endIndex(i+1:end);
end


function res = type(this, value)
% Method for and getting/setting EEG file type
% FORMAT res = type(this, value)
% _______________________________________________________________________
% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging

% Vladimir Litvak
% $Id: type.m 1267 2008-03-28 12:12:14Z vladimir $

if nargin == 1
    res = this.type;
else
    switch value
        case 'continuous'
            if ntrials(this)>1
                error('Continuous file can only have one trial');
            end
        case 'single'
        case 'evoked' % Add additional checks here
        case 'grandmean'
        otherwise
            error('Unrecognized type');
    end

    this.type = value;
    res = this;
end


% Copy this template configuration file to your VOT workspace.
% Enter the full path to the ECO repository root folder.

ECO_repo_path = 'E:/ForwardBackward/Tracker/FBT_ECO';

tracker_label = 'ECO';
tracker_command = generate_matlab_command('benchmark_tracker_wrapper(''ECO'', ''eco_vot_deep'', true)', {[ECO_repo_path '/VOT_integration/benchmark_wrapper']});
tracker_interpreter = 'matlab';


function transpose(f) %#ok<*INUSD>
%TRANSPOSE   CLASSICFUN objects are not transposable.

% Copyright 2017 by The University of Oxford and The Chebfun Developers.
% See http://www.chebfun.org/ for Chebfun information.

error('CHEBFUN:CLASSICFUN:transpose:notpossible', ...
    'CLASSICFUN objects are not transposable.')

end


function t = isprtchr(c)
%ISPRTCHR True for printable characters.
%
%   For a string C, ISPRTCHR(C) is 1 for printable characters and 0
%   otherwise.
%
%   See also: ISALNUM, ISALPHA, ISASCII, ISDIGIT, ISLOWER, ISUPPER,
%   ISXDIGIT.

%   Author:      Peter J. Acklam
%   Time-stamp:  2002-03-03 13:50:57 +0100
%   E-mail:      pjacklam@online.no
%   URL:         http://home.online.no/~pjacklam

   error(nargchk(1, 1, nargin));

   t = ischar(c) & (   ( (  32 <= c ) & ( c <= 126 ) ) ...
                     | ( ( 159 <= c ) & ( c <= 255 ) ) );

   % whether this is correct depends on the coding system used
%   if strncmp(computer, 'PC', 2)
%      t = t | ( ( 130 <= c ) & ( c <= 140 ) ) ...
%            | ( ( 145 <= c ) & ( c <= 156 ) );
%   end


%kSeparator 'Separator Object Menuform'
% This MatLab function was automatically generated by a converter (KhorosToMatLab) from the Khoros Separator.pane file
%
% Parameters:
%
% Example: kSeparator( {})
%
% Khoros helpfile follows below:
%
% .begin tagged
%
% .item "The Separator GUI Object"
% The Khoros pixmap object is used on Khoros graphical user interfaces whenever
% a dividing line is desired for visual effect.  A vertical separator is a
% straight line that spans the height of its parent; a horizontal separator is
% a straight line that spans the width of its parent.
%
% .item "Separator Type"
% The separator line may be solid or dashed.
%
% .item "Separator Orientation"
% The separator may have a horizontal or vertical orientation.
%
% .end tagged

function varargout = kSeparator(varargin)
Inputs={};
if nargin ==0
  arglist={'',''};
elseif nargin ==1
  arglist=varargin{1};
else error('Usage: [out1,..] = kSeparator(arglist).');
end
if size(arglist,2)~=2
  error('arglist must be of form {''ParameterTag1'',value1;''ParameterTag2'',value2}')
 end
narglist={};
maxval={};
minval={};
istoggle=[];
was_set=istoggle * 0;
paramtype={};
% identify the input arrays and assign them to the arguments as stated by the user
if ~iscell(Inputs)
Inputs = {Inputs};
end
NumReqOutputs=0; nextinput=1; nextoutput=1;
  for ii=1:size(arglist,1)
  wasmatched=0;
  for jj=1:size(narglist,1)
   if strcmp(arglist{ii,1},narglist{jj,1})  % a given argument was matched to the possible arguments
     wasmatched = 1;
     was_set(jj) = 1;
     if strcmp(narglist{jj,2}, '__input')
      if (nextinput > length(Inputs))
        error(['Input ' narglist{jj,1} ' has no corresponding input!']);
      end
      narglist{jj,2} = 'OK_in';
      nextinput = nextinput + 1;
     elseif strcmp(narglist{jj,2}, '__output')
      if (nextoutput > nargout)
        error(['Output nr. ' narglist{jj,1} ' is not present in the assignment list of outputs !']);
      end
      if (isempty(arglist{ii,2}))
        narglist{jj,2} = 'OK_out';
      else
        narglist{jj,2} = arglist{ii,2};
      end

      nextoutput = nextoutput + 1;
      if (minval{jj} == 0)
         NumReqOutputs = NumReqOutputs - 1;
      end
     elseif isstr(arglist{ii,2})
      narglist{jj,2} = arglist{ii,2};
     else
        if strcmp(paramtype{jj}, 'Integer') & (round(arglist{ii,2}) ~= arglist{ii,2})
            error(['Argument ' arglist{ii,1} ' is of integer type but non-integer number ' arglist{ii,2} ' was supplied']);
        end
        if (minval{jj} ~= 0 | maxval{jj} ~= 0)
          if (minval{jj} == 1 & maxval{jj} == 1 & arglist{ii,2} < 0)
            error(['Argument ' arglist{ii,1} ' must be bigger or equal to zero!']);
          elseif (minval{jj} == -1 & maxval{jj} == -1 & arglist{ii,2} > 0)
            error(['Argument ' arglist{ii,1} ' must be smaller or equal to zero!']);
          elseif (minval{jj} == 2 & maxval{jj} == 2 & arglist{ii,2} <= 0)
            error(['Argument ' arglist{ii,1} ' must be bigger than zero!']);
          elseif (minval{jj} == -2 & maxval{jj} == -2 & arglist{ii,2} >= 0)
            error(['Argument ' arglist{ii,1} ' must be smaller than zero!']);
          elseif (minval{jj} ~= maxval{jj} & arglist{ii,2} < minval{jj})
            error(['Argument ' arglist{ii,1} ' must be bigger than ' num2str(minval{jj})]);
          elseif (minval{jj} ~= maxval{jj} & arglist{ii,2} > maxval{jj})
            error(['Argument ' arglist{ii,1} ' must be smaller than ' num2str(maxval{jj})]);
          end
        end
     end
     if ~strcmp(narglist{jj,2},'OK_out') &  ~strcmp(narglist{jj,2},'OK_in')
       narglist{jj,2} = arglist{ii,2};
     end
   end
   end
   if (wasmatched == 0 & ~strcmp(arglist{ii,1},''))
        error(['Argument ' arglist{ii,1} ' is not a valid argument for this function']);
   end
end
% match the remaining inputs/outputs to the unused arguments and test for missing required inputs
 for jj=1:size(narglist,1)
     if  strcmp(paramtype{jj}, 'Toggle')
        if (narglist{jj,2} ==0)
          narglist{jj,1} = '';
        end;
        narglist{jj,2} = '';
     end;
     if  ~strcmp(narglist{jj,2},'__input') && ~strcmp(narglist{jj,2},'__output') && istoggle(jj) && ~ was_set(jj)
          narglist{jj,1} = '';
          narglist{jj,2} = '';
     end;
     if strcmp(narglist{jj,2}, '__input')
      if (minval{jj} == 0)  % meaning this input is required
        if (nextinput > size(Inputs))
           error(['Required input ' narglist{jj,1} ' has no corresponding input in the list!']);
        else
          narglist{jj,2} = 'OK_in';
          nextinput = nextinput + 1;
        end
      else  % this is an optional input
        if (nextinput <= length(Inputs))
          narglist{jj,2} = 'OK_in';
          nextinput = nextinput + 1;
        else
          narglist{jj,1} = '';
          narglist{jj,2} = '';
        end;
      end;
     else
     if strcmp(narglist{jj,2}, '__output')
      if (minval{jj} == 0) % this is a required output
        if (nextoutput > nargout & nargout > 1)
           error(['Required output ' narglist{jj,1} ' is not stated in the assignment list!']);
        else
          narglist{jj,2} = 'OK_out';
          nextoutput = nextoutput + 1;
          NumReqOutputs = NumReqOutputs-1;
        end
      else % this is an optional output
        if (nargout - nextoutput >= NumReqOutputs)
          narglist{jj,2} = 'OK_out';
          nextoutput = nextoutput + 1;
        else
          narglist{jj,1} = '';
          narglist{jj,2} = '';
        end;
      end
     end
  end
end
if nargout
   varargout = cell(1,nargout);
else
  varargout = cell(0);
end
global KhorosRoot
if exist('KhorosRoot') && ~isempty(KhorosRoot)
w=['"' KhorosRoot];
else
if ispc
  w='"C:\Program Files\dip\khorosBin\';
else
[s,w] = system('which cantata');
w=['"' w(1:end-8)];
end
end
callKhoros([w 'Separator" '],Inputs,narglist);


%kkimportraw 'Low level import of raw (binary) data'
% This MatLab function was automatically generated by a converter (KhorosToMatLab) from the Khoros kimportraw.pane file
%
% Parameters:
% InputFile: i1 'Raw (binary) input', required: 'raw (binary) input'
% InputFile: i2 'Add-to object', optional: 'Object to which the new data should be added'
% Integer: wsize 'Width ', default: 1: 'Size of width dimension'
% Integer: hsize 'Height ', default: 1: 'Size of height dimension'
% Integer: dsize 'Depth ', default: 1: 'Size of depth dimension'
% Integer: tsize 'Time ', default: 1: 'Size of time dimension'
% Integer: esize 'Elements ', default: 1: 'Size of elements dimension'
% Integer: skip 'Offset or skip (bytes)', default: 0: 'skip this number of bytes before reading'
% OutputFile: o 'Output object', required: 'output object'
%
% Example: o = kkimportraw({i1, i2}, {'i1','';'i2','';'wsize',1;'hsize',1;'dsize',1;'tsize',1;'esize',1;'skip',0;'o',''})
%
% Khoros helpfile follows below:
%
%  PROGRAM
% kimportraw - Low level import of raw (binary) data
%
%  DESCRIPTION
% .I kimportraw
% can be used to read raw binary data and import it to a data segment.  If
% the second input file is supplied, "Add-to Object", the new data
% will be appended onto that object.  Otherwise, an new object is created.
% The data is stored in the "Destination segment" specified.
%
% The source architecture, data type information, and data dimensions and
% structure  must be known to permit the data to be imported correctly.
%
% The -skip argument can be used to cause the specified number of bytes to be
% skipped before reading the data that will eventually end up in the output
% object. This is handy for skipping over a header or other information in
% a raw data file to get to the actual data.
%
% The index order of the data is assumed to be WHDTE.
%
% Access to the raw input is done using a special input mode (obtained
% by setting the KOBJ_RAW flag when the input file is opened) that forces
% the data to be interpreted in raw mode. This means that even if the
% data is actually in one of the supported formats, that fact will be
% ignored. This potentially lets you read the headers and other goodies
% and store them as data in the output object.
%
% If the destination of the raw data is specified to be the comment attribute
% of the output object, the raw data will be accessed as KUBYTE regardless of
% the setting of the -type argument. The number of characters given by the
% product of the sizes of the dimensions will be read in and appended to the
% comment attribute in the output object. Only those characters up to
% the first NULL in the data will actually end up in the comment
% since the comment is required to be a NULL terminated string.
%
%
%
%  EXAMPLES
% The following example shows how one might read a raw data file generated by a
% frame grabber. Here, we will assume that the data is a 512 col by 480 row
% unsigned byte color (RGB) image, with a 1024 byte header. We will assume that
% the color data is stored with the whole R plane, then the whole G plane, then
% the whole B plane.
% .DS
%   % kimportraw -o khoros.rgb -i1 framegrab.rgb -type 3 -wsize 512 -hsize 480 -esize 3 -segment 1 -skip 1024 -color RGB
% .DE
%
%  "SEE ALSO"
% kimportasc(1), kformats(1), kcomment(1), kcolorspace(1)
%
%  RESTRICTIONS
% The data is assumed to be stored in the raw file with a WHDTE index order.
% It would be nice to be able to directly handle mulitply interleaved data,
% but specifiying how that is to be done in 5D is a mess.
%
%  REFERENCES
%
%  COPYRIGHT
% Copyright (C) 1993 - 1997, Khoral Research, Inc. ("KRI")  All rights reserved.
%

function varargout = kkimportraw(varargin)
if nargin ==0
  Inputs={};arglist={'',''};
elseif nargin ==1
  Inputs=varargin{1};arglist={'',''};
elseif nargin ==2
  Inputs=varargin{1}; arglist=varargin{2};
else error('Usage: [out1,..] = kkimportraw(Inputs,arglist).');
end
if size(arglist,2)~=2
  error('arglist must be of form {''ParameterTag1'',value1;''ParameterTag2'',value2}')
 end
narglist={'i1', '__input';'i2', '__input';'wsize', 1;'hsize', 1;'dsize', 1;'tsize', 1;'esize', 1;'skip', 0;'o', '__output'};
maxval={0,1,2,2,2,2,2,1,0};
minval={0,1,2,2,2,2,2,1,0};
istoggle=[0,1,1,1,1,1,1,1,0];
was_set=istoggle * 0;
paramtype={'InputFile','InputFile','Integer','Integer','Integer','Integer','Integer','Integer','OutputFile'};
% identify the input arrays and assign them to the arguments as stated by the user
if ~iscell(Inputs)
Inputs = {Inputs};
end
NumReqOutputs=1; nextinput=1; nextoutput=1;
  for ii=1:size(arglist,1)
  wasmatched=0;
  for jj=1:size(narglist,1)
   if strcmp(arglist{ii,1},narglist{jj,1})  % a given argument was matched to the possible arguments
     wasmatched = 1;
     was_set(jj) = 1;
     if strcmp(narglist{jj,2}, '__input')
      if (nextinput > length(Inputs))
        error(['Input ' narglist{jj,1} ' has no corresponding input!']);
      end
      narglist{jj,2} = 'OK_in';
      nextinput = nextinput + 1;
     elseif strcmp(narglist{jj,2}, '__output')
      if (nextoutput > nargout)
        error(['Output nr. ' narglist{jj,1} ' is not present in the assignment list of outputs !']);
      end
      if (isempty(arglist{ii,2}))
        narglist{jj,2} = 'OK_out';
      else
        narglist{jj,2} = arglist{ii,2};
      end

      nextoutput = nextoutput + 1;
      if (minval{jj} == 0)
         NumReqOutputs = NumReqOutputs - 1;
      end
     elseif isstr(arglist{ii,2})
      narglist{jj,2} = arglist{ii,2};
     else
        if strcmp(paramtype{jj}, 'Integer') & (round(arglist{ii,2}) ~= arglist{ii,2})
            error(['Argument ' arglist{ii,1} ' is of integer type but non-integer number ' arglist{ii,2} ' was supplied']);
        end
        if (minval{jj} ~= 0 | maxval{jj} ~= 0)
          if (minval{jj} == 1 & maxval{jj} == 1 & arglist{ii,2} < 0)
            error(['Argument ' arglist{ii,1} ' must be bigger or equal to zero!']);
          elseif (minval{jj} == -1 & maxval{jj} == -1 & arglist{ii,2} > 0)
            error(['Argument ' arglist{ii,1} ' must be smaller or equal to zero!']);
          elseif (minval{jj} == 2 & maxval{jj} == 2 & arglist{ii,2} <= 0)
            error(['Argument ' arglist{ii,1} ' must be bigger than zero!']);
          elseif (minval{jj} == -2 & maxval{jj} == -2 & arglist{ii,2} >= 0)
            error(['Argument ' arglist{ii,1} ' must be smaller than zero!']);
          elseif (minval{jj} ~= maxval{jj} & arglist{ii,2} < minval{jj})
            error(['Argument ' arglist{ii,1} ' must be bigger than ' num2str(minval{jj})]);
          elseif (minval{jj} ~= maxval{jj} & arglist{ii,2} > maxval{jj})
            error(['Argument ' arglist{ii,1} ' must be smaller than ' num2str(maxval{jj})]);
          end
        end
     end
     if ~strcmp(narglist{jj,2},'OK_out') &  ~strcmp(narglist{jj,2},'OK_in')
       narglist{jj,2} = arglist{ii,2};
     end
   end
   end
   if (wasmatched == 0 & ~strcmp(arglist{ii,1},''))
        error(['Argument ' arglist{ii,1} ' is not a valid argument for this function']);
   end
end
% match the remaining inputs/outputs to the unused arguments and test for missing required inputs
 for jj=1:size(narglist,1)
     if  strcmp(paramtype{jj}, 'Toggle')
        if (narglist{jj,2} ==0)
          narglist{jj,1} = '';
        end;
        narglist{jj,2} = '';
     end;
     if  ~strcmp(narglist{jj,2},'__input') && ~strcmp(narglist{jj,2},'__output') && istoggle(jj) && ~ was_set(jj)
          narglist{jj,1} = '';
          narglist{jj,2} = '';
     end;
     if strcmp(narglist{jj,2}, '__input')
      if (minval{jj} == 0)  % meaning this input is required
        if (nextinput > size(Inputs))
           error(['Required input ' narglist{jj,1} ' has no corresponding input in the list!']);
        else
          narglist{jj,2} = 'OK_in';
          nextinput = nextinput + 1;
        end
      else  % this is an optional input
        if (nextinput <= length(Inputs))
          narglist{jj,2} = 'OK_in';
          nextinput = nextinput + 1;
        else
          narglist{jj,1} = '';
          narglist{jj,2} = '';
        end;
      end;
     else
     if strcmp(narglist{jj,2}, '__output')
      if (minval{jj} == 0) % this is a required output
        if (nextoutput > nargout & nargout > 1)
           error(['Required output ' narglist{jj,1} ' is not stated in the assignment list!']);
        else
          narglist{jj,2} = 'OK_out';
          nextoutput = nextoutput + 1;
          NumReqOutputs = NumReqOutputs-1;
        end
      else % this is an optional output
        if (nargout - nextoutput >= NumReqOutputs)
          narglist{jj,2} = 'OK_out';
          nextoutput = nextoutput + 1;
        else
          narglist{jj,1} = '';
          narglist{jj,2} = '';
        end;
      end
     end
  end
end
if nargout
   varargout = cell(1,nargout);
else
  varargout = cell(1,1);
end
global KhorosRoot
if exist('KhorosRoot') && ~isempty(KhorosRoot)
w=['"' KhorosRoot];
else
if ispc
  w='"C:\Program Files\dip\khorosBin\';
else
[s,w] = system('which cantata');
w=['"' w(1:end-8)];
end
end
[varargout{:}]=callKhoros([w 'kimportraw"  '],Inputs,narglist);


classdef ChannelTag
   %ChannelTag Summary of this class goes here
   % ChannelTag Contains network, station, location and channel information
   %
   %
   % ChannelTag Properties:
   %   network  - network tag   (Generally 2 characters)
   %   station  - station tag   (Up to 5 alphanumeric characters)
   %   location - location tag  (2 characters)
   %   channel  - channel tag   (3 character channel tag)
   %
   % ChannelTag Methods:
   %   char - Retrieve properties as 'network.station.location.channel'
   %   string - Retrieve properties as 'network.station.location.channel'
   %   scn - Retrieve properties as 'station_channel_network'
   %   fixedlengthstrings -  return strings of a fixed length
   %   eq - == for channeltags, with simple '*' wildcard support
   %   ne - ~= for channeltag (no wilcard support)
   %
   %   sort - Sort in assending order by net.cha.loc.sta
   %   matching - Field-by-field comparison
   %
   % Examples of ChannelTag construction:
   %  chaTag = ChannelTag('NW.STA.LO.CHA'); % create from text
   %  chaTag = ChannelTag(network, station, location, channel);
   %  chaTag = ChannelTag(); %default, blank nscl
   %
   %  Example using cells of stationID strings
   %    x = ChannelTag.array({'NW.STA.LO.CHA','NW.STA.LO.CHA'[,...]});
   %
   %  Example using individual parts
   %    cha_tags = ChannelTag.array({'IU','ANMO','00',{'BHZ','BH1','BH2'});
   %
   %  note, an array of cha_tags may be created by including cell arrays of
   %  choices. However, all cells that contain multiple strings must be the
   %  same size and shape.
   %
   %  OK: x = ChannelTag.array('IU',{'ANMO','ANTO},'00',{'BHE','BH1'}
   %        results in IU.ANMO.00.BHE & IU.ANTO.00.BH1
   %
   %  NOTOK: x = ChannelTag.array('IU',{'ANMO','ANTO},'00',{'BHE';'BH1'}
   %        error because stations (1x2) not same size as channels (2x1)
   %

   %{
   %  ----------- WILDCARDS ---------------------
   %  ChannelTag is merely a storage unit for net-sta-chan-loc information. It
   %  is blind to wildcards. However, when used as an argument in WAVEFORM,
   %  wildcards in the ChannelTag take on meaning that depends somewhat on the
   %  DATASOURCE type. In most cases, * wildcards are understood without
   %  issue. Note that '*' differs from ''. The latter excludes this term from
   %  the search altogether. Station and channel cannot be excluded from the
   %  search.
   %
   %  ChannelTag('R*','BHZ','XE','') match all stations begining with R
   %  ChannelTag('MCK','*','XE','')  match all channels for MCK
   %  ChannelTag('R*','*','XE','')   all R* stations. All channels
   %
   %  The uses above have been tested against Antelope data sources. Note that
   %  in Antelope it would be more common to use the literal wildcards 'R.*'
   %  or '.*'. When waveform interprets ChannelTags, it considers these use
   %  the same.
   %
   % EXAMPLE:
   %   s={'ANMO','ANTO'} , n='IU' , L='00' , c={'BHZ,BHE'}
   %   chaTag=ChannelTag(s,c,n,L); %  returns a 1x2 ChannelTag:
   %    chaTag(1) contains ANMO, IU, 00, BHZ
   %    chaTag(2) contains ANTO, IU, 00, BHE
   %}

   properties
      network  = '';
      station  = '';
      location = '';
      channel  = '';
   end

   methods
      function obj = ChannelTag(varargin)
         %ChannelTag    Construct a ChannelTag from a string or 4 strings.
         %  chaTag = ChannelTag('IU', 'ANMO', '00', 'BHZ'); % net, sta, loc, cha
         %  chaTag = ChannelTag('IU.ANMO.00.BHZ');
         %
         %  any of these may be empty ([] or '').
         %  to create multiple ChannelTags at once, use ChannelTag.array()
         %
         %  See also: array
         % TODO: Update the details. ChannelTag.array is not necessary anymore

         switch nargin
            case 4
               % inputsThatAreCells = cellfun(@iscell,{N,S,L,C});
               % if any(cellfun(@iscell,{N,S,L,C}))
               if any(cellfun(@iscell,varargin))
                  error('ChannelTag:InvalidConversion',...
                     ['expected strings but received cells.\n', ...
                     'To create multiple ChannelTags, use ChannelTag.array()']);
               end
               varargin = strtrim(varargin);
               [obj.network, obj.station, obj.location, obj.channel] = deal(varargin{:});
            case 1
               inObj = varargin{1};
               switch class(inObj)
                  case 'ChannelTag'
                     obj = inObj;
                  case {'char','struct'}
                     if size(inObj,1) == 1
                     [obj.network, obj.station, obj.location, obj.channel] = ...
                        ChannelTag.parse(inObj);
                     else
                        obj = ChannelTag.array(varargin{1});
                        %error('ChannelTag:InvalidConversion',...
                        %   'To create multiple ChannelTags, use ChannelTag.array()');
                     end

                  case 'cell'
                     if numel(inObj) == 1
                        [obj.network, obj.station, obj.location, obj.channel] = ...
                           ChannelTag.parse(inObj{1});
                     else
                        obj = ChannelTag.array(varargin{1});
                        %error('ChannelTag:InvalidConversion',...
                        %   'To create multiple ChannelTags, use ChannelTag.array()');
                     end
                  otherwise
                     error ('ChannelTag:InvalidConversion','Invalid number of arguments');
               end

            case 0
               return
            otherwise
               error ('ChannelTag:InvalidConversion','Invalid number of arguments');
         end
      end

      function [IDX, objs] = matching(objs, N, S, L, C)
         %matching   Field-by-field comparison
         %
         % idx = cha_tags.matching(Net, Sta, Loc, Cha);
         % idx = cha_tags.matching('net.sta.loc.cha');
         % returns an index vector the same size as cha_tags
         % [idx, matched] = cha_tags.matching(...)
         %   additionally returns the actual matching cha_tags
         %
         % so, cha_tags(matches) will return the actual values
         % use [] or '' as wildcard
         %  either:
         %    cha_tags.matching('IU','ANMO',[],'BHZ')   or
         %    cha_tags.matching('IU.ANMO..BHZ')
         % will match BOTH
         %   'IU.ANMO.00.BHZ' and 'IU.ANMO.01.BHZ'
         if nargin == 2
            if (ischar(N) && any(N == '.')) || isa(N,'ChannelTag')
               % query used single string : 'net.sta.loc.cha'
               [N, S, L, C] = ChannelTag.parse(N);
            else
               error('CHANNELTAG:matching:UnknownMatchType','don''t know how to match it');
            end
         end
         IDX = true(size(objs));
         if ~isempty(N) && all(N ~= '*')
            IDX = strcmp({objs.network}, N);
         end
         if exist('S', 'var') && ~isempty(S) && all(S ~= '*')
            IDX = IDX & strcmp({objs.station}, S);
         end
         if exist('L', 'var')  && ~isempty(L) && all(L ~= '*')
            IDX = IDX & strcmp({objs.location}, L);
         end
         if exist('C', 'var') && ~isempty(C) && all(C ~= '*')
            IDX = IDX & strcmp({objs.channel}, C);
         end
         if nargout == 2
            objs = objs(IDX);
         end
      end

      function [Y, I] = sort(cha_tags)
         %sort   Sort channels assending name order
         %   sorted = sort(channeltags) will sort channels in assending
         %   order using the precidence: Network, Station, Location,
         %   Channel. The result will be an array of sorted ChannelTags.
         %
         %   [sorted, I] = sort(chantags) will additionally return the
         %   index, so that chantags(I) = sorted
         %
         %   sort is designed to handle SEED-like names, so it will pad each
         %   value to 5 places before comparing.
         %      Example:
         %        c = ChannelTag({'IU.ANMO.00.BHZ','AV.OKTU..EHZ'})
         %        sortedC = c.sort()  % or sort(c)
         %
         %  The above example would compare:
         %    'IU    .ANMO  .00    .BHZ   ' vs
         %    'AV    .OKTU  .      .EHZ   '
         %
         [~,I] = sort(cha_tags.fixedlengthstrings(5,5,5,5));
         Y = cha_tags(I);
      end
      result = eq(A, B)

      function result = ne(A, B)
         %~=   for ChannelTag.
         %   wilcards aren't considered
         result = ~strcmp(A.string,B.string);
      end

      function val = get(obj, prop)
         %GET for the chaTag object
         %  result = get(ChannelTag, property), where PROPERTY is one of the
         %  following:
         %    STATION, CHANNEL, LOCATION, NETWORK, chaTag_STRING
         %
         %  *Deprecated*
         %
         % If the results of a single chaTag are requested, then a string is returned.
         % Otherwise, a cell of values will be returned.

         %warning('Old Usage. instead of "get", ,use the field name directly.  eg. x = ch.station');

         prop = lower(prop);

         switch prop
            case{'station','channel','network','location'}
               val = {obj.(prop)};
            case {'nscl_string'}
               %stuff = chaTag.string;
               warning('CHANNELTAG:get:obsoleteUsage',...
                  ['get(''nsclstring'') obsolete. Use chaTag.string().\n'...
                  '  notice it will return N.S.L.C instead of N_S_C_L']);
               val=strcat({obj.network},'_',{obj.station},'_',{obj.channel},'_',{obj.location});
            otherwise
               error('CHANNELTAG:get:UnrecognizedProperty',...
                  'Unrecognized property name : %s',  upper(prop));
         end

         %if a single chaTag, then return the string representation, else return a
         %cell of strings.
         if numel(val) == 1
            val = val{1};
         else
            val = reshape(val,size(obj));
         end
      end%get

      function obj = set(obj, varargin)
         %SET  Set properties for ChannelTag
         %    s = Set(s,prop_name, val, ...)
         %    Valid property names:
         %    STATION, LOCATION, NETWORK, CHANNEL
         %
         %    * Deprecated*
         %    See also: get.channel, get.location, get.network, get.station

         %warning('Old usage: using set for a ChannelTag. use field notation instead. eg. ch.station = X');
         idx = [1 2];
         while numel(varargin) > 1 % for each property
            [prop, val] = deal(varargin{idx});
            varargin(idx) = []; % remove the prop and val
            switch upper(prop)
               case {'STATION','LOCATION','NETWORK','CHANNEL'}
                  if iscell(val) && numel(val) > 1
                     val = val{1};
                     warning('ChannelTag:set:TooManyValues',...
                        'Too many property values, only the first will be used');
                  end
                  [obj.(lower(prop))] = deal(val);
               otherwise
                  error('ChannelTag:set:UnrecognizedProperty',...
                     'Unrecognized property name : %s',  upper(prop));
            end; %switch
         end; %each property
         if ~isempty(varargin)
            error('ChannelTag:set:PropertyValueMismatch',...
               'Each property should have a value. [%s] has no matching value', varargin{1});
         end
      end

      function s = fixedlengthstrings(objs, netLen, staLen, locLen, chaLen)
         %fixedlengthstrings   return strings of a fixed length
         %    s = fixedlengthstrings(objs, netLen, staLen, locLen, chaLen)

         strformat = sprintf('%%-%ds.%%-%ds.%%-%ds.%%-%ds',netLen,staLen,locLen,chaLen);
         for n = numel(objs) : -1 : 1
            chaTag = objs(n);
            s(n) = {sprintf(strformat,chaTag.network,chaTag.station,chaTag.location,chaTag.channel)};
         end
      end

      function disp(obj)
         %disp   display the ChannelTag details
         if numel(obj) == 1
            disp('ChannelTag with network.station.location.channel:')
            fprintf('   network: ''%s''\n   station: ''%s''\n  location: ''%s''\n   channel: ''%s''\n',...
               obj.network,obj.station,obj.location, obj.channel);
         else
            disp('ChannelTag array (network.station.location.channel):')
            for n=1:numel(obj)
               if strcmp(obj(n).string,'...')
                  disp('  ... (empty ChannelTag)');
               else
                  disp(['  ', obj(n).string]);
               end
            end
         end
      end

      function c = char(obj)
         %char   convert a ChannelTag to its string representation
         c = obj.string([],'nocell');
      end

      function s = string(obj, delim, option)
         %string returns string representation of the channelTag(s)
         % s = chaTag.string()  will return the string representation
         %      (1xn char) in the format NET.STA.LOC.CHA
         %
         % s = chaTag.string(DELIM) will use DELIM to separate
         %     fields.
         %     ex.  chaTag.string('-'); will return NET-STA-LOC-CHA
         %
         % If chaTag is an array, then results are returned as a
         % a cell of strings of same shape as chaTag will be returned.
         %
         % s = chaTag.string(DELIM,'nocell') to overrides functionality
         % to return a padded NxM char array
         % if DELIM is empty, then '.' will be used.
         if ~exist('delim','var') || isempty(delim)
            delim = '.';
         end
         if numel(obj) == 1
            s = getDelimitedString(obj, delim);
         else
            if exist('option','var') && strcmpi(option,'nocell')
               s = '';
               for n=1 : numel(obj)
                  tmp = getDelimitedString(obj(n), delim);
                  s(n,1:numel(tmp)) = tmp;
               end
            else
               s = cell(size(obj));
               for n=1 : numel(obj)
                  s(n) = {getDelimitedString(obj(n), delim)};
               end
            end
         end

          function s = getDelimitedString(obj, delim)
            %getDelimitedString   return a string with delimited fields
            %   getDelimitedString(chtag, delim) returns the fields in
            %   network,station, location, channel order, with the
            %   specified delimiter in between.
            %
            %   example:
            %     >> ch = ChannelTag('NT.STA.00.BHZ');
            %     >> s = getDelimitedString(ch, ':');
            %     s =
            %        'NT:STA:00:BHZ'
            %
            s = [obj.network, delim, obj.station, delim,...
                     obj.location, delim, obj.channel];
          end
      end

      function s = scn(obj, delim, option)
         %scn returns scn representation of the channelTag(s)
         % s = chaTag.scn()  will return the string representation
         %      (1xn char) in the format STA_CHA_NET
         %
         % s = chaTag.string(DELIM) will use DELIM to separate
         %     fields.
         %     ex.  chaTag.string('-'); will return NET-STA-LOC-CHA
         %
         % If chaTag is an array, then results are returned as a
         % a cell of strings of same shape as chaTag will be returned.
         %
         % s = chaTag.string(DELIM,'nocell') to overrides functionality
         % to return a padded NxM char array
         % if DELIM is empty, then '.' will be used.
         if ~exist('delim','var') || isempty(delim)
            delim = '_';
         end
         if numel(obj) == 1
            s = [obj.station, delim, obj.channel, delim, obj.network];
         else
            if exist('option','var') && strcmpi(option,'nocell')
               s = '';
               for n=1 : numel(obj)
                  tmp = [obj.station, delim, obj.channel, delim, obj.network];
                  s(n,1:numel(tmp)) = tmp;
               end
            else
               s = cell(size(obj));
               for n=1 : numel(obj)
                  s(n) = {[obj.station, delim, obj.channel, delim, obj.network]};
               end
            end
         end

      end

   end%methods

   methods(Static)
      function  [N, S, L, C] = parse(val)
         %parse   parse a period-delimeted string into NET,STA,LOC,CHA
         %     [N, S, L, C] = ChannelTag.parse('net.sta.loc.cha')

         if isstruct(val) || isa(val,'ChannelTag')
            N = val.network;
            S = val.station;
            L = val.location;
            C = val.channel;
         elseif ischar(val)
            % test validity, then split.  in R2015a+ strsplit() could do
            % this. But, this is being designed on R2012a.
            parts =  strsplit(val, '.');
            if numel(parts) ~= 4
               error('ChannelTag:parse:InvalidFieldCount',...
                  'Expected ''A.B.C.D'' (4 fields), but received %d.', numel(parts));
            end
            if any(val == ' '),
               parts = strtrim(parts);
            end
            [N, S, L, C] = deal(parts{:});
         else
            error('ChannelTag:parse:UnknownClass',...
               'unknown how to parse: %s', class(val));
         end

         function  C = strsplit(s, delim)
            %strsplit   return a cell created from a string.
            % C = strsplit(str, delim) returns cell created from string
            %  ex. C = strsplit('A.B..D','.') -> {'A', 'B', '', 'D'}
            % can (probably) be commented out or removed in r2015a+
            splitpts = find(s == delim);
            starts = [1, splitpts+1];
            ends = [splitpts-1, numel(s)];
            C = cell(1,numel(starts));
            for n = 1:numel(starts)
               C(n) = {s(starts(n):ends(n))};
            end
         end
      end

      function cha_tags = array(varargin)
         % chaTag.array creates multiple ChannelTags from the input.
         % cha_tags = chaTag.array({'NET.STA.LOC.CHA','NET2.STA2..CH2'[,...]})
         % cha_tags = chaTag.array(Net/list, Sta/list, Loc/list, Cha/list)
         switch nargin
            case 0
               % return an empty nscltag array
               cha_tags = ChannelTag('...');
               cha_tags(:)=[];
            case 1
               % parse a cell array of stations. OR parse an NxM char, where
               % each row is NET.STA.LOC.CHA
               if iscell(varargin{1})
                  for n = numel(varargin{1}) : -1 : 1
                     cha_tags(n) = ChannelTag(varargin{1}{n});
                  end
                  cha_tags = reshape(cha_tags,size(varargin{1}));
               elseif ischar(varargin{1})
                  char_array = varargin{1};
                  nRows = size(char_array,1);
                  for n = nRows : -1 : 1
                     cha_tags(1,n) = ChannelTag(strtrim(char_array(n,:)));
                  end
               else
                  error('ChannelTag:array:UnknownInput','expected cell of strings or a char array');
               end
            case 4
               % expect 1xN char OR arbitrary-sized CELL of 1xN char
               % if multiple cells, then they should all be same size
               inputsThatAreCells = cellfun(@iscell,varargin);
               if any(inputsThatAreCells)
                  %make a cell array of N.S.L.C
                  A = strcat(varargin{1},'.',varargin{2},'.',varargin{3},'.',varargin{4});
               end
               for n = numel(A): -1 : 1
                  cha_tags(n) = ChannelTag(A{n});
               end
         end
      end

   end %static methods
end %classdef