-- Topal: GPG/GnuPG and Alpine/Pine integration
-- Copyright (C) 2001--2022  Phillip J. Brooke
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License version 3 as
-- published by the Free Software Foundation.
--
-- 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.Text_IO;
with Externals;             use Externals;
with Externals.Mail;
with Externals.Simple;
with Menus;                 use Menus;
with Misc;                  use Misc;
with Readline;

package body Attachments is

   type Menu_Array_Ptr is access all Attachlist_Menus.MNA;

   -- Add an attachment to the list....
   procedure Add_Attachment (Filename  : in     UBS;
                             List      : in out Attachment_List) is
      Match : Boolean := False;
   begin
      Debug("+Add_Attachment");
      -- Is it a duplicate?
      Debug("Add_Attachment: Checking for duplicates");
      declare
         use type UBS;
      begin
         Debug("Add_Attachment: Approaching duplicates loop");
         for I in 1..Integer(List.AA.Length) loop
            Debug("Add_Attachment: In duplicates loop");
            Match := Match or Filename = List.AA.Element(I);
         end loop;
      end;
      if not Match then
         -- Add the attachment.
         Debug("Add_Attachment: Adding an attachment");
         List.AA.Append(Filename);
         Debug("Add_Attachment: Done adding an attachment");
      end if;
   exception
      when others =>
         Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error,
                              "Exception raised in Attachments.Add_Attachment");
         raise;
   end Add_Attachment;

   -- Remove an attachment.
   procedure Remove_Attachment (Filename  : in     UBS;
                                List      : in out Attachment_List) is
      A_Num : Integer;
      A_Found : Boolean := False;
      use type UBS;
   begin
      for I in 1..Integer(List.AA.Length) loop
         if Filename = List.AA.Element(I) then
            A_Found := True;
            A_Num := I;
         end if;
      end loop;
      if A_Found then
	 List.AA.Delete(A_Num);
      else
         raise Attachment_Not_Found;
      end if;
   exception
      when others =>
         Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error,
                              "Exception raised in Attachments.Remove_Attachment");
         raise;
   end Remove_Attachment;

   -- List the keys and edit them as appropriate (include removing a key
   -- from that list, and adding one from the keyring.
   procedure List (List : in out Attachment_List) is
      A_Head_Length : constant Positive := 74;
      A_Menu : Menu_Array_Ptr;

      procedure Generate_A_List is
      begin
         Debug("+Generate_A_List");
         -- Create a menu with the prefix of these attachments.
         A_Menu := new Attachlist_Menus.MNA(1..Integer(List.AA.Length));
         for I in 1..Integer(List.AA.Length) loop
            declare
               S : constant String := ToStr(List.AA.Element(I));
            begin
               if S'Length < A_Head_Length then
                  A_Menu(I) := ToUBS(S);
               else
                  -- Get the first 10, then ..., then A_Head_Length-13 of the last characters.
                  A_Menu(I) := ToUBS(S(S'First..S'First+9)
                                     & "..."
                                     & S(S'Last-A_Head_Length+14..S'Last));
               end if;
            end;
         end loop;
         Debug("-Generate_A_List");
      exception
         when others =>
            Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error,
                                 "Exception raised in Attachments.List.Generate_A_List");
            raise;
      end Generate_A_List;

      Selection    : Attachlist_Menus.CLM_Return;
      Selected     : Integer;
   begin
      Debug("+List");
      Generate_A_List;
  List_Keys_Loop:
      loop
         Ada.Text_IO.New_Line(5);
         Ada.Text_IO.Put_Line(Do_SGR(Config.UBS_Opts(Colour_Menu_Title))
                                &  "** Attachment menu:"
                                & Reset_SGR);
         Ada.Text_IO.Put_Line(Integer'Image(Attachments.Count(List))
                              & " attachment(s)");
         Selection := Attachlist_Menu(A_Menu.all);
         if not Selection.Is_Num then
            case Selection.I is
               when Done =>
                  exit List_Keys_Loop;
               when Add => -- Add filename.
                  declare
                     New_Filename : UBS;
                     Alt_Filename : UBS;
                     use Ada.Text_IO;
                  begin
                     New_Line(1);
                     Put_Line("Add attachment:");
                     New_Filename
                       := ToUBS(Readline.Get_String("Type filename: ",
                                                    Enable_Tab_Completion => True));
                     -- Deal with the last space that we'll often get.
                     declare
                        NFN : constant String := ToStr(New_Filename);
                     begin
                        if NFN(NFN'Last) = ' ' then
                           Alt_Filename := ToUBS(NFN(NFN'First..NFN'Last-1));
                        else
                           Alt_Filename := New_Filename;
                        end if;
                     end;
                     -- Does it exist?
                     if Externals.Simple.Test_R(ToStr(New_Filename)) then
                        Add_Attachment(New_Filename, List);
                        Generate_A_List;
                     elsif Externals.Simple.Test_R(ToStr(Alt_Filename)) then
                        Add_Attachment(Alt_Filename, List);
                        Generate_A_List;
                     else
                        Put_Line("`" & ToStr(New_Filename) & "' is not readable or does not exist (if you've used a `~', try expanding it)");
                     end if;
                  end;
            end case;
         else
            -- Remove the given number.
            Selected := Selection.N;
            Remove_Attachment(List.AA.Element(Selected),
                              List);
            Generate_A_List;
         end if;
      end loop List_Keys_Loop;
      Debug("-List_Keys");
   exception
      when others =>
         Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error,
                              "Exception raised in Attachments.List");
         raise;
   end List;

   procedure Empty_Attachment_List (List : in out Attachment_List) is
   begin
      List.AA := UVP.Empty_Vector;
   exception
      when others =>
         Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error,
                              "Exception raised in Attachments.Empty_Attachment_List");
         raise;
   end Empty_Attachment_List;

   function Count (List : in Attachment_List) return Natural is
   begin
      return Natural(List.AA.Length);
   exception
      when others =>
         Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error,
                              "Exception raised in Attachments.Count");
         raise;
   end Count;

   procedure Replace_Tmpfile (Tmpfile           : in String;
                              List              : in Attachment_List) is
      AL : UBS_Array(1..Count(List)+1);
      BF : constant String := Temp_File_Name("att64-");
      TF : constant String := Temp_File_Name("attrtf");
      CT : UBS;
   begin
      -- Do nothing if the attachment list is empty.
      if Integer(List.AA.Length) /= 0 then
         -- Now, construct a list of items.
         AL(1) := ToUBS(Tmpfile);
         for I in 1..Integer(List.AA.Length) loop
            -- Guess the content type.
            CT := ToUBS(Externals.Simple.Guess_Content_Type(ToStr(List.AA.Element(I))) & "; name="""&Basename(ToStr(List.AA.Element(I)))&"""");
            -- Create a base64 subpart.
            Externals.Mail.Mimeconstruct_Subpart(Infile => ToStr(List.AA.Element(I)),
                                                 Outfile => BF & Trim_Leading_Spaces(Integer'Image(I)),
                                                 Content_Type => ToStr(CT),
                                                 Dos2UnixU => True,
                                                 Use_Encoding => True,
                                                 Encoding => "base64");
            -- Add the base64's file to the list.
            AL(I+1) := ToUBS(BF & Trim_Leading_Spaces(Integer'Image(I)));
            Ada.Text_IO.Put_Line("Added file `"&ToStr(List.AA.Element(I))&"' with content-type `"&ToStr(CT)&"'");
         end loop;
         -- And call mimeconstruct.
         Externals.Mail.Mimeconstruct_Mixed(AL, TF);
         -- Then replace the original tmpfile.
         Externals.Simple.Mv_F(TF, Tmpfile);
         -- Convert to CR-LF line endings.
         Externals.Simple.Dos2Unix_U(Tmpfile);
      end if;
   exception
      when others =>
         Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error,
                              "Exception raised in Attachments.Replace_Tmpfile");
         raise;
   end Replace_Tmpfile;

end Attachments;


