-- 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.Command_Line;
with Ada.Environment_Variables;
with Ada.Exceptions;
with Ada.Text_IO;
with Configuration;
with Echo;
with Externals;
with Externals.Mail;
with Externals.Ops;
with Externals.Simple;
with Invocation;
with Globals;               use Globals;
with Help;
with Menus;
with Misc;                  use Misc;
with Readline;
with Receiving;
with Remote_Mode;
with Sending;
with Workaround;

procedure Topal is
   use Invocation;
   Config_Warnings : Boolean;

   procedure Report_Config_Warnings is
      use Ada.Text_IO;
   begin
      if Config_Warnings then
         New_Line;
         Put_Line("There were warnings while reading your config file.");
         New_Line;
      end if;
   end Report_Config_Warnings;
begin
   -- Get our PID.
   Our_PID := Externals.C_Get_Process_ID;
   -- Set defaults.
   Topal_Directory := ToUBS(Ada.Environment_Variables.Value("HOME")
			      & "/.topal");
   -- Load history and config.
   Configuration.Default_Configuration(Config);
   Readline.Load_History;
   -- .topal had better exist if we are going to read the configuration file.
   if Externals.Simple.Test_R(ToStr(Topal_Directory) & "/config") then
      Configuration.Read_Config_File(Config_Warnings);
   end if;
   -- If the topal directory exists, we chmod it appropriately.
   if Externals.Simple.Test_D(ToStr(Topal_Directory)) then
      Externals.Simple.Chmod("700", ToStr(Topal_Directory));
      -- And do the same to the cache directory.
      if Externals.Simple.Test_D(ToStr(Topal_Directory) & "/cache") then
         Externals.Simple.Chmod("700", ToStr(Topal_Directory) & "/cache");
      end if;
   end if;
   -- Sort out the command-line.
   Parse_Command_Line;
   case Run_Mode is
      when Help_Text =>
         Disclaimer;
         Help.Message;
      when Inline_Display =>
         Externals.Simple.Clear;
         Disclaimer;
         Report_Config_Warnings;
         Open_Result_File(Resultfile => ToStr(Resultfile));
         Receiving.Display(Tmpfile => ToStr(Tmpfile));
      when Mime_Display | Pipe_Display =>
         Disclaimer;
         Receiving.Mime_Display(Infile       => ToStr(Infile),
                                Content_Type => ToStr(Content_Type));
      when Old_Mime_Display =>
         Disclaimer;
         Receiving.Mime_Display_APGP(Infile       => ToStr(Infile),
                                     Content_Type => ToStr(Content_Type));
      when Raw_Input =>
	 Disclaimer;
	 Receiving.Process_Stdin;
      when Inline_Send =>
         Externals.Simple.Clear;
         Disclaimer;
         Report_Config_Warnings;
         Open_Result_File(Resultfile => ToStr(Resultfile));
         Sending.Send(Tmpfile    => ToStr(Tmpfile),
                      Recipients => Recipients.all);
      when Mime_Send =>
         Externals.Simple.Clear;
         Disclaimer;
         Report_Config_Warnings;
         Open_Result_File(Resultfile => ToStr(Resultfile));
         Sending.Send(Tmpfile    => ToStr(Tmpfile),
                      Mime       => True,
                      Mimefile   => ToStr(Mimefile),
                      Recipients => Recipients.all);
      when Actual_Send =>
	 -- Eat the input into a tempfile.
	 declare
	    Stdin_Name : constant String := Temp_File_Name("tmpfile");
	    Dummy_Mimefile : constant String := Temp_File_Name("mimefile");
	 begin
	    -- Catch standard input.
	    Externals.Simple.Cat_Stdin_Out(Stdin_Name);
	    -- Figure out the new recipients.
	    Recipients := Sending.Get_Recipients(Stdin_Name);
	    -- Now we want to open /dev/tty and use that as stdin.
	    Externals.Ops.Open_TTY;
	    Echo.Save_Terminal;
	    Echo.Set_Echo;
	    Externals.Simple.Stty_Sane;
	    -- Now show the disclaimer, etc.
	    Externals.Simple.Clear;
	    Disclaimer;
	    Report_Config_Warnings;
	    Sending.Send(Tmpfile     => Stdin_Name,
			 Mime        => True,
			 Mimefile    => Dummy_Mimefile,
			 Recipients  => Recipients.all,
			 Remaining   => Remaining.all,
			 Actual_Send => True);
	 end;
      when Nonpine_Send =>
         Disclaimer;
         -- Does the first object exist as a readable file?
         if not Externals.Simple.Test_R(ToStr(Tmpfile)) then
            -- We should be able to guarantee that Tmpfile is at least one
            -- character long, otherwise it couldn't have been recognised
            -- as an argument on the command line.
            if ToStr(Tmpfile)(1) = '-' then
               -- Perhaps they thought it was a command line option.
               Ada.Text_IO.Put_Line("`" & ToStr(Tmpfile) & "' is not recognised as either a command-line option nor a readable file.");
               Ada.Command_Line.Set_Exit_Status(Ada.Command_Line.Failure);
               -- Fall out normally.
            else
               Error("File `" & ToStr(Tmpfile) & "' is not a readable file.");
            end if;
         else
            -- The file exists....
            if ToStr(Tmpfile)(1) = '-' then
               -- Perhaps they thought it was a command line option.
               Ada.Text_IO.Put_Line("`" & ToStr(Tmpfile) & "' is not recognised as a command-line option, but it is a readable file.  Proceeding anyway -- select `abort' if that's not what you intended.");
               Ada.Text_IO.New_Line;
            end if;
            Sending.Send(Tmpfile    => ToStr(Tmpfile),
                         Recipients => Recipients.all,
                         Non_Pine   => True);
         end if;
      when Remote_Send =>
         Remote_Mode.Remote_Send(Tmpfile    => ToStr(Tmpfile),
                                 Resultfile => ToStr(Invocation.Resultfile),
                                 MIME       => False,
                                 Mimefile   => "",
                                 Recipients => Recipients.all);
      when Remote_MIME_Send =>
         Remote_Mode.Remote_Send(Tmpfile    => ToStr(Tmpfile),
                                 Resultfile => ToStr(Invocation.Resultfile),
                                 MIME       => True,
                                 Mimefile   => ToStr(Mimefile),
                                 Recipients => Recipients.all);
      when Remote_Decrypt =>
         Remote_Mode.Remote_Decrypt(Infile => ToStr(Infile),
                                    MIME   => False);
      when Remote_Mime_Decrypt =>
         Remote_Mode.Remote_Decrypt(Infile       => ToStr(Infile),
                                    MIME         => True,
                                    Content_Type => ToStr(Content_Type));
      when Clear_Temp =>
         Disclaimer;
         Ada.Text_IO.Put_Line("Clearing temporary files...");
         Externals.Simple.Rm_Tempfiles;
      when Clear_Cache =>
         Disclaimer;
         Ada.Text_IO.Put_Line("Clearing cache files...");
         Externals.Simple.Rm_Cachefiles;
      when Clear_All =>
         Disclaimer;
         Ada.Text_IO.Put_Line("Clearing temporary and cache files...");
         Externals.Simple.Rm_Tempfiles;
         Externals.Simple.Rm_Cachefiles;
      when Dump_Default_Config =>
         Configuration.Default_Configuration(Config);
         Configuration.Dump;
      when Dump_Current_Config =>
         Configuration.Dump;
      when Interactive_Config =>
         Disclaimer;
         -- Interactive configuration setting.
         Configuration.Edit_Configuration;
      when Fix_Email =>
         Workaround.Fix_Email;
      when Fix_Folders =>
         Disclaimer;
         Workaround.Fix_Folders(Folders => Folders.all);
      when Check_Send_Token =>
	 -- This is running as a filter.
	 Externals.Mail.Check_Send_Token(ToStr(CST_Token));
	 Ada.Command_Line.Set_Exit_Status(Ada.Command_Line.Success);
      when Server =>
         Disclaimer;
         -- Now accept connections....
         Remote_Mode.Server;
   end case;
   if not Config.Boolean_Opts(No_Clean) then
      Externals.Simple.Rm_Tempfiles_PID;
   end if;
   if Run_Mode /= Fix_Email and Run_Mode /= Check_Send_Token then
      -- Only do this with a real console.  This is nasty.
      Echo.Set_Echo;
      if Run_Mode = Actual_Send then
	 Echo.Restore_Terminal;
      end if;
   end if;
   Close_Debug;
   Readline.Save_History;
   Close_Result_File;
