classdef SpreadsheetController < handle
    % SPREADSHEETCONTROLLER Class definition to communicate with the web based spreadsheet in FPT
    
    %   Copyright 2015-2018 The MathWorks, Inc.
    
    properties(Constant)
        SpreadsheetProperties = fxptui.Web.DataManager.SpreadsheetProperties;
    end
    
    properties (Access = 'private')
        PublishAppendDataChannel = '/fpt/spreadSheet/fptSpreadSheetTable/appendData';
        PublishRowSelectInUI = '/fpt/spreadSheet/makeSelection';
        PublishProposedDTValidation = '/fpt/propdt/validate';
        Subscriptions
    end
    
    methods
        function this = SpreadsheetController(uniqueID)
            connector.ensureServiceOn;
            this.addUniqueIDToChannels(uniqueID);
        end
    end
    
    methods (Hidden)
        function varargout = invokeMethod(this,methodName,varargin)
            out = this.(methodName)(varargin{:});
            varargout{1} = out;
        end
        
        function updateSpreadsheet(this)
            msgObj = struct('data',[]);
            this.publishUpdateData(msgObj);
        end
        
        function publishProposedDTValidity(this, validationResult)
            message.publish(this.PublishProposedDTValidation, validationResult);
        end
        
        function unsubscribe(this)
            for i = 1:numel(this.Subscriptions)
                message.unsubscribe(this.Subscriptions{i});
            end
        end
        
        function delete(this)
            this.unsubscribe();
        end
        
        function selectResult(this, scopingId, index)
            msgObj.uniqueID = scopingId;
            msgObj.resultIndex = index;
            message.publish(this.PublishRowSelectInUI, msgObj);
        end
        
        function spreadsheetData = prepareSpreadsheetData(this, allData, query)
            % PREPARESPREADSHEETDATA post-process the data returned from
            % DataManager (allData) to a struct that will be processed by
            % the Spreadsheet in the client
            % The Range/Proposal properties are used to manage the toolstrip state on the UI
            
            % a table with no rows will be assigned an empty table with
            % the SpreadsheetProperties column headers
            allTable = allData.rows;
            spreadsheetTable = allTable([], this.SpreadsheetProperties);
            % initialize spreadsheetData struct
            spreadsheetData = struct( ...
                'resultsHaveSimRange', allData.resultsHaveSimRange, ...
                'resultsHaveDeriveRange', allData.resultsHaveDeriveRange, ...
                'resultsHaveProposals', allData.resultsHaveProposals, ...
                'exploreTabVisible', allData.exploreTabVisible, ...
                'resultIndex', -1, ...
                'total', allData.total, ...
                'records', table2struct(spreadsheetTable) ...
                );
            
            % Extract the data from the table based on the start/end indices requested.
            startIndex = query.StartIndex;
            endIndex = startIndex + query.Count;
            tableHeight = height(allData.rows);
            if endIndex > tableHeight
                endIndex = tableHeight;
            end
            
            % guard clause and early return
            if (startIndex > endIndex) || (endIndex == 0)
                return;
            end
            
            % get index of the selected result
            index = find(strcmp(allTable.id, query.SelectedResult));
            if ~isempty(index)
                spreadsheetData.resultIndex = index;
            end
            
            % Redefine the total
            spreadsheetData.total = tableHeight;
            
            % send only the rows within range (lazy loading)
            spreadsheetTable = allTable(startIndex:endIndex,this.SpreadsheetProperties);
            spreadsheetData.records = table2struct(spreadsheetTable);
                        
        end
        
    end
    
    methods(Access=private)
        function addUniqueIDToChannels(this, uniqueID)
            this.PublishAppendDataChannel = sprintf('%s/%s',this.PublishAppendDataChannel, uniqueID);
            this.PublishRowSelectInUI = sprintf('%s/%s',this.PublishRowSelectInUI, uniqueID);
            this.PublishProposedDTValidation = sprintf('%s/%s',this.PublishProposedDTValidation, uniqueID);
        end
        
        function publishUpdateData(this,msgObj)
            message.publish(this.PublishAppendDataChannel, msgObj);
        end
    end
end
