classdef MTTTagFeatureGenreBasic < MTTAudioFeature & handle
    
 
    properties(Constant = true)
        
        my_revision = str2double(substr('$Rev: 99 $',  5, -1));
    end
       
    % ---
    % here come the internal clip properties.
    % the database is stored as a global variable
    % ---
    properties
        
       my_params = struct(...
            ... % ---
            ... % these are GenreBasic parameters
            ... % ---
            'pct_genres', 1, ... % 1/100 percentile genre tags used
            'empty_genres', 0 ... % allow empty genres to persist
            );
    end
    
    methods
        
        % ---
        % constructor: pointer to feature in database
        % ---
        function feature = MTTTagFeatureGenreBasic(varargin)

            feature = feature@MTTAudioFeature(varargin{:});
        end
 
        function data = extract(feature, clip)
        % process tag information and build the tag vector
            
            % ---
            % get total number of tags. 
            %
            % NOTE: the highest tag id should match the size of the 
            % tagdb lexicon
            % ---
%             num_tags = clip.my_db.genredb.size();
            
            if feature.my_params.pct_genres ~= 0
                % no tags, f.e. for parameter experiments in
                % higher level features
                [tagids, score] = clip.my_db.genredb.annotids_for_owner(clip.id);

                % save lexicon
                data.lexicon = clip.my_db.genredb.lexicon;

                % save to data structure
                data.tags.ids = tagids;
                data.tags.scores = score;
            end
            
            % save info data
            data.info.type = 'MTTTagFeatureGenreBasic';
            data.info.owner = clip;
            data.info.owner_id = clip.id;
            data.info.creatorrev = feature.my_revision;
            
            % save parameters
            data.info.params = feature.my_params;
            
            data.final.vector = [];
            data.final.vector_info = struct(); 
            data.final.dim = 0;
        end
        
        
        % ---
        % NOTE: the following transforms are just stated fro 
        % future use by now 
        % ---
        function define_global_transform(features)
            % ---
            % compute the relevant tags, and save
            % them in the commom place
            % ---
            
            % compute extreme cases
            if features(1).my_params.pct_genres == 1 && features(1).my_params.empty_genres
                % all tags allowed
                
                common.rel_dimensions.tags.ids = 1:numel(features(1).data.lexicon);
                common.rel_dimensions.tags.id_pos = 1:numel(features(1).data.lexicon);
                
                % set common feature values
                features(1).my_db.set_common(common);
                return;
                
            elseif features(1).my_params.pct_genres == 0 
                % no tags, f.e. for parameter experiments in
                % higher level features
                
                common.rel_dimensions.tags.ids = [];
                common.rel_dimensions.tags.id_pos = [];
                
                % set common feature values
                features(1).my_db.set_common(common);
                return;
            end
            
            
            allids = sparse(numel(features), numel(features(1).data.lexicon));
            for i = 1:numel(features)
                
                allids(i,features(i).data.tags.ids) = ...
                    allids(i,features(i).data.tags.ids) + features(i).data.tags.scores;
            end
            
            % ---
            % get usage of tags and filter not-used tags
            % ---
            tagsum = sum(allids, 1);
            nonzero = tagsum > 0;
            
            % ---
            % NOTE: We remove the empty genres, then
            % sort by weight / number of appearance and cut off
            ids = find(nonzero);
            [null, idx] = sort(tagsum(ids),'descend');
            ids = ids(idx);
            
            % cutoff
            num_tags = ceil( features(1).my_params.pct_genres * numel(ids));
            valid_ids = ids(1:min(end, num_tags));
            
            % ---
            % NOTE: make sure that the positions for the tags
            % stay correctly assigned / consistent
            % ---
            id_pos = sparse(size(valid_ids));
            id_pos(valid_ids) = 1:num_tags; 
            
            % save to common data structure
            common.rel_dimensions.tags.ids = valid_ids;
            common.rel_dimensions.tags.id_pos = id_pos;
     
            % set common feature values
            features(1).my_db.set_common(common);
        end
        
        function finalise(features)
          
            for i = 1:numel(features)
                
                 % check for neccesary parameters
                if isempty(features(i).my_db.commondb)

                    error('Define the global transformation first')
                    return;
                end
                
                if features(i).my_params.pct_genres == 0
                    vector_info.labels = {};

                    % save into feature struct
                    features(i).data.final.dim = 0;
                    features(i).data.final.vector = [];
                    features(i).data.final.vector_info = vector_info;
                    return;
                end
                % get valid tag ids
                valid_ids = features(i).common.rel_dimensions.tags.ids;
                num_tags = numel(valid_ids);
                
                % ---
                % get vector positions for contined ids,
                % and sort out the non_allowed id's
                % ---
                id_pos = features(i).common.rel_dimensions.tags.id_pos;
                
                [has_ids, has_pos] = intersect(features(i).data.tags.ids, valid_ids);
                
                score = features(i).data.tags.scores;
                
                % create feature vector
                vector = zeros(num_tags,1);     
                vector(id_pos(has_ids)) = score(has_pos);
                
                % ---
                % NOTE: this labelling costs to much space
                % ---
                vector_info.labels = features(i).data.lexicon(valid_ids);

                % save into feature struct
                features(i).data.final.dim = num_tags;
                features(i).data.final.vector = vector;
                features(i).data.final.vector_info = vector_info;
            end
        end
        
        function [a1] = visualise(feature)
            
            % get tag descriptions form first clip
            tags = MTTClip(feature(1).owner_id()).my_db.genres;
            
            % works for multiple feature instances
             for i = 1:numel(feature)
                  
                clip = MTTClip(feature(i).owner_id());
                
                % plot feature data
                h = bar(feature(i).data.final.vector);
                a1 = gca;
                
                set(a1,'XTick', 1:feature(i).data.final.dim,...
                    'XTickLabel', tags);
                title(sprintf('clip %d: %s by %s, genres', ...
                    clip.id, clip.title(),clip.artist()));
             end
        end

    end
end