------------------------------------------------------------------------------
--                                  Log4Ada                                 --
--                                                                          --
--                         Copyright (C) 2007-2009                          --
--                              X. Grave CNRS                               --
--                                                                          --
--  This library is free software; you can redistribute it and/or modify    --
--  it under the terms of the GNU General Public License as published by    --
--  the Free Software Foundation; either version 2 of the License, or (at   --
--  your option) any later version.                                         --
--                                                                          --
--  This library 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       --
--  General Public License for more details.                                --
--                                                                          --
--  You should have received a copy of the GNU General Public License       --
--  along with this library; if not, write to the Free Software Foundation, --
--  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.          --
--

with Ada.Unchecked_Deallocation;
with Ada.Text_IO;

package body Tcp_Receiver is

   use Log4ada.Appenders.Consoles;
   use Log4ada.Appenders.Files;

   type Receiver_Task;
   type Receiver_Access is access Receiver_Task;
   task type Receiver_Task is
      entry Start (Socket : GNAT.Sockets.Socket_Type;
                   Self : Receiver_Access);
   end Receiver_Task;

   procedure Free is new Ada.Unchecked_Deallocation (Receiver_Task,
                                                     Receiver_Access);

   task Tcp_Server is
      entry Start (Port : GNAT.Sockets.Port_Type);
      entry Stop;
   end Tcp_Server;

   -----------
   -- Start --
   -----------

   procedure Start (Port : GNAT.Sockets.Port_Type;
                    Name : String) is
   begin
      Logger_Shell.Setup (Name);
      Tcp_Server.Start (Port);
   end Start;

   ----------
   -- Stop --
   ----------

   procedure Stop is
   begin
      Logger_Shell.Stop_File_Recording;
      Logger_Shell.Stop_Output;
      Tcp_Server.Stop;
   end Stop;

   protected body Logger_Shell is
      procedure Setup (Name : String) is
         use Log4ada.Loggers;
      begin
         Set_Name (Logger'Access, Name);
         Set_Level (Logger'Access, Log4ada.All_Level);

         Add_Appender (Logger'Access, Console'Access);
         Add_Appender (Logger'Access, File'Access);

         Disable (Console'Access);
         Disable (File'Access);

         Setup_Done := True;
      end Setup;

      procedure Start_File_Recording (File_Name : String) is
      begin
         if File_Recording_On then
            return;
         end if;
         Open (File'Access,
               File_Name);
         Enable (File'Access);
         File_Recording_On := True;
      end Start_File_Recording;

      procedure File_Name_Refresh is
      begin
         if not File_Recording_On then
            return;
         end if;
         Refresh (File'Access);
      end File_Name_Refresh;

      procedure Stop_File_Recording is
      begin
         if not File_Recording_On then
            return;
         end if;
         Disable (File'Access);
         Close (File'Access);
         File_Recording_On := False;
      end Stop_File_Recording;

      procedure Start_Output is
      begin
         Enable (Console'Access);
      end Start_Output;

      procedure Stop_Output is
      begin
         Disable (Console'Access);
      end Stop_Output;

      procedure Put_Message (Event : Log4ada.Events.Event_Type) is
      begin
         Log4ada.Loggers.Event_Out (Logger'Access, Event);
      end Put_Message;
   end Logger_Shell;

   task body Receiver_Task is
      use GNAT.Sockets;
      Socket : Socket_Type;
      Self : Receiver_Access;
   begin
      select
         accept Start (Socket : GNAT.Sockets.Socket_Type;
                       Self : Receiver_Access) do
            Receiver_Task.Socket := Start.Socket;
            Receiver_Task.Self := Start.Self;
         end Start;
      or
         terminate;
      end select;
      loop
         declare
            Channel : GNAT.Sockets.Stream_Access := null;
            Event : Log4ada.Events.Event_Type;
         begin
            Channel := GNAT.Sockets.Stream (Socket);
            Event := Log4ada.Events.Event_Type'Input (Channel);
            Logger_Shell.Put_Message (Event);
            GNAT.Sockets.Free (Channel);
         exception
            when others =>
               if Channel /= null then
                  GNAT.Sockets.Free (Channel);
               end if;
               Close_Socket (Socket);
               Ada.Text_IO.Put_Line ("tcp receiver connection socket closed");
               exit;
         end;
      end loop;
      Free (Self);
   end Receiver_Task;

   task body Tcp_Server is
      use Gnat.Sockets;
      Network_Address : Sock_Addr_Type;
      Server : Socket_Type;
      Socket : Socket_Type;
      Request : Request_Type := (Non_Blocking_IO, True);
      New_Task : Receiver_Access;
   begin
      select
         accept Start (Port : GNAT.Sockets.Port_Type) do
            Network_Address.Addr := Addresses (Get_Host_By_Name ("localhost"),
                                               1);
            Network_Address.Port := Port;
            Create_Socket (Server);
            Set_Socket_Option
              (Server,
               Socket_Level,
               (Reuse_Address, True));
            Bind_Socket (Server, Network_Address);
            Listen_Socket (Server);
            Control_Socket (Server, Request);
         end Start;
      or
         terminate;
      end select;
      loop
         begin
            Accept_Socket (Server, Socket, Network_Address);
            New_Task := new Receiver_Task;
            New_Task.Start (Socket, New_Task);
         exception
            when Gnat.Sockets.Socket_Error =>
               select
                  accept Stop;
                  exit;
               or
                  delay 0.1;
               end select;
         end;
      end loop;
      Close_Socket (Server);
   end Tcp_Server;
end Tcp_Receiver;
