--  dh_ada_library, helper for Ada libraries Debian maintainers
--
--  Copyright (C) 2012-2020 Nicolas Boulenguez <nicolas@debian.org>
--
--  This program 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 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
--  General Public License for more details.
--  You should have received a copy of the GNU General Public License
--  along with this program. If not, see <http://www.gnu.org/licenses/>.
--
with Ada.Characters.Latin_1;
with Ada.Directories;
with Ada.Strings.Fixed;
with Ada.Text_IO;

with GNAT.Regpat;

with Misc;
with String_Vectors;

package body Dh_Lib is

   procedure Load (File_Name : in     String;
                   Content   :    out String_Vectors.Vector);
   procedure Save (File_Name : in String;
                   Content   : in String_Vectors.Vector);

   ----------------------------------------------------------------------

   procedure Addsubstvar
     (Binary_Package                   : in String;
      Substvar                         : in String;
      Package_That_Will_Be_Depended_On : in String;
      Version_Info                     : in String := "")
   is
      use GNAT.Regpat;
      use String_Vectors;
      Start : constant String := Substvar & '=';
      function Compute_Appended return String;
      function Compute_Appended return String is
      begin
         if Version_Info = "" then
            return Package_That_Will_Be_Depended_On;
         else
            return Package_That_Will_Be_Depended_On
              & " (" & Version_Info & ')';
         end if;
      end Compute_Appended;
      Appended : constant String := Compute_Appended;
      Regex : constant Pattern_Matcher
        := Compile ('^' & Quote (Start) & "(.*, *)?" & Quote (Appended)
                      & "(,.*)?$");
      File_Name : constant String := "debian/" & Binary_Package & ".substvars";
      Content : Vector;
      Line : Natural;
   begin
      if not String_Sets.Contains (Dopackages, Binary_Package) then
         return;
      end if;
      if Ada.Directories.Exists (File_Name) then
         Load (File_Name, Content);
      end if;
      Line := 1;
      loop
         if Line > Natural (Length (Content)) then
            Append (Content, String'(Start & Appended));
            exit;
         elsif Ada.Strings.Fixed.Head (Element (Content, Line), Start'Length)
           = Start
         then
            if Match (Regex, Element (Content, Line)) then return; end if;
            Verbose_Print ("Add " & Appended & " to " & Substvar & " in "
                             & File_Name);
            if Element (Content, Line)'Length = Start'Length then
               Replace_Element (Content, Line, Element (Content, Line)
                                  & Appended);
            else
               Replace_Element (Content, Line, Element (Content, Line) & ", "
                                  & Appended);
            end if;
            exit;
         end if;
         Line := Line + 1;
      end loop;
      if Noact then return; end if;
      Save (File_Name, Content);
   end Addsubstvar;

   procedure Copy_File
     (Source_Name : in String;
      Target_Name : in String)
   is
   begin
      Verbose_Print ("Copy_File " & Misc.Relative_Path (Source_Name)
                       & ' ' & Misc.Relative_Path (Target_Name));
      if not Noact then
         if Ada.Directories.Exists (Target_Name) then
            Ada.Directories.Delete_File (Target_Name);
         end if;
         Ada.Directories.Copy_File (Source_Name, Target_Name);
      end if;
   end Copy_File;

   procedure Create_Path
     (New_Directory : in String)
   is
   begin
      if not Ada.Directories.Exists (New_Directory) then
         Verbose_Print ("Create_Path " & Misc.Relative_Path (New_Directory));
         if not Noact then
            Ada.Directories.Create_Path (New_Directory);
         end if;
      end if;
   end Create_Path;

   procedure Do_It
     (Program_Name : in String;
      Args         : in GNAT.OS_Lib.Argument_List)
   is
      Return_Code : Integer;
   begin
      if Verbose then
         Ada.Text_IO.Put (Ada.Characters.Latin_1.HT);
         Ada.Text_IO.Put (Ada.Directories.Simple_Name (Program_Name));
         for Arg of Args loop
            Ada.Text_IO.Put (' ');
            Ada.Text_IO.Put (Arg.all);
         end loop;
         Ada.Text_IO.New_Line;
      end if;
      if not Noact then
         Return_Code := GNAT.OS_Lib.Spawn (Program_Name, Args);
         if Return_Code /= 0 then
            raise Misc.Subprocess_Error with
              Program_Name & " returned " & Integer'Image (Return_Code);
         end if;
      end if;
   end Do_It;

   procedure Init
     (Ignored : in Command_Line_Argument_Set)
   is
      use Ada.Command_Line;
      use Ada.Text_IO;
      use GNAT.OS_Lib;
      Temp_File   : String_Access;
      Return_Code : Integer;
      File        : File_Type;
      Dh_Args     : Argument_List (1 .. Argument_Count) := (others => null);
      Last        : Natural := 0;
   begin
      for I in Ignored'Range loop
         if not Ignored (I) then
            Last := Last + 1;
            Dh_Args (Last) := new String'(Argument (I));
            if Argument (I) = "-v" or Argument (I) = "--verbose" then
               Verbose := True;
            elsif Argument (I) = "--no-act" then
               Noact := True;
            elsif Argument (I) = "-i" or Argument (I) = "--indep" then
               Indep := True;
            end if;
         end if;
      end loop;
      Misc.Capture_Output ("/usr/bin/dh_listpackages", Dh_Args (1 .. Last),
                           Temp_File, Return_Code);
      if Return_Code /= 0 then
         --  dh_listpackages outputs its error message on stdout.
         Open (File, In_File, Temp_File.all);
         while not End_Of_File (File) loop
            Put_Line (Standard_Error, Get_Line (File));
         end loop;
         Close (File);
         Misc.Raise_Subprocess_Error ("/usr/bin/dh_list_packages",
                                      Dh_Args (1 .. Last), Return_Code);
      end if;
      for I in 1 .. Last loop
         Free (Dh_Args (I));
      end loop;
      Open (File, In_File, Temp_File.all);
      while not End_Of_File (File) loop
         String_Sets.Insert (Dopackages, Get_Line (File));
      end loop;
      Close (File);
      Ada.Directories.Delete_File (Temp_File.all);
      Free (Temp_File);
   exception
      when others =>
         for I in 1 .. Last loop
            Free (Dh_Args (I));
         end loop;
         if Is_Open (File) then Close (File); end if;
         if Temp_File /= null then
            Ada.Directories.Delete_File (Temp_File.all);
            Free (Temp_File);
         end if;
         raise;
   end Init;

   procedure Load
     (File_Name : in     String;
      Content   :    out String_Vectors.Vector)
   is
      use Ada.Text_IO;
      File : File_Type;
   begin
      Open (File, In_File, File_Name);
      while not End_Of_File (File) loop
         String_Vectors.Append (Content, Get_Line (File));
      end loop;
      Close (File);
   exception
      when others =>
         if Is_Open (File) then Close (File); end if;
         raise;
   end Load;

   procedure Save
     (File_Name : in String;
      Content   : in String_Vectors.Vector)
   is
      use Ada.Text_IO;
      use String_Vectors;
      File : File_Type;
   begin
      Create (File, Out_File, File_Name);
      for I in 1 .. Natural (Length (Content)) loop
         Put_Line (File, Element (Content, I));
      end loop;
      Close (File);
   exception
      when others =>
         if Is_Open (File) then Close (File); end if;
         raise;
   end Save;

   procedure Verbose_Print
     (Message : in String)
   is
      use Ada.Text_IO;
   begin
      if Verbose then
         Put (Ada.Characters.Latin_1.HT);
         Put (Message);
         New_Line;
      end if;
   end Verbose_Print;

   procedure Warning
     (Message : in String)
   is
      use Ada.Text_IO;
   begin
      Put (Standard_Error, "warning: ");
      Put (Standard_Error, Message);
      New_Line (Standard_Error);
   end Warning;

   procedure Write_Log
     (Command        : in String := Ada.Command_Line.Command_Name;
      Binary_Package : in String)
   is
      use Ada.Text_IO;
      Log_Name : constant String := "debian/" & Binary_Package
        & ".debhelper.log";
      Log : File_Type;
   begin
      if Ada.Directories.Exists (Log_Name) then
         Open (Log, Append_File, Log_Name);
      else
         Create (Log, Out_File, Log_Name);
      end if;
      Put_Line (Log, Command);
      Close (Log);
   exception
      when others =>
         if Is_Open (Log) then Close (Log); end if;
         raise;
   end Write_Log;

end Dh_Lib;
