classdef Calculator < handle & matlab.mixin.Copyable

    properties (Access = private)
        
        CalculatorDefaults     = struct('ChemicalPotential',                                       1, ...
                                        'EnergyComponents',                                        1, ...
                                        'NormalizedResiduals',                                     1, ...
                                        'OrderParameter',                                          1, ...
                                        'PhaseCoherence',                                          1, ...
                                        'TotalEnergy',                                             1, ...
                                        'CutOffType',                                  'Cylindrical', ...
                                        'CustomCylindricalCutOffRadius',                           1, ...
                                        'CustomCylindricalCutOffHeight',                           1);

    end

    properties (Access = public)
        ChemicalPotential;
        EnergyComponents;
        NormalizedResiduals;
        OrderParameter;
        PhaseCoherence;
        TotalEnergy;
        CutOffType;
        CustomCylindricalCutOffRadius
        CustomCylindricalCutOffHeight
    end
    
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %- Methods
    
    methods
        function this  = Calculator(varargin)
            
            p = inputParser;
            p.KeepUnmatched = true;
            addParameter(p, 'CutOffType', this.CalculatorDefaults.CutOffType,... 
                @(x) any(strcmpi(x,{'None', 'Cylindrical','CylindricalInfiniteZ', 'Spherical', 'CustomCylindrical'})));
            addParameter(p, 'CustomCylindricalCutOffRadius',     1,...
                @(x) assert(isnumeric(x) && isscalar(x) && (x > 0)));
            addParameter(p, 'CustomCylindricalCutOffHeight',     1,...
                @(x) assert(isnumeric(x) && isscalar(x) && (x > 0)));
            p.parse(varargin{:});
            
            this.ChemicalPotential             = this.CalculatorDefaults.ChemicalPotential;
            this.EnergyComponents              = this.CalculatorDefaults.EnergyComponents;
            this.NormalizedResiduals           = this.CalculatorDefaults.NormalizedResiduals;
            this.OrderParameter                = this.CalculatorDefaults.OrderParameter;
            this.PhaseCoherence                = this.CalculatorDefaults.PhaseCoherence;
            this.TotalEnergy                   = this.CalculatorDefaults.TotalEnergy;
            this.CutOffType                    = p.Results.CutOffType;
            this.CustomCylindricalCutOffRadius = p.Results.CustomCylindricalCutOffRadius;
            this.CustomCylindricalCutOffHeight = p.Results.CustomCylindricalCutOffHeight;
        end

        function restoreDefaults(this)
            this.ChemicalPotential             = this.CalculatorDefaults.ChemicalPotential;
            this.EnergyComponents              = this.CalculatorDefaults.EnergyComponents;
            this.NormalizedResiduals           = this.CalculatorDefaults.NormalizedResiduals;
            this.OrderParameter                = this.CalculatorDefaults.OrderParameter;
            this.PhaseCoherence                = this.CalculatorDefaults.PhaseCoherence;
            this.TotalEnergy                   = this.CalculatorDefaults.TotalEnergy;
            this.CutOffType                    = this.CalculatorDefaults.CutOffType;
            this.CustomCylindricalCutOffRadius = this.CalculatorDefaults.CustomCylindricalCutOffRadius;
            this.CustomCylindricalCutOffHeight = this.CalculatorDefaults.CustomCylindricalCutOffHeight;
        end
        
    end %

    % methods
        
    % end % - setters and getters
    
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %- Methods
    
    methods(Access = protected)
        function cp = copyElement(this)
            % Shallow copy object
            cp = copyElement@matlab.mixin.Copyable(this);
            
            % Forces the setter to redefine the function handles to the new copied object
            
            pl = properties(this);
            for k = 1:length(pl)
                sc = superclasses(this.(pl{k}));
                if any(contains(sc,{'matlab.mixin.Copyable'}))
                    cp.(pl{k}) = this.(pl{k}).copy();
                end
            end
        end
    end
    
    methods (Static)
        
        % Creates an Instance of Class, ensures singleton behaviour (that there
        % can only be one Instance of this class
        function singleObj = getInstance(varargin)
            % Creates an Instance of Class, ensures singleton behaviour
            persistent localObj;
            if isempty(localObj) || ~isvalid(localObj)
                localObj =  Simulator.Calculator(varargin{:});
            end
            singleObj = localObj;
        end
    end
    
end
