% version 2.9: 10/15/2012
% try to increase the performance of the application, used pre-allocated
% arrays
% Used to submit custom function and data to Condor, 
% then returns output back to users.
% INPUT:
% - XStart: double type, start element of the input array.
% - XEnd: double type, end element of the input array.
% - NumberOfPoints: total numbers of the elements of the input array. (including both XStart and XEnd)
% - exeFile: custom compiled function.
% OUTPUT: Output data ,which are returned by Condor, are represented by
% graph


function DrawGraph(XStart, XEnd, NumberOfPoints, cusFunction)

% validate the input parameters
status = CheckInput(XStart, XEnd, NumberOfPoints, cusFunction);
if (status ==1)
    return;
end

% clear all old data (all CondorMatlab_App files)
status = ClearOldData();
if(status ==1)
    return;
end

% initialize some important global parameters
status = InitGlobalParams(NumberOfPoints);
if (status == 1)
    return;
end

% save application settings infomation
status = GenerateAppSettings(XStart, XEnd, NumberOfPoints, cusFunction);
if(status == 1)
    return;
end

% process the request
status = ProcessRequest(XStart, XEnd, NumberOfPoints, cusFunction);
if(status == 1)
    return;
end

% draw histogram
status = DrawHistogram(NumberOfPoints);
if(status == 1)
    return;
end
end



% clear all old data from the last time
function [status] = ClearOldData()
status = 0;
try
cover = 'CoverMe';
appFiles = 'CondorMatlab_App';
% delete CoverMe files
 delete(['*',cover,'*']);
 % delete Setting File and XML log fileshist
 delete(['*', appFiles, '*']);
 % delete log file
 if(exist('DrawGraph.log','file') ~= 0)
    delete('DrawGraph.log');
 end
% delete error file
 if(exist('DrawGraph.err', 'file') ~= 0)
     delete('DrawGraph.err');
 end

% delete input files
if(exist('inp', 'file') == 7)
     rmdir('inp','s');
end

% delete output files
if(exist('out', 'file') == 7)
     rmdir('out','s');hist
end
 
catch err
    disp('ERROR: Cannot delete temporary files.');
    status = 1;
end
end



% draw histogram about running time of all the jobs
function [status] = DrawHistogram(NumberOfPoints)
status = 0;
global TOTAL_CHUNKS;
try
    timeList = zeros(1, NumberOfPoints);
    total = 1;
    
    % get all the times in all chunks
    for i=0:TOTAL_CHUNKS
           [status, timeList, total] = GetTimeInChunk(i, timeList, total);
           if(status == 1)
               return;
           end
           
    end
    % truncate the array
    timeList = timeList(1:total-1);   
    % find min, max elements, then draw histogram
    minValue = min(timeList);
    maxValue = max(timeList);
     % divide the time from min to max into every seconds
    group = maxValue - minValue;
    DISTANCE = group + 1;
    if(DISTANCE > 100)
        DISTANCE = 100;
    end
    ranges = linspace(minValue, maxValue, DISTANCE);
    % draw histogram
    figure('name', 'Histogram');
    hist(timeList, ranges);
    % set title and x-label, y-label, and color
    title('Run-time distribution of the jobs');
    xlabel('Run-time(s)');
    ylabel('Number of jobs');
    % set color for histogram
    h = findobj(gca, 'Type', 'patch');
    set(h, 'FaceColor','g','EdgeColor','w');
    
catch err
     msg = getReport(err);
     disp(msg);   
end

end


% get time of complete jobs in a specific chunk
function [status, timeListOut, totalOut ] = GetTimeInChunk(currentChunk, timeListIn, totalIn)
status = 0;
timeListOut = timeListIn;
totalOut = totalIn;
logfileName = 'CondorMatlab_AppLog';
try
        % if couldn't find the log file, then return
        logFile = [logfileName, num2str(currentChunk), '.xml'];
        if(exist(logFile, 'file')== 0)
            return;
        end
        % file exists, read file
        xDoc = xmlread(logFile);
        % root Node
        xRoot = xDoc.getDocumentElement;
        % get node CompletedJobs
        CompletedJobsNode = xRoot.getChildNodes.item(1);
        % get all submitted time
        sNodes = CompletedJobsNode.getElementsByTagName('SubmittedTime');
        % get all finish Time
        fNodes = CompletedJobsNode.getElementsByTagName('FinishTime');
        
        n = sNodes.getLength;
        if(n == 0)
            return;
        end
        
        % read all the node SubmittedTime, FinishTime and store the
        % runningTime in array timeList
        for i = 0:n-1/afs/cs.wisc.edu/u/g/d/gdoan
            % get current value of node X
            s = str2double(sNodes.item(i).getTextContent);
            % get current value of node Y
            f = str2double(fNodes.item(i).getTextContent);
            % add x and y values to xArray and yArray
            timeListIn(totalIn) = f - s;
            totalIn = totalIn + 1;
        end
    timeListOut = timeListIn;
    totalOut = totalIn;
catch err
    msg = getReport(err);
    disp(msg);
end


end



