classdef SBSRSOLUTION
    %the solution
    properties(Dependent=true)
        % indices into samplegraph.m_compts, what compts are involved in
        % this solution
        idxcompts
        adjmat
        
        % dbobj constructed from seglist
        dbobj
        
        % dbobj constructed from seglist_free3d
        dbobj_free3d
        
        % are all the components connected?
        isconnected
        
        % symmetric pairs, [i,j] = idxsympair(:,k), then idxcompts(i) and
        % idxcompts(j) are symmetric
        idxsympair
        
        % is all seglbs covered by this solution?
        is_seglbs_allcover
    end
    
    properties
        % identity information
        m_name
        m_id
        
        % put temp data here, these are not used by any function
        m_custom_data
        
        % if this solution is solved by mip, this is the options for solver
        m_mipopt
        
        % full size component graph, dimension equalt number of components
        % in samplegraph
        m_adjmat_comp2comp
        
        % sympair(1,i) and sympair(2,i) are symmetric compts
        m_sympair_idxcompts = zeros(2,0);
        
        % source points
        m_idxcompts_src
        
        % sequence of compts being selected
        m_idxcompts_seq
        
        % seglbs assigned to compts
        m_seglbs2compts_assignment
        m_seglbs2compts_assignment_unique %one seglbs just assigned to one compts
        
        % evaluation result
        m_score
        m_scoredata
        m_rebuildinfo %rebuilt sketch
        
        % realized with actual segments
        m_seglist
        m_meshlist %well placed meshlist
        m_relpos
        m_adjmat_seg2seg
        m_subobjlist %substructures
        m_seglist_free3d %solved by allowing segments to move freely in 3d
        m_meshlist_free3d
        m_lbmap %the label map
        m_seglbs_all %all seglbs presented in sketch
		
		% adjacency matrix output by MIP solver
		% adjmat(i,j)=1 iff seglist{i} connects to seglist{j}
		m_adjmat_mip
        
        % substructure match using subobjlist
        m_subcoverlist
        m_sublabelist
        
        % information in solution refinement
        m_refineinfo
        
        % evaluation information
        m_gteval
    end
    
    methods
        function val = get.is_seglbs_allcover(obj)
            if isempty(obj.m_seglbs2compts_assignment)
                val = false;
                return;
            end
            
            seglist = obj.m_seglist_free3d;
            if isempty(seglist)
                val = false;
                return;
            end
            
            seglbs = cellfun(@(x)x.m_covered_seglbs,seglist,'uniformoutput',false);
            seglbs = cat(2,seglbs{:});
            seglbs = unique(seglbs);
            val = length(seglbs) == length(obj.m_seglbs_all);
        end
        
        function val = get.idxsympair(obj)
            val = [];
            idxcompts = obj.idxcompts;
            if ~isempty(idxcompts) && ~isempty(obj.m_sympair_idxcompts)
                [~,val] = ismember(obj.m_sympair_idxcompts,idxcompts);
            end
        end
        
        function val = get.isconnected(obj)
            val = false;
            if ~isempty(obj.m_adjmat_comp2comp)
                adjmat = obj.adjmat;
                ncomp = graphconncomp(sparse(adjmat),'directed',false);
                val = ncomp == 1;
            end
        end
        
        function val = get.adjmat(obj)
            val = [];
            if ~isempty(obj.m_adjmat_comp2comp)
                val = obj.m_adjmat_comp2comp(obj.idxcompts,obj.idxcompts);
            end
        end
        
        function val = get.dbobj_free3d(obj)
            val = [];
            if ~isempty(obj.m_seglist)
                val = COMPOUND.init_with_seglist(obj.m_seglist_free3d,obj.m_adjmat_seg2seg);
            end
        end
        
        function val = get.dbobj(obj)
            val = [];
            if ~isempty(obj.m_seglist)
                val = COMPOUND.init_with_seglist(obj.m_seglist,obj.m_adjmat_seg2seg);
            end
        end
        
        function val = get.idxcompts(obj)
            val = [];
            if ~isempty(obj.m_adjmat_comp2comp)
                val = cat(1,obj.m_idxcompts_seq(:),obj.m_idxcompts_src(:),obj.m_sympair_idxcompts(:));
                val = unique(val(:));
                val = val';
            end
        end
        
        function obj = complete_seglbs_assignment(obj,samplegraph,database)
            % complete assignment
            if isempty(obj.m_seglbs2compts_assignment)
                return;
            end
            
            seglbs2compts_original = obj.m_seglbs2compts_assignment;
            
            seglist = obj.m_seglist;
            if ~isempty(seglist)
                % assign something to each in seglist
                for i=1:length(seglist)
                    seg = seglist{i};
                    seglbs = unique(seg.m_covered_seglbs);
                    idxcompts = seg.m_idxcompts;
                    if nnz(seglbs2compts_original(:,idxcompts))==0
                        if isempty(seglbs)
                            seglbs2compts_original(1,idxcompts)=1;
                        else
                            seglbs2compts_original(seglbs,idxcompts) = 1;
                        end
                    end
                end
                
                [seglbs2compts,seglist]= srec_complete_label_assignment(seglbs2compts_original,samplegraph,database,'seglist',seglist);
                seglist = srec_update_seglist_assignment(seglist,seglbs2compts,samplegraph);
                obj.m_seglist = seglist;
                obj.m_seglbs2compts_assignment = seglbs2compts;
            end
            
            seglist = obj.m_seglist_free3d;
            if ~isempty(seglist)
                for i=1:length(seglist)
                    seg = seglist{i};
                    seglbs = unique(seg.m_covered_seglbs);
                    idxcompts = seg.m_idxcompts;
                    if nnz(seglbs2compts_original(:,idxcompts))==0
                        if isempty(seglbs)
                            seglbs2compts_original(1,idxcompts)=1;
                        else
                            seglbs2compts_original(seglbs,idxcompts) = 1;
                        end
                    end
                end
                [seglbs2compts,seglist]= srec_complete_label_assignment(seglbs2compts_original,samplegraph,database,'seglist',seglist);
                seglist = srec_update_seglist_assignment(seglist,seglbs2compts,samplegraph);
                obj.m_seglist_free3d = seglist;
                obj.m_seglbs2compts_assignment = seglbs2compts;
            end
        end
        
        function val = get_symmetric_idxcompts(obj,idxcompts)
            % given idxcompts, find symmetric idxcompts, note that
            % idxcompts is like obj.idxcompts
            [~,loc] = ismember(idxcompts,obj.idxcompts);
            idxseg = obj.get_symmetric_idxseg(loc);
            val = zeros(1,length(idxseg));
            val(idxseg>0) = obj.idxcompts(idxseg(idxseg>0));
        end
        
        function val = get_symmetric_idxseg(obj,idxseg)
            % given index of components (ith is seglist{i}), find index of
            % symmetric components
            idxsympair = obj.idxsympair;
            if isempty(idxsympair)
                val = zeros(1,length(idxseg));
                return;
            end
            idxcompts_inobj = obj.idxcompts;
            
            symadjmat = accumarray(idxsympair',1,[length(idxcompts_inobj),length(idxcompts_inobj)])>0;
            symadjmat = symadjmat | symadjmat';
            assert(all(sum(symadjmat,2)<2),'symmetry error');
            
            idx2sym = zeros(1,length(idxcompts_inobj));
            idx2sym(idxsympair(1,:)) = idxsympair(2,:);
            idx2sym(idxsympair(2,:)) = idxsympair(1,:);
            val = idx2sym(idxseg);
        end
        
        function obj = realize(obj,samplegraph,database,varargin)
            pp = inputParser;
            pp.addParamValue('fullcover',true); %force segments to cover all pixels
            pp.addParamValue('snaptoconnpts',false); %snap to convex curvature connpts
            pp.addParamValue('seglist',[],@(x)isempty(x)||length(x)==length(obj.idxcompts));%if you have the realized seglist, give it here to save time
            pp.parse(varargin{:});
            
            assert(obj.isconnected,'cannot realize unconnected solution');
            
            % default to zonly realization
            if isempty(obj.m_adjmat_comp2comp)
                return;
            end
            obj.m_seglbs_all = unique(samplegraph.m_seglbs_per_sample);
            obj.m_seglbs_all = obj.m_seglbs_all(:);
            
            seglist = pp.Results.seglist;
            if isempty(seglist)
                [seglist,adjmat] = srec_seglist_from_solution(obj,samplegraph,database,'fullcover',pp.Results.fullcover);
            else
                % good, you have seglist, check the completeness of seglist
                idxcompts_seglist = cellfun(@(x)x.m_idxcompts,seglist);
                
                % sort it
                [tf,loc] = ismember(obj.idxcompts,idxcompts_seglist);
                assert(all(tf),'seglist is wrong');
                seglist = seglist(loc);
                idxcompts_seglist = idxcompts_seglist(loc);
                
                adjmat = obj.m_adjmat_comp2comp(idxcompts_seglist,idxcompts_seglist);
            end
            relpos = srec_relpos_by_connpts(seglist,adjmat,database.m_conngraph,...
                'snaptoconnpts',pp.Results.snaptoconnpts);
            if ~isempty(obj.m_sympair_idxcompts)
                idxcompts = cellfun(@(x)x.m_idxcompts,seglist);
                [~,idxnodes_sym] = ismember(obj.m_sympair_idxcompts,idxcompts);
                p0 = samplegraph.m_symplane_p0;
                plnormal = samplegraph.m_symplane_normal;
                [~,idxseg,idxelem] = srec_symplane_obj_from_seglist(seglist,p0,plnormal);
                if ~isempty(idxseg)
                    symplane = [idxseg,idxelem];
                else
                    symplane = plnormal;
                end
                seglist = srec_place_seglist_by_relposdata(seglist,relpos.m_relpos_imgc_zonly,...
                    'symplane',symplane,'idxsympair',idxnodes_sym);
                seglist_free3d = srec_place_seglist_by_relposdata(seglist,relpos.m_relpos_imgc,...
                    'symplane',symplane,'idxsympair',idxnodes_sym);
            else
                seglist = srec_place_seglist_by_relposdata(seglist,relpos.m_relpos_imgc_zonly);
                seglist_free3d = srec_place_seglist_by_relposdata(seglist,relpos.m_relpos_imgc);
            end
            sublist = srec_substruct_from_segraph(seglist,adjmat,relpos);
            obj.m_seglist = seglist;
            obj.m_seglist_free3d = seglist_free3d;
            obj.m_relpos = relpos;
            obj.m_adjmat_seg2seg = adjmat;
            obj.m_subobjlist = sublist;
            obj.m_lbmap = srec_label_pixel_by_solution(obj,samplegraph);
            
            if ~isempty(obj.m_subcoverlist)
                obj.m_subcoverlist = cellfun(@(x)SBSRSUBSTRUCTCOVER.init_with_essential(x,samplegraph,database),...
                    obj.m_subcoverlist,'uniformoutput',false);
            end
            
            if ~isempty(obj.m_sublabelist)
                obj.m_sublabelist = cellfun(@(x)SBSRSUBSTRUCTLABEL.init_with_essential(x,samplegraph,database),...
                    obj.m_sublabelist,'uniformoutput',false);
            end            
        end
        
        function obj = unrealize(obj)
            obj.m_seglist = [];
            obj.m_seglist_free3d = [];
            obj.m_relpos = [];
            obj.m_adjmat_seg2seg = [];
            obj.m_subobjlist = [];
            
            if ~isempty(obj.m_scoredata) && ~isempty(obj.m_scoredata.m_evalconfig)
                obj.m_scoredata.m_evalconfig.m_sol = [];
            end
            
            if ~isempty(obj.m_subcoverlist)
                srec_clear_non_essential(obj.m_subcoverlist);
            end
            
            if ~isempty(obj.m_sublabelist)
                srec_clear_non_essential(obj.m_sublabelist);
            end
        end
        
        function obj = match_substruct(obj,dbsublist)
            assert(~isempty(obj.m_subobjlist));
            subcovermat = srec_substruct_match_slow(obj.m_subobjlist,dbsublist);
            
            % take the best subcover as the selected subcover
            scoremat = cellfun(@(x)x.matchscore,subcovermat);
            [~,idxmax] = max(scoremat,[],2);
            is = 1:size(subcovermat,1);
            js = idxmax;
            idx = sub2ind(size(scoremat),is(:),js(:));
            subcoverlist = subcovermat(idx);
            seglbslist = cellfun(@(x)x.get_seglbs_for_sketch,obj.m_subobjlist,'uniformoutput',false);
            sublabelist = cellfun(@(x,y)SBSRSUBSTRUCTLABEL.init_with_subcover(x,y),subcoverlist(:),seglbslist(:),...
                'uniformoutput',false);
            
            obj.m_subcoverlist = subcoverlist(:)';
            obj.m_sublabelist = sublabelist(:)';
        end
        
        % get connection points in global coordinate
        % connptsfrom(:,i) connects to connptsto(:,i) for segments
        % whose indices are idxsegpair(:,i)
        function [connptsfrom,connptsto,idxsegpair] = get_connpts_global(obj)
            [connptsfrom,connptsto,idxsegpair] = srec_connpts_from_segraph(...
                obj.m_seglist,obj.m_adjmat_seg2seg,obj.m_relpos);
        end
        
        function [connptsfrom,connptsto,idxsegpair] = get_connpts_local(obj)
            adjmat = obj.m_adjmat_seg2seg;
            [is,js] = find(tril(adjmat));
            connptsfrom = zeros(3,length(is));
            connptsto = zeros(3,length(is));
            idxsegpair = [is(:) js(:)]';
            for i=1:length(is)
                idxfrom = is(i);
                idxto = js(i);
                thisconnptsfrom = relpos.m_connptsfrom_imgc_zonly(idxfrom,idxto,:);
                thisconnptsto = relpos.m_connptsto_imgc_zonly(idxfrom,idxto,:);
                connptsfrom(:,i) = thisconnptsfrom;
                connptsto(:,i) = thisconnptsto;
            end
        end
        
        function obj = reconnect(obj,samplegraph,keep_current)
            if nargin<=2
                % respect current connection?
                keep_current = false;
            end
            
            % reconnect all components
            idxcompts = obj.idxcompts;
            ncomp = length(idxcompts);
            if ~keep_current
                adjmat = srec_connect_components(obj.idxcompts,false(ncomp),samplegraph,...
                    'idxcompts_sym',obj.m_sympair_idxcompts);
            else
                adjmat = srec_connect_components(obj.idxcompts,obj.adjmat,samplegraph,...
                    'idxcompts_sym',obj.m_sympair_idxcompts);
            end
            adjmat_full = false(size(samplegraph.m_adjmat_comp2comp));
            adjmat_full(idxcompts,idxcompts) = adjmat;
            obj.m_adjmat_comp2comp = adjmat_full;
        end
        
        % remove obj.idxcompts(idx)
        function obj = remove_compts(obj,idxcompts_remove,includesym)
            if nargin <= 2
                includesym = false;
            end
            
            if isempty(idxcompts_remove)
                return;
            end
            
            if includesym
                idxcompts_sympair = obj.m_sympair_idxcompts;
                tf = ismember(idxcompts_remove,idxcompts_sympair);
                maskcol = any(tf,1);
                idxcompts_remove_sym = idxcompts_sympair(:,maskcol);
                idxcompts_remove_sym = idxcompts_remove_sym(:)';
                
                idxcompts_remove = cat(2,idxcompts_remove(:)',idxcompts_remove_sym(:)');
                idxcompts_remove = unique(idxcompts_remove);
            end
            
            % remove from src
            tf = ismember(obj.m_idxcompts_src,idxcompts_remove);
            obj.m_idxcompts_src = obj.m_idxcompts_src(~tf);
            
            % remove from seq
            tf = ismember(obj.m_idxcompts_seq,idxcompts_remove);
            obj.m_idxcompts_seq = obj.m_idxcompts_seq(~tf);
            
            % remove from sym
            if ~isempty(obj.m_sympair_idxcompts)
                tf = ismember(obj.m_sympair_idxcompts,idxcompts_remove);
                tf = any(tf,1);
                tmp = obj.m_sympair_idxcompts(:,tf);
                obj.m_sympair_idxcompts = obj.m_sympair_idxcompts(:,~tf);
                tmp = unique(tmp(:)');
                tmp = setdiff(tmp,idxcompts_remove);
                tmp = setdiff(tmp,obj.m_idxcompts_seq);
                tmp = setdiff(tmp,obj.m_idxcompts_src);
                obj.m_idxcompts_seq = cat(2,obj.m_idxcompts_seq,tmp(:)');
            end
            
            % remove from adjmat
            obj.m_adjmat_comp2comp(idxcompts_remove,:) = 0;
            obj.m_adjmat_comp2comp(:,idxcompts_remove) = 0;
        end
    end
    methods(Static=true)
        function obj = init_with_idxcompts(idxcompts,samplegraph,varargin)
            pp = inputParser;
            
            % specify adjacency matrix for the idxcompts
            pp.addParamValue('adjmat',[]);
            
            % automatically add symmetric points or not
            pp.addParamValue('usesym',false);
            
            pp.parse(varargin{:});
            
            obj = SBSRSOLUTION;
            adjmat = pp.Results.adjmat;
            if isempty(adjmat)
                adjmat = false(length(idxcompts));
            end
            
            if pp.Results.usesym
                idxcompts_sym = samplegraph.get_symmetric_idxcompts(idxcompts);
                maskhavesym = idxcompts_sym~=0;
                sympairs = cat(1,idxcompts(:)',idxcompts_sym(:)');
                sympairs = sympairs(:,maskhavesym);
                sympairs = sort(sympairs,1);
                sympairs = unique(sympairs','rows')';
                
                obj.m_sympair_idxcompts = sympairs;
                idxcompts_add = setdiff(sympairs(:)',idxcompts(:)');
                idxcompts_all = cat(2,idxcompts(:)',idxcompts_add(:)');
                
                adjmat_all = adjmat;
                adjmat_all(end+length(idxcompts_add),end+length(idxcompts_add)) =0;
                adjmat_all_update = srec_connect_components(idxcompts_all,adjmat_all,...
                    samplegraph,'idxcompts_sym',sympairs);
            else
                idxcompts_all = idxcompts;
                adjmat_all = adjmat;
                adjmat_all_update = srec_connect_components(idxcompts_all,adjmat_all,samplegraph);
            end
            
            adjmat_full = false(size(samplegraph.m_compts,2));
            adjmat_full(idxcompts_all,idxcompts_all) = adjmat_all_update;
            obj.m_adjmat_comp2comp = adjmat_full;
            obj.m_idxcompts_seq = idxcompts_all;
            obj.m_idxcompts_src = idxcompts_all(1);
        end
    end
    
end

