--  Copyright (2008-2013) Cédric Coussinet (cedric.coussinet@nomoseed.net)
--
--  This program is free software: you can redistribute it and/or modify
--  it under the terms of the GNU Affero General Public License as published
--  by the Free Software Foundation, either version 3 of the License, or
--  (at your option) any later version.
--
--  This program is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
--  GNU Affero General Public License for more details.

--  You should have received a copy of the GNU Affero General Public License
--  along with this program. If not, see <http://www.gnu.org/licenses/>

with Nomo.Interpreter.Plant.Chunks_Memory;

with Nomo.Interpreter.Plant.Pointers_Memory;

with Nomo.Interpreter.Plant.Error;

with Nomo.Interpreter.Plant.Errors_Manager;

package body Nomo.Interpreter.Plant.Engine is

   use Errors_Manager;

   type Transition is (Affectation,
                       Inhibitory_Premise,
                       Excitatory_Premise,
                       Finalization,
                       Standby);

   type Start_Symbol is (Null_Start,
                         Start_Affectation,
                         Start_Inhibitory_Premise,
                         Start_Excitatory_Premise,
                         Start_Finalization);

   Current_Transition : Transition := Standby;
   Current_Start_Symbol : Start_Symbol := Null_Start;
   Event : Boolean := False;

   procedure Cancel;

   procedure Assume_Affectation (T : in Positive_Time);
   procedure Assume_Finalization (T : in Positive_Time);
   procedure Assume_Excitatory_Premise (T : in Positive_Time);
   procedure Assume_Inhibitory_Premise (T : in Positive_Time);
   procedure Assume_Standby (T : in Positive_Time);

   procedure Assume (T : in Positive_Time) is
   begin
      Errors_Manager.Update_Sending_Time (T);
      if Event then
         case Current_Transition is
            when Affectation =>
               Assume_Affectation (T);
            when Finalization =>
               Assume_Finalization (T);
            when Excitatory_Premise =>
               Assume_Excitatory_Premise (T);
            when Inhibitory_Premise =>
               Assume_Inhibitory_Premise (T);
            when Standby =>
               Assume_Standby (T);
         end case;
         Current_Start_Symbol := Null_Start;
         Event := False;
      end if;
   exception
      when Error.PARSER_FAIL =>
         Cancel;
   end Assume;

   procedure Assume_Affectation (T : in Positive_Time) is
   begin
      case Current_Start_Symbol is
         when Start_Inhibitory_Premise =>
            Chunks_Memory.End_Affectations;
            Pointers_Memory.Update_Positions (T);
            Chunks_Memory.Send_In_Inhibitory_Condition;
            Current_Transition := Inhibitory_Premise;
         when Start_Excitatory_Premise =>
            Chunks_Memory.End_Affectations;
            Pointers_Memory.Update_Positions (T);
            Chunks_Memory.Send_In_Excitatory_Condition;
            Current_Transition := Excitatory_Premise;
         when Null_Start =>
            Pointers_Memory.Update_Positions (T);
            Chunks_Memory.Update_Affectations (T);
         when Start_Affectation | Start_Finalization =>
            Errors_Manager.Error (ILLEGAL_START_IN_AFFECTATION);
      end case;
   end Assume_Affectation;

   procedure Assume_Finalization (T : in Positive_Time) is
   begin
      case Current_Start_Symbol is
         when Start_Affectation =>
            Chunks_Memory.New_Affectations;
            Pointers_Memory.Update_Positions (T);
            Chunks_Memory.Update_Affectations (T);
            Current_Transition := Affectation;
         when Null_Start =>
            Pointers_Memory.Update_Positions (T);
            Chunks_Memory.Send_In_Conclusion;
         when Start_Finalization | Start_Excitatory_Premise | Start_Inhibitory_Premise =>
            Errors_Manager.Error (ILLEGAL_START_IN_CONCLUSION);
      end case;
   end Assume_Finalization;

   procedure Assume_Excitatory_Premise (T : in Positive_Time) is
   begin
      case Current_Start_Symbol is
         when Start_Finalization =>
            Pointers_Memory.Update_Positions (T);
            Chunks_Memory.Send_In_Conclusion;
            Current_Transition := Finalization;
         when Null_Start =>
            Pointers_Memory.Update_Positions (T);
            Chunks_Memory.Send_In_Excitatory_Condition;
         when Start_Affectation | Start_Excitatory_Premise | Start_Inhibitory_Premise =>
            Errors_Manager.Error (ILLEGAL_START_IN_EXCITATORY_PREMISE);
      end case;
   end Assume_Excitatory_Premise;

   procedure Assume_Inhibitory_Premise (T : in Positive_Time) is
   begin
      case Current_Start_Symbol is
         when Start_Excitatory_Premise =>
            Pointers_Memory.Update_Positions (T);
            Chunks_Memory.Send_In_Excitatory_Condition;
            Current_Transition := Excitatory_Premise;
         when Null_Start =>
            Pointers_Memory.Update_Positions (T);
            Chunks_Memory.Send_In_Inhibitory_Condition;
         when Start_Affectation | Start_Finalization | Start_Inhibitory_Premise =>
            Errors_Manager.Error (ILLEGAL_START_IN_INHIBITORY_PREMISE);
      end case;
   end Assume_Inhibitory_Premise;

   procedure Assume_Standby (T : in Positive_Time) is
   begin
      case Current_Start_Symbol is
         when Start_Affectation =>
            Pointers_Memory.Update_Positions (T);
            Chunks_Memory.New_Affectations;
            Chunks_Memory.Update_Affectations (T);
            Current_Transition := Affectation;
         when Null_Start =>
            Pointers_Memory.Update_Positions (T);
         when Start_Excitatory_Premise | Start_Finalization | Start_Inhibitory_Premise =>
            Errors_Manager.Error (ILLEGAL_START_IN_STANDBY);
      end case;
   end Assume_Standby;

   procedure Cancel is
   begin
      Current_Start_Symbol := Null_Start;
      Current_Transition := Standby;
      Event := False;
      Chunks_Memory.Cancel;
      Pointers_Memory.Cancel;
   end Cancel;

   protected body Put is

      procedure Operator (Index : in Chunk_Index;
                          Information : in Positive_Integer) is
      begin
         Chunks_Memory.Active_Chunk (Index, Information);
         Event := True;
      end Operator;

      procedure Pointer (Index       : in Chunk_Index;
                         Information : in Positive_Integer) is
      begin
         Pointers_Memory.Active_Pointer (Index, Information);
         Event := True;
      end Pointer;

      procedure Transition (Information : in Positive_Integer) is
      begin
         Current_Start_Symbol := Start_Symbol'Val (Information);
         Event := True;
      end Transition;

   end Put;

end Nomo.Interpreter.Plant.Engine;