% process the job, divide it into 2 cases:
%   - case 1: the number of points <= MAX_POINT for each chunk.
%   - case 2: the number of points > MAX_POINT for each chunk.
function [status] = ProcessRequest(XStart, XEnd, NumberOfPoints, cusFunction)
% if the number of points <= MAX_POINT
global MAX_POINT;
global TOTAL_POINTS;
global settingFile;
if(NumberOfPoints <= MAX_POINT)
    % call the function basic DrawGraph
    status = DrawGraphOneChunk(XStart, XEnd, NumberOfPoints, cusFunction,0, MAX_POINT, TOTAL_POINTS, settingFile);
    if(status == 1)
        return;
    end
    
% if the number of points > MAX_POINT
else
   
    % divide it into smaller numbers
    Distance = (XEnd - XStart)/ (NumberOfPoints-1);
    Range = Distance * (MAX_POINT - 1);
    count = 0;
    NOP = NumberOfPoints;
    
    while(NOP > MAX_POINT)
        status = DrawGraphOneChunk(XStart + count*MAX_POINT*Distance, XStart + count*MAX_POINT*Distance + Range, MAX_POINT, cusFunction,count, MAX_POINT, TOTAL_POINTS, settingFile);
        if(status == 1)
            return;
        end
        count = count + 1;
        NOP = NOP - MAX_POINT;
    end
        status =  DrawGraphOneChunk(XStart + count*MAX_POINT*Distance, XEnd, NOP, cusFunction,count, MAX_POINT, TOTAL_POINTS, settingFile);
        if(status == 1)
                return;
        end        
end
end


% initialize important global parameters
function [status] = InitGlobalParams(NumberOfPoints)
status = 0;
try
    global MAX_POINT;
    MAX_POINT = 10000; % the maximum number of points in 1 chunk
    global TOTAL_POINTS;
    TOTAL_POINTS = NumberOfPoints;
    global TOTAL_CHUNKS;
    TOTAL_CHUNKS     = floor(TOTAL_POINTS/MAX_POINT);
    if(rem(TOTAL_POINTS, MAX_POINT) == 0)
        TOTAL_CHUNKS = TOTAL_CHUNKS -1;
    end
    global settingFile;
    settingFile = 'CondorMatlab_AppSettings.xml';
catch err
    disp('Cannot initialize global params.');
    status = 1;
end
end



% log the application settings
function [status] = GenerateAppSettings(XStart, XEnd, NumberOfPoints, Function)
global settingFile;
global MAX_POINT;
    try
     
     docNode = com.mathworks.xml.XMLUtils.createDocument('Settings');
     docRootNode = docNode.getDocumentElement;
     
     % create node StartPoint=XStart
     thisElement = docNode.createElement('StartPoint');
     thisElement.appendChild(docNode.createTextNode(num2str(XStart)));
     docRootNode.appendChild(thisElement); 
     
     % create node EndPoint=XEnd
     thisElement = docNode.createElement('EndPoint');
     thisElement.appendChild(docNode.createTextNode(num2str(XEnd)));
     docRootNode.appendChild(thisElement); 
     
       % create node NOP=NumberOfPoints
     thisElement = docNode.createElement('NOP');
     thisElement.appendChild(docNode.createTextNode(num2str(NumberOfPoints)));
     docRootNode.appendChild(thisElement);
     
       % create node Function=Function
     thisElement = docNode.createElement('Function');
     thisElement.appendChild(docNode.createTextNode(Function));
     docRootNode.appendChild(thisElement); 
     
       % create node Range=MAX_POINT
     thisElement = docNode.createElement('Range');
     thisElement.appendChild(docNode.createTextNode(num2str(MAX_POINT)));
     docRootNode.appendChild(thisElement);
     
     % create node CurrentChunk which stores the index of the last complete
     % chunk
     thisElement = docNode.createElement('CurrentChunk');
     thisElement.appendChild(docNode.createTextNode('-1'));
     docRootNode.appendChild(thisElement);
     
     % write data to file AppSettings.xml
     xmlwrite(settingFile,docNode);
     
    catch err
        status = 1;
        disp('ERROR: Cannot create application settings!');
        % display message when we have exceptions
        msg = getReport(err);
        disp(msg);
            return;
    end
    status = 0;
end

% Validate the input data of function DrawGraph
function [status] = CheckInput(XStart, XEnd, NumberOfPoints, exeFile)
    status = 0;
 try
    if(XStart > XEnd)

        disp('Start point must be less than or equal end point.');
        status = 1;
        return;

    end
     % check the lower bound and upper bound for NumberOfPoints
    if(NumberOfPoints <=0 || NumberOfPoints > 10000000)

        disp('Number of points must be a positive integer from 1 to 10,000,000');
        status = 1;
        return;

    end
    

    if(mod(NumberOfPoints,1) ~=0)

        disp('Number of points must be a positive integer.');
        status = 1;
        return;
    end

    if(exist(exeFile) == 0)
        disp('ERROR: Can not find the executable file.');
        status = 1;
        return;

    end
    
 catch err
    disp('ERROR: invalid input data.');
    status = 1;
 end
end
