with Ada.Containers.Vectors;
with Ada.Strings.Unbounded;
with Ada.Unchecked_Deallocation;

with Log4ada.Appenders.Files;
with Log4ada.Loggers;
with Logger.Environment;
with Logger.Appenders;

package body Soap_Logger is

   type File_Logging_Type is record
      Logger : Log4ada.Loggers.Logger_Access;
      File : Log4ada.Appenders.Files.File_Access;
   end record;

   package Logger_Vectors is new Ada.Containers.Vectors (Positive,
                                                         File_Logging_Type,
                                                         "=");

   procedure Free is new Ada.Unchecked_Deallocation
     (Log4ada.Appenders.Files.File_Type, Log4ada.Appenders.Files.File_Access);
   procedure Free is new Ada.Unchecked_Deallocation
     (Log4ada.Loggers.Logger_Type, Log4ada.Loggers.Logger_Access);

   protected Soap_Shell is
      procedure Create (Name_ID : String);
      procedure Close (Name_ID : String);
      procedure Append_Log (Name_ID : String;
                            Message : String;
                            Level : Log4ada.Level_Type);
   private
      File_Loggers : Logger_Vectors.Vector;
   end Soap_Shell;

   procedure Create_File_Log (Name_ID : String) is
   begin
      Logger.Appenders.Logger.Debug_Out ("create file log : " & Name_ID);
      Soap_Shell.Create (Name_ID);
   end Create_File_Log;

   procedure Close_File_Log (Name_ID : String) is
   begin
      Logger.Appenders.Logger.Debug_Out ("close file log : " & Name_ID);
      Soap_Shell.Close (Name_ID);
   end Close_File_Log;

   procedure Append_Log (Name_ID : String;
                         Message : String;
                         Level : Log4ada.Level_Type) is
   begin
      Logger.Appenders.Logger.Debug_Out ("append log : " & Name_ID);
      Soap_Shell.Append_Log (Name_ID,
                             Message,
                             Level);
   end Append_Log;

   ID_Not_Found : exception;
   ID_Already_Present : exception;

   protected body Soap_Shell is
      function Find (Name_ID : String) return Positive;
      function Find (Name_ID : String) return Positive is
         Index : Positive := 1;
      begin
         for File_Logging of File_Loggers loop
            if File_Logging.File.Get_Name = Name_ID then
               return Index;
            end if;
            Index := Index + 1;
         end loop;
         raise ID_Not_Found with Name_ID & ", ID not found";
      end Find;

      procedure Create (Name_ID : String) is
         Index : Positive;
         File_Logger : File_Logging_Type;
         use Ada.Strings.Unbounded;
      begin
         begin
            Index := Find (Name_ID);
            raise ID_Already_Present
              with Name_ID & " already present @" & Index'Img;
         exception
            when ID_Not_Found =>
               File_Logger.File := new Log4ada.Appenders.Files.File_Type;
               File_Logger.Logger := new Log4ada.Loggers.Logger_Type;
               File_Logger.File.Set_Name (Name_ID);
               File_Logger.Logger.Set_Name (Name_ID);
               File_Logger.Logger.Set_Level
                 (Logger.Appenders.Logger.Get_Level);
               File_Logger.File.Open
                 (To_String (Logger.Environment.Default_Path) &
                    "/" & Name_ID & ".log");
               File_Loggers.Append (File_Logger);
               File_Logger.Logger.Add_Appender
                 (Log4ada.Appenders.Appender_Class_Access (File_Logger.File));
         end;
      exception
         when E : others =>
            Logger.Appenders.Logger.Error_Out ("create error", E);
            raise;
      end Create;

      procedure Close (Name_ID : String) is
      begin
         declare
            Index : constant Positive := Find (Name_ID);
            File_Logger : File_Logging_Type :=
              File_Loggers.Element (Index);
         begin
            File_Logger.File.Close;
            Free (File_Logger.Logger);
            Free (File_Logger.File);
            File_Loggers.Delete (Index);
         end;
      exception
         when E : others =>
            Logger.Appenders.Logger.Error_Out ("close error", E);
            raise;
      end Close;

      procedure Append_Log (Name_ID : String;
                            Message : String;
                            Level : Log4ada.Level_Type) is
         Index : constant Positive := Find (Name_ID);
         File_Logger : File_Logging_Type :=
           File_Loggers.Element (Index);
      begin
         File_Logger.Logger.Logger_Output (Message, Level);
      end Append_Log;

   end Soap_Shell;

end Soap_Logger;
