classdef map < handle
    
    properties (GetAccess = private)
        name
       % original_image
        original_segmented_image
        filtered_segmented_image
        color_table
        classes_array %each elemnt is a sea_class
    end
    
    methods
        
        function obj = map(name,  original_segmented_image, color_table)
            % constructor
            obj.name = name;
          %  obj.original_image = original_image;
            obj.original_segmented_image = original_segmented_image;
            obj.color_table = color_table;
            obj.classes_array = classes_array();
            valid_indices = unique(original_segmented_image);
                        
            % filtering:
            obj.filtered_segmented_image = original_segmented_image;
            close_se = strel('disk', 25);
            for i = 1:length(valid_indices)
                class_idx = valid_indices(i);
                blobs_of_the_same_label = (obj.filtered_segmented_image == class_idx);
                closed = imclose(blobs_of_the_same_label, close_se);
                obj.filtered_segmented_image(closed == 1) = class_idx;
            end
            
            disp('Done filtering')
            
            % construct segments array:
            for i = 1:length(valid_indices)
                class_idx = valid_indices(i);
                
                % if the class index is faulty - skip it
                if class_idx == 255
                    continue 
                end
                
                blobs_of_the_same_label = (obj.filtered_segmented_image == class_idx); % logical image
                objects_image = bwlabel(blobs_of_the_same_label); % image where '0' pixels are background, '1'ns are pixels of object1 and so on..
                measurements = regionprops(objects_image, 'Area', 'Centroid','PixelList');
                object_name = color_table.label(color_table.color_idx == class_idx);
                
                % create new sea_class:
                sea_clas = sea_class(object_name{1});
                
                % loop over each object j in the i'th class
               for j = 1:max(max(objects_image))
                   size = measurements(j).Area;
                   centroid = measurements(j).Centroid;
                   pixelist = measurements(j).PixelList;
                   segment = biotics(object_name{1}, obj, size, centroid, pixelist);
                   sea_clas.add_sea_segment(segment);
               end 
               obj.classes_array.add_sea_class(sea_clas);
            end  
        end
        
        
        %%%%%%%%%% set methods %%%%%%%%%%
        
        function set_name(obj, name)
            obj.name = name;
        end
                       
        
        %%%%%%%%%% get methods %%%%%%%%%%
        
        function name = get_name(obj)
            name = obj.name;
        end
        
        function original_image = get_original_image(obj)
            original_image = obj.original_image;
        end
        
        function segmented_image = get_segmented_image(obj)
            segmented_image = obj.original_segmented_image;
        end
        
        function filtered_segmented_image = get_filtered_segmented_image(obj)
            filtered_segmented_image = obj.filtered_segmented_image;
        end
        
        function filtered_segmented_image_colored = get_filtered_segmented_image_colored(obj)
            segmented = obj.get_filtered_segmented_image;
            height = size(segmented, 1);
            width = size(segmented, 2);
            R = zeros(height, width);
            G = zeros(height, width);
            B = zeros(height, width);
            T = obj.color_table;
            for i = 1:size(T, 1)
                color_idx = T{i, 'color_idx'};
                R(segmented == color_idx) = T{i, 'R'};
                G(segmented == color_idx) = T{i, 'G'};
                B(segmented == color_idx) = T{i, 'B'};
            end
            filtered_segmented_image_colored(:,:,1) = uint8(R);
            filtered_segmented_image_colored(:,:,2) = uint8(G);
            filtered_segmented_image_colored(:,:,3) = uint8(B);
        end

        function original_segmented_image_colored = get_original_segmented_image_colored(obj)
            segmented = obj.get_segmented_image;
            height = size(segmented, 1);
            width = size(segmented, 2);
            R = zeros(height, width);
            G = zeros(height, width);
            B = zeros(height, width);
            T = obj.color_table;
            for i = 1:size(T, 1)
                color_idx = T{i, 'color_idx'};
                R(segmented == color_idx) = T{i, 'R'};
                G(segmented == color_idx) = T{i, 'G'};
                B(segmented == color_idx) = T{i, 'B'};
            end
            original_segmented_image_colored(:,:,1) = uint8(R);
            original_segmented_image_colored(:,:,2) = uint8(G);
            original_segmented_image_colored(:,:,3) = uint8(B);
        end

        function color_table = get_color_table(obj)
            color_table = obj.color_table;
        end

        function classes_array = get_classes_array(obj)
            classes_array = obj.classes_array;
        end
    
        function amount = get_amount_from_class(obj, name)
            cls_arr = obj.classes_array;
            amount = cls_arr.get_amount_of_objects_from_class(name);
        end
        
        function amount = get_total_amount_of_objects(obj)
            cls_arr = obj.classes_array;
            amount = cls_arr.get_total_amount_of_objects;
        end
        
        function class_names = get_classes_names(obj)
            class_names = obj.classes_array.get_classes_names;
        end

        function [pixels, centroids] = get_locals(obj, name)
            centroids = [];
            pixels = [];
            classes_arr = obj.get_classes_array;
            if strcmp(name, 'all')
                % return pixels and centroids of all classes:
                names = obj.get_classes_names;
                for i = 1:length(names)
                    name = names{i};
                    sea_class = classes_arr.get_sea_class(name);
                    segments = sea_class.get_segments_list;
                    for j = 1:length(segments)
                        pixels = [pixels ; segments{j}.get_pixels];
                        centroids = [centroids ; segments{j}.get_centroid];
                    end
                end
            else
                % return centroids of the specific class:
                sea_class = classes_arr.get_sea_class(name);
                segments = sea_class.get_segments_list;
                for j = 1:length(segments)
                    pixels = [pixels ; segments{j}.get_pixels];
                    centroids = [centroids ; segments{j}.get_centroid];
                end
            end
        end
                
        function statistics_table = get_statistics(obj)
            names = obj.get_classes_names;
            
            % create empty columns for construction of a table
            Total_Amount = zeros(length(names), 1);
            Relative_Area = zeros(length(names), 1);
            Relative_Amount = zeros(length(names), 1);
            
            % create a table:
            statistics_table = table(Total_Amount, Relative_Area, ...
                Relative_Amount, 'RowNames', names);
            
            % updating column Total_Amount for each class:
            for i = 1:length(names)
                class_name = names{i};
                statistics_table{class_name, 'Total_Amount'} = ...
                    obj.get_amount_from_class(class_name);
            end

            % updating Relative_Area for each class:\
            classes_arr = obj.get_classes_array;
            segmented_image = obj.get_segmented_image;
            total_area = numel(segmented_image);
            for i = 1:length(names) %loop over classes
                class_name = names{i};
                class_area = 0;
                sea_class = classes_arr.get_sea_class(class_name);
                for j = 1:sea_class.get_number_of_segments %loop over segments
                    segments_list = sea_class.get_segments_list;
                    segment = segments_list{j};
                    class_area = class_area + segment.get_size;
                end
                relative_area = class_area / total_area;
                statistics_table{class_name, 'Relative_Area'} = relative_area;
            end
                                       
            % updating Relative_Amount for each class:
            total_amount_of_all = obj.get_total_amount_of_objects;
            for i = 1:size(statistics_table,1)
               statistics_table{i,'Relative_Amount'} = ...
                   statistics_table{i,'Total_Amount'} / total_amount_of_all;
            end   
        end    
        
        function stat_table = get_knn(obj, class_name, k)
            
            % create a table:
            class_names = obj.get_classes_names;
            stat_table = table('rowNames', class_names);
            stat_table{:,1} = 0;
            stat_table.Properties.VariableNames = {'Amount'};
            
            % for each name from "class_name", update the table using
            % his k-nn:
            cls_array = obj.classes_array;
            sea_cls = cls_array.get_sea_class(class_name);
            segments = sea_cls.get_segments_list;
            for i = 1:length(segments)
                k_nearest_seg = segments{i}.get_knn(k);
                for j = 1:length(k_nearest_seg)
                    stat_table{k_nearest_seg{j}.get_name,1} = ...
                        stat_table{k_nearest_seg{j}.get_name,1} + 1;
                end
            end
            stat_table = sortrows(stat_table, {'Amount'}, 'descend');
            stat_table = stat_table(1:k, :);
        end
        
        function class_table = get_class_satatistics(obj, class_name)
            classes_arr = obj.get_classes_array;
            class_arr = classes_arr.get_sea_class(class_name);
            segments_list = class_arr.get_segments_list;
            number_of_segments = length(segments_list);
            
            Size = zeros(number_of_segments,1);
            Centroid_x = zeros(number_of_segments,1);
            Centroid_y = zeros(number_of_segments,1);
            Name = cell(number_of_segments,1);
            Index = zeros(number_of_segments,1);
            
            for i = 1:number_of_segments
                segment = segments_list{i};
                centroid = segment.get_centroid;
                Size(i,1) = segment.get_size;
                Centroid_x(i,1) = centroid(1);
                Centroid_y(i,1) = centroid(2);
                Name{i,1} = segment.get_name; 
                Index(i,1) = i;
            end
            
            % create a table:
            class_table = table(Index, Name, Size, Centroid_x, Centroid_y);
            
        end
        
        function richness = get_richness(obj)
            richness = 0;
            stats_table = M.get_statistics(obj);
            for i = 1:size(stats_table,1)
                if stats_table{i,'Total_Amount'} > 0
                    richness = richness + 1;
                end
            end     
        end
    end
end


