% --
% This class loads and hanles the aufdio features included with the MTT
% Library
% --- 
classdef MTTMixedFeatureStober11GenrePCA < MTTAudioFeature & handle
    
    properties(Constant = true)
        
        my_revision = str2double(substr('$Rev: 457 $',  5, -1));
    end
    
    properties
        
        % ---
        % Set default parameters
        % ---
        my_basetype = 'MTTMixedFeatureStober11Genre';
        
        my_params = MTTAudioFeature.inherited_params(...
            'MTTMixedFeatureStober11Genre', ...
            'min_pca_var', 0, ... % [0-1], def. 0 fraction of variance to keep 
            'max_pca_coeffs', 0, ...% max. number of final coefficients
            'norm_pre_pca', 1, ...
            'norm_post_pca', 1 ...% normalise pca coefficients after transformation
        );
    end
    % ---
    % member functions
    % ---
    methods
        
        % ---
        % constructor: pointer to feature in database
        % ---
        function feature = MTTMixedFeatureStober11GenrePCA(varargin)

            feature = feature@MTTAudioFeature(varargin{:});
        end
        
        % ---
        % load feature data from xml file
        % ---
        function data = extract(feature, clip)
        % load feature data by parsing xml
            
            global globalvars;
            
            % ---
            % we extract the base features, and save 
            % the pointers to these.
            % the main work is then done in the define_global_transf
            % and finalise functions.
            % ---
            data.basefeat = clip.features(feature.my_basetype,...
                feature.my_params); 

            % save info data
            data.info.type = class(feature);
            data.info.owner_id = clip.id;
            data.info.creatorrev = feature.my_revision;
            
            % save param data
            data.info.params = feature.my_params;
            
            % prepare field for final features
            data.final.vector = [];
            data.final.dim = 0;
            data.final.vector_info.labels = {};
        end
        
        function define_global_transform(features)
            
            if numel(features) == 1
                error ('Insert feature array for this method');
            end
            
            % ---
            % We collect all the relevant genretag 
            % features and get the transform on this basis.
            % ---
            for i = 1:numel(features)
            	basef(i) = features(i).data.basefeat;
            end
            
            % call the features own transsform function
            basef.define_global_transform();
            
            % ---
            % finalise the basic features, and 
            % get the feature vectors;
            % ---
            X = basef.vector();
            
            % check dataset dimension
            if numel(features) < basef.dim;
                
                error ('Not enough feature vectors for PCA calculation. need %d samples', ...
                    basef.dim);
            end
            
            % ---
            % NOTE: should the data be normalised and scaled to -1:1 
            % instead of being in a range of 0-1 AND max-min = 1
            % --- 
            if features(1).my_params.norm_pre_pca == 1
                                
                [X, pstd] = mapminmax(X,-1,1);
                common.pca.pre_norm = pstd;
            elseif features(1).my_params.norm_pre_pca == 2
                
                [X, pstd] = mapstd(X,0,1);
                common.pca.pre_norm = pstd;
            end

            
            % ---
            % get and apply the principal component analysis
            % NOTE: the variance percentile is applied here, too.
            % ---

            [Y, ps] = processpca(X, 0);
            common.pca.transform = ps;
            
            % ---
            % get cumulative sum of variance, and decide on cutoff
            % point
            % --- 
            v = cumsum(var(Y'));
            v = v / max(v);
            common.pca.transform.varpart = v;
            
            if features(1).my_params.min_pca_var > 0
                            
                min_pca_idx = find(v >= features(1).my_params.min_pca_var,1, 'first');
                
                % save into pca structure
                common.pca.transform.yrows = min_pca_idx;
                
            end 
            
            % normalise pca values after processing
            if features(1).my_params.norm_post_pca
                
                [Y,pmm] = mapminmax(Y,0,1);
                common.pca.post_norm = pmm;
                
            end
            
            % ---
            % set common feature values for mixed features
            % ---
            features(1).my_db.set_common(common);            
            
            % save the transformed features straight away!
            features.finalise(Y);
        end
        
        function finalise(feature, final)
        % applies a final transformation and
        % collects the information of this feature within a single vector
        % see info for types in specific dimensions
        
            % determine size
            if feature(1).my_params.max_pca_coeffs > 0

                max_size = min(feature(1).common.pca.transform.yrows, ...
                    feature(1).my_params.max_pca_coeffs);
            else

                max_size = feature(1).common.pca.transform.yrows;
            end


            % prepare information
            info = {'PCA'};
            if isfield(feature(1).common.pca.transform, 'varpart')
                info(2:max_size) = num2cell(feature(1).common.pca.transform.varpart(2:max_size));
            else
                info(2:max_size) = num2cell(2:max_size);
            end

        
            % check if features have been finalised already
            if nargin == 2 && isempty(final)
                
                % the final vector etc already are set to zero;
                return;
                
            elseif nargin == 2 && (numel(feature) == size(final, 2))
                    
                for i = 1:numel(feature)
                    
                    % save final vector and description
                    feature(i).data.final.vector = final(1:max_size,i);
                    feature(i).data.final.dim = max_size;
                    feature(i).data.final.vector_info.labels = info;
                end
                
            else
            % features have to be transformed first
            % ---
            % TODO: this code remains untested
            % ---
            
            % check for neccesary parameters
                if isempty(feature(1).my_db.commondb)

                    error('Define the global transformation first')
                    return;
                end
             

                for i = 1:numel(feature)

                    % check for neccesary parameters
                    if isempty(feature(i).my_db.commondb)

                        error('Define the global transformation first')
                    end
                    
                    % ---
                    % get feature vector and apply transformation
                    % ---
                    X = feature(i).data.basefeat.vector();
                    
                    % ---
                    % apply normalisation used for removing mean 
                    % in training data
                    % ---
                    if feature(1).my_params.norm_pre_pca == 1
                                
                        X = mapminmax('apply', X, feature(1).common.pca.pre_norm);
                    elseif feature(1).my_params.norm_pre_pca == 2

                        X = mapstd('apply', X, feature(1).common.pca.pre_norm);
                    end
 
                    % apply PCA transform
                    vec = processpca('apply', X, feature(1).common.pca.transform);
                    
                    % normalise pca values after transformation
                    if feature(1).my_params.norm_post_pca

                        vec = mapminmax('apply', vec,...
                            feature(1).common.pca.post_norm);
                    end
                    
                    % ---
                    % cut vector to final size. 
                    % NOTE: this should be done before
                    % transformation to reduce computation time
                    % ---
                    vec = vec(1:max_size);
                    
                    % save final vector and description
                    feature(i).data.final.vector = vec;
                    feature(i).data.final.dim = numel(vec);
                    feature(i).data.final.vector_info.labels = info;
                end
            end
        end
        
    end
end