% A Pattern-Class which holds Members of a class
%  and works as a simple interface for all class Members
%
%
classdef janis_class < handle

    properties (SetAccess = public)
        % Human readable name for this class
        name
        testGroup;
    end

    properties (SetAccess = protected)
        % cell array of members
        members;
        sourceDimension = {};
    end

    methods

        % constructur with class name & optional the members of the class
        function obj = janis_class(className,varargin)
            obj.name = className;
            obj.members = {};
            obj.testGroup = false;
            if nargin > 1
                for memberCount=1:size(varargin,2)
                    obj = addMember(obj,varargin{memberCount});
                end
            end
        end

        % adds a new member to the class
        function this = addMember(this,newMember)
            if isa(newMember,'janis_pattern')
                this.members{size(this.members,1)+1,1} = newMember;
                if isempty(this.sourceDimension)
                    this.sourceDimension = newMember.sourceDimension;
                elseif ~isequal(newMember.sourceDimension,this.sourceDimension)
                    warning(['Image dimension of Member "' newMember.description '" does not match dimension of other members!']);
                end
            else
                throw(MException('janis:WrongPatternClass','This is not a janis pattern class file'));
            end
        end

        % returns the member with index "memberIndex"
        function member = getMember(this,memberIndex)
            if this.size > 0 && this.size >= memberIndex
                member = this.members{memberIndex};
            else
                throw(MException('janis:MemberIndexOutOfBounds','Index does not match class size'));
            end
        end

        % simply returns all Members
        function members = getAllMembers(this)
            members = this.members;
        end

        % returns amount of Members
        function s = size(this)
            s = size(this.members,1);
        end

        % if there are is one object not loaded or no subject at all
        % return false
        function loaded = allLoaded(this)
            if this.size > 0
                loaded = true;
                for mem=1:size(this.members,1)
                    loaded = this.members{mem}.isLoaded;
                end
            else
                loaded = false;
            end
        end


        % sets for all members the same reduction method "fsmethod"
        function this = setDimensionalitiyReduction(this,fsmethod)
            try
                for mem=1:size(this.members,1)
                    this.members{mem} = this.members{mem}.setFeatureSelection(fsmethod);
                end
            catch excep
                error('Seems not to be a correct dim. reduction method!')
                rethrow(excep);
            end
        end

        % returns true, if all reduction methods were set
        function tf = reductionMethodsSet(this)
            tf = true;
            try
                for mem=1:size(this.members,1)
                    this.members{mem}.getFeatureSelection;
                end
            catch
                tf = false;
            end
        end

        % Load all Pattern (and reload already loaded patterns)
        function this = loadAll(this)
            for mem=1:size(this.members,1)
                this.members{mem} = this.members{mem}.generatePattern;
            end
        end

        % returns the voxels of all subjects (by looking at the first
        % subject - there is NO validation if alle have the same Voxel
        % amount!!! therefore look at "isValid")
        function v = voxelsPerSub(this)
            if this.allLoaded
                v = this.members{1}.getVoxelCount;
            else
                v = 0;
            end
        end

        % should return true if everything is ready for computing:
        %   * feature reduction methods should be set
        %   * images should be loaded
        %   * there should be at least ONE subject.
        function valid = isValid(this)
            if this.allLoaded && this.reductionMethodsSet && this.size > 0
                valid = true;
                v = this.voxelsPerSub;
                for mem=1:this.size
                    if this.members{1}.getVoxelCount ~= v
                        valid = false;
                    end
                end

            else
                valid = false;
            end
        end

        % prints out a human readable Description of the Class
        function show(this)
            fprintf('\nClass:  ::: "%s" :::\n',this.name);
            fprintf('   Members:               %d\n',this.size);
            if this.reductionMethodsSet; r = 'Yes'; else r = 'No';
            end;
            if this.allLoaded; l = 'Yes'; else l = 'No';
            end;
            fprintf('   Reduction Methods set: %s\n', r);
            if l
                fprintf('   Images Loaded:         %s\n', l);
                fprintf('   Voxels per Subject:    %d\n', this.voxelsPerSub);
            end
        end

        % prints out a human readable list of class members
        function showMembers(this)
            fprintf('\nMembers of:  ::: "%s" :::\n',this.name);
            fprintf('   Index        Description\n')
            fprintf('   ---------------------------------------\n');
            for mem=1:size(this.members,1)
                fprintf('    %3d.:       %s\n', mem, this.members{mem}.description);
            end
            fprintf('   ---------------------------------------\n');
        end

        function markerList = getUniqueMarkers(this)
            markerList = cell(1,this.size);
            for mem=1:size(this.members,1)
                markers = struct;
                markerList{mem} = this.members{mem}.getUniqueMarkers(markers);
            end
        end
    end

end