classdef EnvironmentBasis < rl.env.MATLABEnvironment
    %ENVIRONMENTBASIS: Template for defining custom environment in MATLAB.    
    
    %% Properties (set properties' attributes accordingly)
    properties

        eta             =       [];
        Batt_P          =       [];
        Batt_cap        =       [];
        PV_Peak         =       [];
        PV_gen_time     =       [];
        Load_time       =       [];
        Price_time      =       [];
        Compense_time   =       [];
        Faktor_time     =       [];
    end
    properties
% Initialize system state;
State = [0;0;0;1]
    end
    
    properties(Access = protected)
        % Initialize internal flag to indicate episode termination()
        IsDone = false;      
    end

    %% Necessary Methods
    methods              
        % Contructor method creates an instance of the environment
        % Change class name and constructor name accordingly


        function this = EnvironmentBasis()
%           Initialize Observation settings
%           ObservationInfo = rlFiniteSetSpec({[1 1 1],[1 1 2],[1 1 3],[1 2 1] [1 2 2],[1 2 3],[1 3 1],[1 3 2],[1 3 3],[2 1 1],[2 1 2],[2 1 3],[2 2 1],[2 2 2],[2 2 3],[2 3 1],[2 3 2], [2 3 3],[3 1 1],[3 1 2],[3 1 3],[3 2 1],[3 2 2],[3 2 3],[3 3 1],[3 3 2],[3 3 3]});
            ObservationInfo = rlNumericSpec([4 1]);
            ObservationInfo.Name = 'Observation';
            ObservationInfo.Description = 'load_PV_obs, batt_obs, Price_Compense_obs, timestep';
            
            % Initialize Action settings   
            ActionInfo = rlFiniteSetSpec([10 11 20 21 30 31 40 41 42 50 51 60 61 70 71 72]);
            ActionInfo.Name = 'Betriebsmodus';
            
            % The following line implements built-in functions of RL env
            this = this@rl.env.MATLABEnvironment(ObservationInfo,ActionInfo);
%             updateActionInfo(this); 
        end
        
        % Apply system dynamics and simulates the environment with the 
        % given action for one step.
        function [Observation,Reward,IsDone,LoggedSignals] = step(this,Action)
            
    % Werte aus Zeitreihen
    load                = this.Load_time(this.State(4));         % Last in kWh
    PV_gen              = this.PV_gen_time(this.State(4));       % PV Erzeugung in kWh
 
    % in diesem ersten Beispiel sind der Strompreis und die EEG-Vergütung
    % Konstant
    actual_price    = this.Price_time;
    actual_compense  = this.Compense_time;

    % Es wird eine sehr gute (optimale) Prognose angenommen. Daher werden
    % an den Agent die Lasten, PV-Einspeisung, Strompreise und Vergütungen
    % für die folge Stunde (für welche die Entschiedungen getroffen werden)
    % weiter gegeben

    if this.State(4) < length(this.PV_gen_time)
        t = this.State(4)+1;
        load_obs            = this.Load_time(t);
        PV_gen_obs          = this.PV_gen_time(t);
    else
        load_obs            = 0;
        PV_gen_obs          = 0;
    end
    actual_price_obs    = this.Price_time;
    actual_compens_obs  = this.Compense_time;
    
    Batt_stor = this.State(3)*this.Batt_cap;
% Algorithmen für die Betriebsmodi

% Modus 1.0
    if Action ==10
        %% Lastdeckung durch PV
        PV_Load = min(load,PV_gen);           
        load_PV = max([load-PV_Load 0]);                            % Verbleibende Last nach Lastdeckung durch PV
        PV_left = max(PV_gen-load,0);                               % Verbleibende PV Leistung nach Lastdeckung
        %% Lastdeckung durch den Speicher
        Batt_out = min([this.Batt_P Batt_stor load_PV]);            % Lastdeckung durch Batterie
        Batt_stor = Batt_stor - Batt_out;                           % Beladung nach der Lastdeckung durch Batterie
        load_batt = load_PV - Batt_out;                             % Verbleibende Last nach Lastdeckung durch Batterie
        %% Lastdeckung durch Netzbezug
        reward_grid_load = -load_batt*actual_price + 0;             % reward für den Netzbezug
        Grid_load = load_batt;                                      % Lastdeckung durch Netzbezug
        %% Beladen der Batterie mit PV-Strom
        temp1 = Batt_stor+(PV_left*this.eta);                       % Summe aus Speicherfbeladung vor der Beladung und dem Produkt aus Wirkungsgrad und verfügbarem PV-Strom
        temp2 = Batt_stor+this.Batt_P;                              % Summe aus Speicherbeladung vor der Beladung und der Maximalen Beladeleistung
        temp3 = PV_left*this.eta;
        temp4 = this.Batt_cap-Batt_stor;
        batt_in = min([temp3 this.Batt_P temp4]);                   % Beladung im Zeitschritt
        Batt_stor = min([this.Batt_cap temp1 temp2]);               % Speicherbeladung nach der Beladung
        PV_left = PV_left-(batt_in/this.eta);                       % Verbleibender PV-Strom nach Batteriebeladung
        %% Einspeisung PV-Strom ins Netz
        reward_grid_PV = PV_left*actual_compense;                   % Reward für den Netzbezug
        reward_Batt_load = 0;
        reward_Grid_batt = 0;
% Modus 1.1
    elseif Action == 11
        %% Lastdeckung durch PV
        PV_Load = min(load,PV_gen);           
        load_PV = max([load-PV_Load 0]);                            % Verbleibende Last nach Lastdeckung durch PV
        PV_left = max(PV_gen-load,0);                               % Verbleibende PV Leistung nach Lastdeckung
        %% Lastdeckung durch den Speicher
        Batt_out = min([this.Batt_P Batt_stor load_PV]);            % Lastdeckung durch Batterie
        Batt_stor = Batt_stor - Batt_out;                           % Beladung nach der Lastdeckung durch Batterie
        load_batt = load_PV - Batt_out;                             % Verbleibende Last nach Lastdeckung durch Batterie
        %% Lastdeckung durch Netzbezug
        reward_grid_load = -load_batt*actual_price + 0;             % reward für den Netzbezug
        %% Beladen der Batterie mit PV-Strom
        temp1 = Batt_stor+(PV_left*this.eta);                       % Summe aus Speicherfbeladung vor der Beladung und dem Produkt aus Wirkungsgrad und verfügbarem PV-Strom
        temp2 = Batt_stor+this.Batt_P;                              % Summe aus Speicherbeladung vor der Beladung und der Maximalen Beladeleistung
        temp3 = PV_left*this.eta;
        temp4 = this.Batt_cap-Batt_stor;
        batt_in = min([temp3 this.Batt_P temp4]);                   % Beladung im Zeitschritt
        Batt_stor = min([this.Batt_cap temp1 temp2]);               % Speicherbeladung nach der Beladung
        PV_left = PV_left-(batt_in/this.eta);                       % Verbleibender PV-Strom nach Batteriebeladung
        %% Einspeisung PV-Strom ins Netz
        reward_grid_PV = PV_left*actual_compense;                   % Reward für den Netzbezug
        temp1 = this.Batt_P-Batt_out-batt_in;
        temp2 = this.Batt_cap - Batt_stor;
        Batt_grid = min([temp1 temp2]);
        Batt_stor = Batt_stor+(this.eta*Batt_grid);
        reward_Batt_load = -Batt_grid*actual_price;
        reward_Grid_batt = 0;
% Modus 2.0
    elseif Action ==20
        %% Lastdeckung durch PV
        PV_Load = min(load,PV_gen);           
        load_PV = max([load-PV_Load 0]);                            % Verbleibende Last nach Lastdeckung durch PV
        PV_left = max(PV_gen-load,0);                               % Verbleibende PV Leistung nach Lastdeckung
        %% Lastdeckung durch den Speicher
        Batt_out = min([this.Batt_P Batt_stor load_PV]);            % Lastdeckung durch Batterie
        Batt_stor = Batt_stor - Batt_out;                           % Beladung nach der Lastdeckung durch Batterie
        load_batt = load_PV - Batt_out;                             % Verbleibende Last nach Lastdeckung durch Batterie
        %% Lastdeckung durch Netzbezug
        reward_grid_load = -load_batt*actual_price + 0;             % reward für den Netzbezug
        %% Einspeisung PV-Strom ins Netz
        reward_grid_PV = PV_left*actual_compense;                   % Reward für den Netzbezug
        reward_Batt_load = 0;
        reward_Grid_batt = 0;
% Modus 2.1
    elseif Action ==21
        %% Lastdeckung durch PV
        PV_Load = min(load,PV_gen);           
        load_PV = max([load-PV_Load 0]);                            % Verbleibende Last nach Lastdeckung durch PV
        PV_left = max(PV_gen-load,0);                               % Verbleibende PV Leistung nach Lastdeckung
        %% Lastdeckung durch den Speicher
        Batt_out = min([this.Batt_P Batt_stor load_PV]);            % Lastdeckung durch Batterie
        Batt_stor = Batt_stor - Batt_out;                           % Beladung nach der Lastdeckung durch Batterie
        load_batt = load_PV - Batt_out;                             % Verbleibende Last nach Lastdeckung durch Batterie
        %% Lastdeckung durch Netzbezug
        reward_grid_load = -load_batt*actual_price + 0;             % reward für den Netzbezug
        %% Einspeisung PV-Strom ins Netz
        reward_grid_PV = PV_left*actual_compense;                   % Reward für den Netzbezug
        temp1 = this.Batt_P-Batt_out;
        temp2 = Batt_stor;
        Grid_batt = min([temp1 temp2]);
        Batt_stor = Batt_stor - Grid_batt;
        reward_Grid_batt = Grid_batt*actual_compense;
        reward_Batt_load  = 0;


% Modus 3.0
    elseif Action == 30
        %% Lastdeckung durch PV
        PV_Load = min(load,PV_gen);           
        load_PV = max([load-PV_Load 0]);                                % Verbleibende Last nach Lastdeckung durch PV
        PV_left = max(PV_gen-load,0);                                   % Verbleibende PV Leistung nach Lastdeckung
        %% Lastdeckung durch Netzbezug
        reward_grid_load = -load_PV*actual_price + 0;                   % reward für den Netzbezug
        %% Beladen der Batterie mit PV-Strom
        temp1 = Batt_stor+(PV_left*this.eta);                           % Summe aus Speicherfbeladung vor der Beladung und dem Produkt aus Wirkungsgrad und verfügbarem PV-Strom
        temp2 = Batt_stor+this.Batt_P;                                  % Summe aus Speicherbeladung vor der Beladung und der Maximalen Beladeleistung
        batt_in = min([PV_left*this.eta this.Batt_P (this.Batt_cap-Batt_stor)]);    % Beladung im Zeitschritt
        Batt_stor = min([this.Batt_cap temp1 temp2]);                   % Speicherbeladung nach der Beladung
        PV_left = PV_left-(batt_in/this.eta);                           % Verbleibender PV-Strom nach Batteriebeladung
        %% Einspeisung PV-Strom ins Netz
        reward_grid_PV = PV_left*actual_compense;                       % Reward für den Netzbezug
        reward_Batt_load = 0;
        reward_Grid_batt = 0;
% Modus 3.1
    elseif Action == 31
        %% Lastdeckung durch PV
        PV_Load = min(load,PV_gen);           
        load_PV = max([load-PV_Load 0]);                                % Verbleibende Last nach Lastdeckung durch PV
        PV_left = max(PV_gen-load,0);                                   % Verbleibende PV Leistung nach Lastdeckung
        %% Lastdeckung durch Netzbezug
        reward_grid_load = -load_PV*actual_price + 0;                   % reward für den Netzbezug
        %% Beladen der Batterie mit PV-Strom
        temp1 = Batt_stor+(PV_left*this.eta);                           % Summe aus Speicherfbeladung vor der Beladung und dem Produkt aus Wirkungsgrad und verfügbarem PV-Strom
        temp2 = Batt_stor+this.Batt_P;                                  % Summe aus Speicherbeladung vor der Beladung und der Maximalen Beladeleistung
        batt_in = min([PV_left*this.eta this.Batt_P (this.Batt_cap-Batt_stor)]);    % Beladung im Zeitschritt
        Batt_stor = min([this.Batt_cap temp1 temp2]);                   % Speicherbeladung nach der Beladung
        PV_left = PV_left-(batt_in/this.eta);                           % Verbleibender PV-Strom nach Batteriebeladung
        %% Einspeisung PV-Strom ins Netz
        reward_grid_PV = PV_left*actual_compense;                       % Reward für den Netzbezug
        temp1 = this.Batt_P-batt_in;
        temp2 = this.Batt_cap - Batt_stor;
        Batt_grid = min([temp1 temp2]);
        Batt_stor = Batt_stor+(this.eta*Batt_grid);
        reward_Batt_load = -Batt_grid*actual_price;
        reward_Grid_batt = 0;
% Modus 4.0
    elseif Action == 40
        %% Lastdeckung durch PV
        PV_Load = min(load,PV_gen);           
        load_PV = max([load-PV_Load 0]);                    % Verbleibende Last nach Lastdeckung durch PV
        PV_left = max(PV_gen-load,0);                       % Verbleibende PV Leistung nach Lastdeckung
        %% Lastdeckung durch Netzbezug
        reward_grid_load = -load_PV*actual_price + 0;       % reward für den Netzbezug
        %% Einspeisung PV-Strom ins Netz
        reward_grid_PV = PV_left*actual_compense;           % Reward für den Netzbezug
        reward_Batt_load = 0;
        reward_Grid_batt = 0;
% Modus 4.1
    elseif Action == 41
        %% Lastdeckung durch PV
        PV_Load = min(load,PV_gen);           
        load_PV = max([load-PV_Load 0]);                    % Verbleibende Last nach Lastdeckung durch PV
        PV_left = max(PV_gen-load,0);                       % Verbleibende PV Leistung nach Lastdeckung
        %% Lastdeckung durch Netzbezug
        reward_grid_load = -load_PV*actual_price + 0;       % reward für den Netzbezug
        %% Einspeisung PV-Strom ins Netz
        reward_grid_PV = PV_left*actual_compense;           % Reward für den Netzbezug
        temp1 = this.Batt_P;
        temp2 = this.Batt_cap - Batt_stor;
        Batt_grid = min([temp1 temp2]);
        Batt_stor = Batt_stor+(this.eta*Batt_grid);
        reward_Batt_load = -Batt_grid*actual_price;
        reward_Grid_batt = 0;
% Modus 4.2
    elseif Action == 42
        %% Lastdeckung durch PV
        PV_Load = min(load,PV_gen);           
        load_PV = max([load-PV_Load 0]);                        % Verbleibende Last nach Lastdeckung durch PV
        PV_left = max(PV_gen-load,0);                           % Verbleibende PV Leistung nach Lastdeckung
        %% Lastdeckung durch Netzbezug
        reward_grid_load = -load_PV*actual_price + 0;           % Reward für den Netzbezug
        %% Einspeisung PV-Strom ins Netz
        reward_grid_PV = PV_left*actual_compense;               % Reward für den Netzbezug
        temp1 = this.Batt_P;
        temp2 = Batt_stor;
        Grid_batt = min([temp1 temp2]);
        Batt_stor = Batt_stor - Grid_batt;
        reward_Grid_batt = Grid_batt*actual_compense;
        reward_Batt_load  = 0;
% Modus 5.0
    elseif Action == 50
        %% Lastdeckung durch den Speicher
        Batt_out = min([this.Batt_P Batt_stor load]);   % Lastdeckung durch Batterie
        Batt_stor = Batt_stor - Batt_out;               % Beladung nach der Lastdeckung durch Batterie
        load_batt = load - Batt_out;                    % Verbleibende Last nach Lastdeckung durch Batterie
        %% Lastdeckung durch PV
        PV_Load = min(load,PV_gen);           
        load_PV = max([load-PV_Load 0]);                % Verbleibende Last nach Lastdeckung durch PV
        PV_left = max(PV_gen-load_batt,0);              % Verbleibende PV Leistung nach Lastdeckung
        %% Lastdeckung durch Netzbezug
        reward_grid_load = -load_PV*actual_price + 0;   % reward für den Netzbezug
        %% Einspeisung PV-Strom ins Netz
        reward_grid_PV = PV_left*actual_compense;       % Reward für den Netzbezug
        reward_Batt_load = 0;
        reward_Grid_batt = 0;
% Modus 5.1
    elseif Action == 51
        %% Lastdeckung durch den Speicher
        Batt_out = min([this.Batt_P Batt_stor load]);   % Lastdeckung durch Batterie
        Batt_stor = Batt_stor - Batt_out;               % Beladung nach der Lastdeckung durch Batterie
        load_batt = load - Batt_out;                    % Verbleibende Last nach Lastdeckung durch Batterie
        %% Lastdeckung durch PV
        PV_Load = min(load,PV_gen);           
        load_PV = max([load-PV_Load 0]);                    % Verbleibende Last nach Lastdeckung durch PV
        PV_left = max(PV_gen-load_batt,0);              % Verbleibende PV Leistung nach Lastdeckung
        %% Lastdeckung durch Netzbezug
        reward_grid_load = -load_PV*actual_price + 0;   % reward für den Netzbezug
        %% Einspeisung PV-Strom ins Netz
        reward_grid_PV = PV_left*actual_compense;       % Reward für den Netzbezug
        temp1 = this.Batt_P-Batt_out;
        temp2 = Batt_stor;
        Grid_batt = min([temp1 temp2]);
        Batt_stor = Batt_stor - Grid_batt;
        reward_Grid_batt = Grid_batt*actual_compense;
        reward_Batt_load  = 0;
% Modus 6.0
    elseif Action == 60
        %% Lastdeckung durch den Speicher
        Batt_out = min([this.Batt_P Batt_stor load]);       % Lastdeckung durch Batterie
        Batt_stor = Batt_stor - Batt_out;                   % Beladung nach der Lastdeckung durch Batterie
        load_batt = load - Batt_out;                        % Verbleibende Last nach Lastdeckung durch Batterie
        %% Lastdeckung durch Netzbezug
        reward_grid_load = -load_batt*actual_price + 0;     % reward für den Netzbezug
        %% Einspeisung PV-Strom ins Netz
        reward_grid_PV = PV_gen * actual_compense;          % Reward für den Netzbezug
        reward_Batt_load = 0;
        reward_Grid_batt = 0;
% Modus 6.1
    elseif Action == 61
        %% Lastdeckung durch den Speicher
        Batt_out = min([this.Batt_P Batt_stor load]);       % Lastdeckung durch Batterie
        Batt_stor = Batt_stor - Batt_out;                   % Beladung nach der Lastdeckung durch Batterie
        load_batt = load - Batt_out;                        % Verbleibende Last nach Lastdeckung durch Batterie
        %% Lastdeckung durch Netzbezug
        reward_grid_load = -load_batt*actual_price + 0;     % reward für den Netzbezug
        %% Einspeisung PV-Strom ins Netz
        reward_grid_PV = PV_gen * actual_compense;          % Reward für den Netzbezug
        temp1 = this.Batt_P-Batt_out;
        temp2 = Batt_stor;
        Grid_batt = min([temp1 temp2]);
        Batt_stor = Batt_stor - Grid_batt;
        reward_Grid_batt = Grid_batt*actual_compense;
        reward_Batt_load  = 0;
% Modus 7.0
    elseif Action == 70
        %% Lastdeckung durch Netzbezug
        reward_grid_load = -load*actual_price + 0;          % reward für den Netzbezug
%         Grid_load = load;                                 % Lastdeckung durch Netzbezug
%         load = 0;
        %% Einspeisung PV-Strom ins Netz
        reward_grid_PV = PV_gen * actual_compense;          % Reward für den Netzbezug
        reward_Batt_load = 0;
        reward_Grid_batt = 0;
% Modus 7.1
    elseif Action == 71
        %% Lastdeckung durch Netzbezug
        reward_grid_load = -load*actual_price + 0;          % reward für den Netzbezug
        %% Einspeisung PV-Strom ins Netz
        reward_grid_PV = PV_gen * actual_compense;          % Reward für den Netzbezug
        temp1 = this.Batt_P;
        temp2 = this.Batt_cap - Batt_stor;
        Batt_grid = min([temp1 temp2]);
        Batt_stor = Batt_stor+(this.eta*Batt_grid);
        reward_Batt_load = -Batt_grid*actual_price;
        reward_Grid_batt = 0;

% Modus 7.2
    elseif Action == 72
        %% Lastdeckung durch Netzbezug
        reward_grid_load = -load*actual_price + 0;          % reward für den Netzbezug
        %% Einspeisung PV-Strom ins Netz
        reward_grid_PV = PV_gen * actual_compense;          % Reward für den Netzbezug
        temp1 = this.Batt_P;
        temp2 = Batt_stor;
        Grid_batt = min([temp1 temp2]);
        Batt_stor = Batt_stor - Grid_batt;
        reward_Grid_batt = Grid_batt*actual_compense;
        reward_Batt_load  = 0;
    else
    end
% Diskretisierung
% Option 1
% Es wird eine obere und untere Grenze festgelegt . Zustände oberhalb der oberen Grenze werden als Hoch(1), zwischen den Grenzen als Mittel(2) und unter der unteren Grenze als Niedrig(3) an den Agent übergeben
    %% Last und Erzeugung

    load_PV_obs = load_obs/(PV_gen_obs+load_obs); % Später kann hier eine fixe Höchstlast angenommen werden
%     if temp > this.load_PV_high
%         load_PV_obs = 1;
%     elseif temp <= this.load_PV_high &&  temp >= this.load_PV_low
%         load_PV_obs = 2;
%     else
%         load_PV_obs = 3;
%     end


    %% Speicherbeladung

    batt_obs = Batt_stor/this.Batt_cap;
%     if temp > this.batt_high
%         batt_obs = 1;
%     elseif temp <= this.batt_high &&  temp >= this.batt_low
%         batt_obs = 2;
%     else
%         batt_obs = 3;
%     end

    %% Strompreis und EEG

    Price_Compense_obs = actual_price_obs/(actual_price_obs+actual_compens_obs);
%     if temp > this.Price_Compense_high
%         Price_Compense_obs = 1;
%     elseif temp <= this.Price_Compense_high &&  temp >= this.Price_Compense_low
%         Price_Compense_obs = 2;
%     else
%         Price_Compense_obs = 3;
%     end
            
            LoggedSignals = [];
            if this.State(4) <= length(this.PV_gen_time)
            this.State(4) = this.State(4)+1;
            else
            end
            % Erzeuge das Observation Array

            Observation = [load_PV_obs;batt_obs;Price_Compense_obs;this.State(4)];
            % Update system states
            this.State = Observation;
            Reward = reward_Batt_load + reward_Grid_batt + reward_grid_load + reward_grid_PV;
            

            length(this.PV_gen_time)

            IsDone = this.State(4) == 4;
            this.IsDone = IsDone;
            notifyEnvUpdated(this);
        end
        
        % Reset environment to initial state and output initial observation

        function InitialObservation = reset(this)
        Load_PV_obs0 = 0;
        batt_obs0 = 0;
        Price_Compense_obs0 = 0;
        timestep0  = 1;
    
            InitialObservation = [Load_PV_obs0;batt_obs0;Price_Compense_obs0;timestep0];
            this.State = InitialObservation;
            

            notifyEnvUpdated(this);
        end


    end
  
    methods (Access = protected)
        % (optional) update visualization everytime the environment is updated 
        % (notifyEnvUpdated is called)
        function envUpdatedCallback(this)
        end
    end
end