exception
   when Externals.System_Abort_By_User | Misc.User_Interrupt =>
      -- Just clean up.
      Close_Debug;
      Ada.Command_Line.Set_Exit_Status(Ada.Command_Line.Failure);
      Echo.Set_Echo;
      Externals.Simple.Stty_Sane;
      if Run_Mode = Actual_Send then
	 Echo.Restore_Terminal;
      end if;
      if not Config.Boolean_Opts(No_Clean) then
         Externals.Simple.Rm_Tempfiles_PID;
      end if;
      Readline.Save_History;
   when The_Exception : others =>
      declare
         use Ada.Exceptions;
         use Ada.Text_IO;
      begin
         New_Line(3);
         Put_Line(Standard_Error,
                  Do_SGR(Config.UBS_Opts(Colour_Important))
                    & "EXCEPTION!  Oops."
                    & Reset_SGR);
         -- Dump info.
         if Is_Open(Result_File) then
            Put_Line(Result_File,
                     "Exception raised: "
                       & Exception_Name(The_Exception));
            Close(Result_File);
         end if;
         Put_Line(Standard_Error,
                  "Exception raised: "
                    & Exception_Name(The_Exception));
         Put_Line(Standard_Error,
                  Exception_Information(The_Exception));
         New_Line;
         Put("Command line:");
         for I in 1..Ada.Command_Line.Argument_Count loop
            Put(" " & Ada.Command_Line.Argument(I));
         end loop;
         New_Line(2);
         Put_Line(Standard_Error,
                    "This is not good: please send a bug report with the information above to "
                    &NL&
                    "p.j.brooke@bcs.org.uk (unless it was caused by ctrl-c when GPG was"
                    &NL&
                    "running).  Tell me *exactly* what you were doing when it crashed."
                    &NL&
                    "Thank you!"
                             );
      end;
      Close_Debug;
      Ada.Command_Line.Set_Exit_Status(Ada.Command_Line.Failure);
         -- Try to pause....
      Menus.Wait;
      -- Clean up terminal settings.
      Echo.Set_Echo;
      Externals.Simple.Stty_Sane;
      if Run_Mode = Actual_Send then
	 Echo.Restore_Terminal;
      end if;
      if not Config.Boolean_Opts(No_Clean) then
         Externals.Simple.Rm_Tempfiles_PID;
      end if;
      Readline.Save_History;
end Topal;
