classdef TopoART < handle
    properties (Access = protected)
        shortName
        longName
    end

    properties
        LibTopoART_version {mustBeNumeric}
        taFileFormatVersion {mustBeNumeric}
        x_F0_len {mustBeInteger, mustBePositive}        
        x_F0
        rho_a {mustBeNumeric}
        beta_sbm {mustBeNumeric}
        tau {mustBeInteger, mustBePositive}
        phi {mustBeInteger}
        phis
        learningSteps {mustBeInteger, mustBeNonnegative}
        alpha {mustBeNumeric}
        moduleNum {mustBeInteger, mustBePositive}
        modules
    end

    methods (Access = protected)
        function colorIndex = GetClusterColor(ta, moduleIndex, nodeIndex)
            arguments
                ta TopoART
                moduleIndex {mustBeInteger, mustBePositive}
                nodeIndex {mustBeInteger, mustBePositive}
            end

            colorIndex = ta.modules(moduleIndex).nodes(nodeIndex).clusterID;
        end

        function colorNum = GetClusterColorNum(ta, moduleIndex)
            arguments
                ta TopoART
                moduleIndex {mustBeInteger, mustBePositive}
            end

            colorNum = max(ta.modules(moduleIndex).clusterNum, 2);
        end

        function color = GetColor(ta, colors, colorIndex, colorNum)
            arguments
                ta TopoART
                colors
                colorIndex {mustBeInteger, mustBeNonnegative}
                colorNum {mustBeInteger, mustBePositive}
            end

            color = colors(round((colorIndex * (length(colors) - 1)) / colorNum), :);
        end

        function suffix = GetTitleSuffix(ta)
            arguments
                ta TopoART
            end
            
            suffix = '';
        end 

        function [off1, off2] = GetWeightsOffsets(ta)
            arguments
                ta TopoART
            end

            off1 = 0;
            off2 = 0;
        end

        function Init(ta)
            ta.shortName = 'TA';
            ta.longName = 'TopoART';
        end

        function file = OpenFile(ta, path)
            arguments
                ta TopoART
                path char {mustBeFile}
            end

            disp(['Load ' ta.longName ' network: ' path])

            file = fopen(path,'r');
            assert(file ~= -1, 'File not found')

            fgetl(file);
            l = fgetl(file);
            assert(strcmp(l, '*        TopoART network        *') ~= 0, 'No TopoART text file')    
            l = fgetl(file);
            ta.LibTopoART_version = sscanf(l(8:length(l)),'LibTopoART (v%f)');
            fprintf("LibTopoART v%.2f\n", ta.LibTopoART_version);
            fgetl(file);

            l = fgetl(file);
            ta.taFileFormatVersion = sscanf(l,'file format version: %f');
            assert(ta.taFileFormatVersion == 0.10 || ta.taFileFormatVersion == 0.11 || ...
                ta.taFileFormatVersion == 1.00, 'File format version not supported')
        end

        function PlotModule(ta, moduleIndex, colors, off1, off2, figureTitleModuleSuffix, figureTitleSuffix, fileNameSuffix, colorIndexFunc, colorNumFunc)
            arguments
                ta TopoART
                moduleIndex {mustBeInteger, mustBeNonnegative}
                colors
                off1 {mustBeInteger, mustBeNonnegative} = 0
                off2 {mustBeInteger, mustBeNonnegative} = 0
                figureTitleModuleSuffix = ''
                figureTitleSuffix = ''
                fileNameSuffix = ''
                colorIndexFunc = @ta.GetClusterColor
                colorNumFunc = @ta.GetClusterColorNum
            end

            [inputOff1, inputOff2] = ta.GetWeightsOffsets();

            assert(ta.x_F0_len == 2 || ((inputOff1 == 2 && inputOff1 == 2) && ((off1 == inputOff1 && off2 == 0) || ...
                (off1 == 0 && off2 == inputOff2))), 'Incompatible input length')

            letters = 'a':'z';

            basePath = fileparts(mfilename('fullpath'));

            f = figure('Name', [ta.longName ' module ' letters(moduleIndex) figureTitleSuffix]);
            f.Position(3:4) = [500 500];
            hold on;

            axis([0 1 0 1]);
            set(gca, 'FontSize', 12.0);
            set(gca, 'XTick', 0:0.1:1);
            set(gca,'XTickLabel', {0, [], 0.2, [], 0.4, [], 0.6, [], 0.8, [], 1});
            set(gca, 'YTick', 0:0.1:1);
            set(gca,'YTickLabel', {0, [], 0.2, [], 0.4, [], 0.6, [], 0.8, [], 1});
            set(gca,'DataAspectRatio', [1 1 1]);

            phiSubscript = ifelse(ta.phi ~= -1, '', ['_{' letters(moduleIndex) '}']);

            title([ta.shortName ' ' letters(moduleIndex) figureTitleModuleSuffix ': $\rho_' letters(moduleIndex) ' = ' num2str(ta.modules(moduleIndex).rho) ...
                '$, $\beta_{sbm} = ' num2str(ta.beta_sbm) '$, $\phi' phiSubscript ' = ' num2str(ta.phis(moduleIndex)) '$, $\tau = ' ... 
                num2str(ta.tau) '$' ta.GetTitleSuffix()], 'FontSize', 13.3, 'Interpreter', 'Latex', 'Position', [0.48 1.03 0]);

            grid on;

            for n = 1:ta.modules(moduleIndex).nodeNum

                colorIndex = colorIndexFunc(moduleIndex, n);

                if colorIndex < 0
                    continue
                end

                colorNum = colorNumFunc(moduleIndex);

                ta.PlotWeights(ta.modules(moduleIndex).nodes(n).weights, ta.GetColor(colors, colorIndex, colorNum), off1, off2);
            end

            f.Units = 'inches';
            set(f, 'PaperPositionMode', 'auto', 'PaperUnits', 'inches', 'PaperSize', f.Position(3:4) + 0.02)
            saveas(f, [basePath '/../images/' ta.shortName '_' letters(moduleIndex) fileNameSuffix '.pdf']);
        end

        function PlotWeights(ta, weights, color, off1, off2)
            arguments
                ta TopoART
                weights {mustBeVector}
                color
                off1 {mustBeInteger, mustBeNonnegative}
                off2 {mustBeInteger, mustBeNonnegative}
            end

            x1 = weights(1 + off1);
            y1 = weights(2 + off1);
            x2 = 1 - weights(3 + 2 * off1 + off2);
            y2 = 1 - weights(4 + 2 * off1 + off2);

            rectangle('Position',[x1, y1, max(x2 - x1, 0), max(y2 - y1, 0)], ...
                'EdgeColor', 'black', 'FaceColor', color);
        end

        function ReadAdditionalParams(ta, file)
            arguments
                ta TopoART
                file
            end
        end

        function ReadModules(ta, file)
            arguments
                ta TopoART
                file
            end

            for i = 1:ta.moduleNum
                readModules(i) = TopoARTModule(file, i, ta.longName);
            end
            ta.modules = readModules;
        end
    end

    methods
        function ta = TopoART(path)
            arguments
                path char {mustBeFile}
            end

            ta.Init();

            file = ta.OpenFile(path);
            fgetl(file);
            fgetl(file);
            fgetl(file);

            ta.ReadAdditionalParams(file)

            l = fgetl(file);
            ta.x_F0_len = sscanf(l, 'x^F0 length: %i');

            l = fgetl(file);
            ta.x_F0 = sscanf(l(6:length(l)), ' %f', ta.x_F0_len);

            l = fgetl(file);
            ta.rho_a = sscanf(l, 'rho_a: %f');
            l = fgetl(file);
            ta.beta_sbm = sscanf(l, 'beta_sbm: %f');
            l = fgetl(file);
            ta.tau = sscanf(l, 'tau: %i');
            l = fgetl(file);
            ta.phi = sscanf(l, 'phi: %i');
            l = fgetl(file);
            ta.learningSteps = sscanf(l, 'learning steps: %i');
            l = fgetl(file);
            ta.alpha = sscanf(l, 'alpha: %f');

            if ta.taFileFormatVersion >= 1.00 
                fgetl(file);
            end

            l = fgetl(file);
            ta.moduleNum = sscanf(l, 'module number: %i');

            if  ta.phi ~= -1
                ta.phis(1:ta.moduleNum) = ta.phi;
            else
                l = fgetl(file);
                ta.phis = sscanf(l(6:length(l)), ' %i', ta.moduleNum);
            end

            ta.ReadModules(file)

            fclose(file);
        end

        function PlotNetwork(ta, colors)
            arguments
                ta TopoART
                colors
            end

            for m = 1:ta.moduleNum
                ta.PlotModule(m, colors)
            end
        end
    end
end