classdef SBSRSAMPLEGRAPH
    % the 2-layer graph that contains samples and its intepretation as well
    % as a graph relating them
    properties(Dependent=true)
        maskvalid_compts
        objtypestr
        ncomp
        ptsketch
        gdxy
    end
    
    properties
        % sample points in 1st layer
        m_samplepts
        
        % pixels grouped due to nearby parallel lines
        m_pixgroup_parallel_sample2sample
        
        % pixels grouped as curve segments
        m_pixgroup_curve_sample2sample
        
        % component parameters in 2nd layer
        m_compts %component center locations
        m_complabels %component labels
        m_compscores %component match score
        m_segid_per_compts %segid = [idxsrc,idseg]
        
        %list of 3x3 transmat that transforms database segments to m_compts
        m_dftransmat_per_compts
        
        % sample and component relation, the adjmat contains edge weight
        m_adjmat_comp2sample %adjmat(i,j)>0 iff component i relates to sample j
        m_adjmat_comp2comp %adjmat(i,j)>0 iff component i connects to component j
        
        % coverage is computed from sketch to component, and then component
        % to sketch
        m_adjmat_comp2sample_fullcover
        
        % coverage is computed from component to sketch, finding just
        % enough points to cover the component
        m_adjmat_comp2sample_justenough 
        
        % connectability based on label
        m_connectable_by_label_comp2comp
        
        % edge weights in the complete graph among components
        m_structscore_comp2comp
        m_structscore_comp2comp_raw %this does not exclude edges indicated by invalidmask
        m_movedist_comp2comp %to connect component i and j, how much 2d movement is needed
        
        % probs of merging nearby identical labels
        m_mergeprobs_comp2comp
        
        % pairwise overlap ratio
        m_overlapcost_comp2comp
        
        % invalid edges due to various reasons
        m_adjmat_invalid_comp2comp
        
        % higher score, less conflict
        m_conflictscore_comp2comp
        
        % symmetry score for each pair of component
        m_symscore_comp2comp
        
        % known symmetric, read from SBSRSKETCH
        m_symask_comp2comp
        
        % seglbs confliction matrix
        % m(i,j,k)=1 iff 2d segments i and j conflicts in that they cover
        % the same stroke of component k
        % this is computed using fullcover comp2sample, because other cases
        % can be derived from it
        m_conflict_seg2seg2comp
        
        % cover length
        % m(i,j)=k iff 2d segment i covers k percentage of component j
        % this is computed using fullcover comp2sample
        m_coverlen_seg2comp
        
        % maximum cover length for each component, which is the proportion
        % covered by non-conflicting 2d sketch segments
        m_maxcoverlen_per_comp
        
        % m{i}{k} = kth conflicting segment group 
        m_conflict_seglbs_per_comp
        
        % cover fitness
        % m(i,j)=k iff the fitness of 2d segment i covering component j is
        % k. Higher fitness means more confident coverage (segment closer
        % to component, near parallel or etc)
        m_coverfitness_seg2comp
        
        % label likelihood for each sample
        m_likelihood_sample2label
        
        % for object clustering
        m_idxcompts_per_obj %compts that are clustered as one object
        m_rawgraph %samplegraph whose compts are clustered into this graph
        
        % part of sketchdata
        m_sketchimg
        m_gdx %gradient of sketchimg, m_gdx(i,j) the x gradient of point (i,j)
        m_gdy
        m_seglbs_per_sample %sketch segment label
        m_dblbs %all labels
        
        % maxdgmat(i,j) = dblbs(i) can have up to this number of dblbs(j)
        % attached
        m_maxdgmat
        
        m_symplane_p0
        m_symplane_normal
        
        % if this is a sub graph of another graph, val(i)=k iff
        % idxcompts(i) of current graph comes from idxcompts(k) from super
        % graph
        m_idxcompts_supergraph
        
        % val(i)=k iff idxcompts(i) is kth candpivot in the sketchdata
        m_idxcandpivot_in_sketchdata
        m_compscores_label_context
        m_compscores_ldir_context
        m_compscores_dist2
        m_compscores_coverage
    end
    
    methods
        function val = get.ptsketch(obj)
            val = obj.m_samplepts;
        end
        
        function val = get.gdxy(obj)
            val = [];
            if ~isempty(obj.m_sketchimg)
                dx = obj.m_gdx(obj.m_sketchimg);
                dy = obj.m_gdy(obj.m_sketchimg);
                val = [dx(:) dy(:)]';
            end
        end
        
        function val = get.ncomp(obj)
            val = length(obj.m_complabels);
        end
        
        function val = get.objtypestr(obj)
            if isempty(obj.m_complabels)
                val = [];
                return;
            end
            [~,namelist] = util_get_name_by_label(obj.m_complabels);
            val = namelist{1};
        end
        
        function val = get.maskvalid_compts(obj)
            if isempty(obj.m_compts)
                val = [];
            else
                if ~isempty(obj.m_adjmat_invalid_comp2comp)
                    val = any(~obj.m_adjmat_invalid_comp2comp,1);
                else
                    val = true(1,size(obj.m_compts,2));
                end
            end
        end
        
        function comp2sample = get_comp2sample(obj,covermode)
            if nargin == 1
                covermode = 'normal';
            end
            
            switch covermode
                case 'normal'
                    comp2sample = obj.m_adjmat_comp2sample>0;
                case 'fullcover'
                    comp2sample = obj.m_adjmat_comp2sample_fullcover>0;
                case 'justenough'
                    comp2sample = obj.m_adjmat_comp2sample_justenough>0;
            end
        end
        
        function conflictgrouplist = get_conflict_group_per_comp(obj,covermode,idxcompts)
            if nargin < 3
                idxcompts = 1:length(obj.m_complabels);
            end
            [~,conflictgrouplist] = obj.get_max_coverlen_per_comp(covermode,idxcompts);
        end
        
        function [coverlens,conflictgrouplist] = ...
                get_max_coverlen_per_comp(obj,covermode,idxcompts)
            % get maximum coverable length (proportion of the component 
            % gets covered) of a list of components
            % coverlens(i) = the max coverlen for idxcompts(i)
            % conflictgrouplist{i} = {seglbs1,seglbs2..} where seglbsi is a
            % group of seglbs that form a confliction graph. In integer
            % programming, one segment in a conflict group m{i}{j} must be
            % assigned to the component i
            assert(ischar(covermode),'covermode must be character array');
            if nargin < 3
                idxcompts = 1:length(obj.m_complabels);
            end
            
            seglbs2comp = obj.get_seglbs2comp(covermode);
            coverlens = zeros(1,length(idxcompts));
            conflictgrouplist = cell(1,length(idxcompts));
            
            for i=1:length(idxcompts)
                thisidxcompts = idxcompts(i);
                thiseglbs = find(seglbs2comp(:,thisidxcompts));
                
                % find conflicting subgraphs
                conflict_idxseg2idxseg = ...
                    obj.m_conflict_seg2seg2comp(thiseglbs,thiseglbs,thisidxcompts);
                [n,idxconncomp] = graphconncomp(sparse(conflict_idxseg2idxseg),'Weak',true);
                
                % for each group, the one with longest coverlen retain its
                % coverlen, others will be set to 0
                coverlen_per_idxseg = obj.m_coverlen_seg2comp(thiseglbs,thisidxcompts);
                thisconflictgroup = {};
                for k=1:n
                    idxsegingroup = find(idxconncomp == k);
                    if length(idxsegingroup) == 1
                        % this seglb does not conflict with others, ignore
                        continue;
                    end
                    thisconflictgroup{end+1} = thiseglbs(idxsegingroup);
                    clens = coverlen_per_idxseg(idxsegingroup);
                    [maxlen,idxmax] = max(clens);
                    clens(:) = 0;
                    clens(idxmax) = maxlen;
                    coverlen_per_idxseg(idxsegingroup) = clens;
                end
                
                conflictgrouplist{i} = thisconflictgroup;
                coverlens(i) = sum(coverlen_per_idxseg);
            end
        end
        
        function seg2seg2comp = get_conflict_seg2seg2comp(obj,covermode)
            if nargin == 1
                covermode = 'normal';
            end
            
            seglbs2comp = obj.get_seglbs2comp(covermode);
            seg2seg2comp = obj.m_conflict_seg2seg2comp;
            for i=1:length(obj.m_complabels)
                seglbsnotuse = find(~seglbs2comp(:,i));
                seg2seg = seg2seg2comp(:,:,i);
                seg2seg(seglbsnotuse,:) = 0;
                seg2seg(:,seglbsnotuse) = 0;
                seg2seg2comp(:,:,i) = seg2seg;
            end
        end
        
        function seglbs2comp = get_seglbs2comp(obj,covermode)
            if nargin == 1
                covermode = 'normal';
            end
            comp2sample = obj.get_comp2sample(covermode);
            
            [is,js] = find(comp2sample);
            js2seglbs = obj.m_seglbs_per_sample;
            js = js2seglbs(js);
            ncomp = length(obj.m_complabels);
            nseg = max(obj.m_seglbs_per_sample);
            seglbs2comp = accumarray([js(:),is(:)],1,[nseg,ncomp])>0;
        end
        
        function obj = update_compscores(obj,sketchdata,database)
            idxcandpivot = obj.m_idxcandpivot_in_sketchdata;
            if isempty(idxcandpivot)
                assert(size(obj.m_compts,2) == size(sketchdata.m_candpivots,2));
                idxcandpivot = 1:size(sketchdata.m_candpivots,2);
            end
            scores = sketchdata.get_candpivot_scores;
            scores = scores(:)';
            obj.m_compscores = scores(idxcandpivot);
            obj.m_compscores_coverage = sketchdata.m_matchscore_per_candpivot(idxcandpivot);
            obj.m_compscores_label_context = sketchdata.m_cxscore_per_candpivot_label(idxcandpivot);
            obj.m_compscores_ldir_context = sketchdata.m_cxscore_per_candpivot_ldir(idxcandpivot);
            
            if nargin>=3
                dist2 = srec_evaluate_match_by_pts2pts_dist(1:obj.ncomp,obj,database);
                dist2 = dist2(:)';
                obj.m_compscores_dist2 = 1-mat2gray(dist2);
            else
                fprintf(1,'SAMPLEGRAPH: updating compscores without database, ignoring dist2 score\n');
                obj.m_compscores_dist2 = ones(1,obj.ncomp);
            end
        end
        
        obj = update_structscore_comp2comp(obj,database,sketchdata)
        obj = update_mergeprobs_comp2comp(obj,database)
        obj = update_overlapcost_comp2comp(obj,database)
        [obj,invalidmask] = update_invalid_edges_comp2comp(obj,varargin)
        obj = update_symscore(obj,p0,plnormal)
        obj = update_likelihood_sample2label(obj,database,solist);
        obj = update_seg2seg_conflict(obj,sketchdata,database)
        val = get_symmetric_idxcompts(obj,idxcompts,varargin)
        
        newgraph = cluster_compts_into_obj(obj,varargin)
        
        % extract subgraph
        obj = subgraph(obj,idxcompts)
        
        % make subgraph that contains valid compts
        obj = subgraph_valid(obj)
        
        % get a graph with scores as edge weight, 0=disconnect
        % if adjmat is given, the score is calculated based on it,
        % otherwise it's based on candidate graph
        adjmat = get_scored_graph(obj,varargin)
        
        show_comp2sample(obj,varargin)
        show_comp2comp(obj,varargin)
        show_comp2comp_candgraph(obj,varargin)
        show_compts(obj,varargin)
        show_seglist(obj,database,varargin)
        show_coverage_by_compts(obj,idxcompts,database,varargin)
        show_symplane(obj,database,varargin);
    end
    
    methods(Access=public)
        obj = update_conflict_cost(obj,database,sketchdata)
    end
    
    methods(Static=true);
        % the sketch data must have been matched to database
        obj = init_with_sketchdata(sketchdata)
        
        % pairwise structure compatibility
        structscoremap = structscoremap_from_compts(compts,complbs,database)
        
        % probability of merging nearby same-label points
        gmdist = merging_probs(comp_ptscloud,comp_label)
    end
    
end

