------------------------------------------------------------------------------
--                              Ada Web Server                              --
--                                                                          --
--                     Copyright (C) 2000-2014, AdaCore                     --
--                                                                          --
--  This library is free software;  you can redistribute it and/or modify   --
--  it under terms of the  GNU General Public License  as published by the  --
--  Free Software  Foundation;  either version 3,  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.                    --
--                                                                          --
--                                                                          --
--                                                                          --
--                                                                          --
--                                                                          --
--  You should have received a copy of the GNU General Public License and   --
--  a copy of the GCC Runtime Library Exception along with this program;    --
--  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see   --
--  <http://www.gnu.org/licenses/>.                                         --
--                                                                          --
--                                                                          --
--                                                                          --
--                                                                          --
--                                                                          --
--                                                                          --
--                                                                          --
------------------------------------------------------------------------------

--  This package handle the logging facility for AWS. The log file is named
--  '<progname>-Y-M-D.log' and is written by default in the directory where
--  the server is launched, see configuration file.
--
--  Note that this package is used internally by AWS to log server requests
--  but it can also be used by users to handle application's log.
--
--  This package is thread safe.

with AWS.Containers.String_Vectors;
with AWS.Headers;
with AWS.Messages;
with AWS.Response;
with AWS.Status;

private with Ada.Containers.Indefinite_Ordered_Maps;
private with Ada.Finalization;
private with Ada.Strings.Unbounded;
private with Ada.Text_IO;
private with AWS.Utils;

package AWS.Log is

   type Object is limited private;
   --  A log object. It must be activated by calling Start below

   type Callback is access procedure (Message : String);
   --  Access to a procedure that handles AWS access and/or error log data.
   --  If the access and/or error logs are started with a Callback procedure
   --  set, then AWS will no longer handle writing the log data to file, nor
   --  will it rotate or split the data. In short : If you set a Callback, it's
   --  up to you to handle these things.
   --  The raw log data generated by AWS is simply handed verbatim to the
   --  Callback procedure.

   type Split_Mode is (None, Each_Run, Daily, Monthly);
   --  It specifies when to create a new log file.
   --  None     : all log info gets accumulated into the same file.
   --  Each_Run : a new log file is created each time the server is started.
   --  Daily    : a new log file is created each day.
   --  Monthly  : a new log file is created each month.

   type Fields_Table is private;
   --  Type to keep record for Extended Log File Format

   Empty_Fields_Table : constant Fields_Table;

   Not_Specified : constant String;

   procedure Start
     (Log             : in out Object;
      Split           : Split_Mode := None;
      Size_Limit      : Natural    := 0;
      File_Directory  : String     := Not_Specified;
      Filename_Prefix : String     := Not_Specified;
      Auto_Flush      : Boolean    := False);
   --  Activate server's activity logging. Split indicate the way the log file
   --  should be created. If Size_Limit more than zero and size of log file
   --  become more than Size_Limit, log file would be splitted. Filename_Prefix
   --  is the log filename prefix. If it is not specified the default prefix is
   --  the program name. Set Auto_Flush to True if you want every write to the
   --  log to be flushed (not buffered). Auto_Flush should be set to True only
   --  for logs with few entries per second as the flush has a performance
   --  penalty.

   procedure Start
     (Log    : in out Object;
      Writer : Callback;
      Name   : String);
   --  Activate server's activity logging and send all log data to Callback.
   --  When the logging object is started with a Callback no splitting or size
   --  limits are imposed on the logging data. This will all have to be handled
   --  in the Callback.
   --  When a log is started with a Callback, all log data is passed verbatim
   --  to the Callback.
   --  The Name String is returned when the Filename function is called. This
   --  serves no other function than to label the Callback procedure.

   procedure Register_Field (Log : in out Object; Id : String);
   --  Register field to be written into extended log format

   procedure Set_Field
     (Log : Object; Data : in out Fields_Table; Id, Value : String);
   --  Set field value into the extended log record. Data could be used only
   --  in one task and with one log file. Different tasks could write own Data
   --  using the Write routine with Fields_Table parameter type.

   procedure Set_Header_Fields
     (Log    : Object;
      Data   : in out Fields_Table;
      Prefix : String;
      Header : AWS.Headers.List);
   --  Set header fields into extended log record.
   --  Name of the header fields would be <Prefix>(<Header_Name>).
   --  Prefix should be "cs" - Client to Server or "sc" - Server to Client.

   procedure Write (Log : in out Object; Data : in out Fields_Table);
   --  Write extended format record to log file and prepare record for the next
   --  data. It is not allowed to use same Fields_Table with different extended
   --  logs.

   procedure Write
     (Log          : in out Object;
      Connect_Stat : Status.Data;
      Answer       : Response.Data);
   --  Write log info if activated (i.e. Start routine above has been called)

   procedure Write
     (Log            : in out Object;
      Connect_Stat   : Status.Data;
      Status_Code    : Messages.Status_Code;
      Content_Length : Response.Content_Length_Type);
   --  Write log info if activated (i.e. Start routine above has been called).
   --  This version separated the Content_Length from Status.Data, this is
   --  required for example in the case of a user defined stream content. See
   --  AWS.Resources.Stream.

   procedure Write
     (Log          : in out Object;
      Connect_Stat : Status.Data;
      Data         : String);
   --  Write user's log info if activated.  (i.e. Start routine above has been
   --  called).

   procedure Write (Log : in out Object; Data : String);
   --  Write Data into the log file. This Data is unstructured, only a time
   --  tag prefix is prepended to Data. This routine is designed to be used
   --  for user's info in error log file.

   procedure Flush (Log : in out Object);
   --  Flush the data to the Log file, for be able to see last logged
   --  messages.
   --  If a Callback procedure is used to handle the log data, then calling
   --  Flush does nothing.

   procedure Stop (Log : in out Object);
   --  Stop logging activity

   function Is_Active (Log : Object) return Boolean;
   --  Returns True if Log is activated

   function Filename (Log : Object) return String;
   --  Returns current log filename or the empty string if the log is not
   --  activated.
   --  If a Callback is used to handle the log, then the name given in the
   --  Start procedure is returned. See the Start procedure for starting logs
   --  with a Callback.

   function Mode (Log : Object) return Split_Mode;
   --  Returns the split mode. None will be returned if log is not activated or
   --  a Callback procedure is used to handle the log data.

private

   use Ada;
   use Ada.Strings.Unbounded;

   package Strings_Positive is
     new Ada.Containers.Indefinite_Ordered_Maps (String, Positive);

   package SV renames AWS.Containers.String_Vectors;

   type Fields_Table is record
      Values : SV.Vector;
   end record;

   Empty_Fields_Table : constant Fields_Table := (Values => SV.Empty_Vector);

   Not_Specified : constant String := "";

   type Object is new Ada.Finalization.Limited_Controlled with record
      File                 : Text_IO.File_Type;
      Writer               : Callback := null;
      Writer_Name          : Unbounded_String := Null_Unbounded_String;
      Stop_Has_Been_Called : Boolean := False;
      Extended_Fields      : Strings_Positive.Map;
      Header_Written       : Boolean;
      File_Directory       : Unbounded_String;
      Filename_Prefix      : Unbounded_String;
      Split                : Split_Mode := None;
      Size_Limit           : Natural    := 0;
      Current_Tag          : Positive;
      Semaphore            : Utils.Semaphore;
      Auto_Flush           : Boolean;
   end record;

   overriding procedure Finalize (Log : in out Object);

end AWS.Log;
