--
-- @filename adadoc.adb
-- @author Julien Burdy & Vincent Decorges
-- @date 17.6.02
-- @brief AdaDoc main program
--
-- History  :
-- Date     Modification               Author
-- 29.08.02 Pass multiple files        JC Groult
-- 27.12.04 Add Module.Texinfo and     Vincent Decorges
--          StrongHelp   
--
with Ada.Text_IO; use Ada.Text_IO;

with Ada.Command_Line; use Ada.Command_Line;

-- from XmlAda
with Input_Sources.File;
with Unicode.CES.Basic_8bit;
with Unicode.CCS.Iso_8859_1;
with Sax.Readers;

-- from AdaDoc Module
with Module_Interface;
with Module;
with Module.LaTeX;
with Module.HTML;
with Module.Stronghelp;
with Module.Texinfo;

-- Parser
with Ada95_Parser;
with AdaDoc_Tree.Comment;
with Ada95_Parser_Tokens;

-- XML generation
with AdaDoc_Tree.Xml;

with Dict_Tda;
with Ada.Strings.Fixed;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;

-- Here we have a dependency with GNAT but it can easily be removed
-- because we use this package only for Directory_Separator
with GNAT.OS_Lib;

procedure AdaDoc is

   type String_Access is access String;

   -- to simplify the String_Access construction.
   function S(Item : String) return String_Access is
   begin
      return new String'(Item);
   end S;

   -- options values
   type Option_Name is (Delete_Xml, Extract_Comment, HTML, LaTeX, StrongHelp, Texinfo);

   -- options content
   type Option_Value is
      record
         Stat    : Boolean;       -- is active
         Switch  : String_Access; -- switch (two char)
         Comment : String_Access; -- show in the help msg
         Options : String_Access; -- option for the module (after the switch)
      end record;

   type Option_Tab_Type is array (Option_Name) of Option_Value;

   Option_Error : exception;

   ADS_File_Name : String_Access; -- ADS file name (with extension)
   XML_File_Name : String_Access; -- XML file name (with extension)

   Tags_File_Name : constant String := "adadoc_tags.cfg";

   --Read the tag file definition
   function Read_Tags_File (File_Name : String) return Dict_TDA.Dict_Type is separate;

   -- To stock the tags definitions
   Dict_Tags : Dict_Tda.Dict_Type := Read_Tags_File(Tags_File_Name);

   -- Print the help with options comments
   procedure Print_Help (Opt : Option_Tab_Type) is
   begin
      New_Line(2);
      Put_Line ("AdaDoc create a documentation in different format");
      Put_Line ("from an Ada95 specification package.");
      New_Line;
      Put_Line ("Use : adadoc {option } {File_Name }");
      New_Line;
      Put_Line ("Options :");

      -- Print the options comments from array
      for O in Opt'range loop
         Put("  "); Put(Opt(O).Switch.All);
         Set_Col(11);
         Put(": "); Put_Line(Opt(O).Comment.All);
      end loop;
      New_Line;
      Put_Line ("Example : adadoc -x -Hfid spec.ads spec2.ads ...");
      New_Line;
      Put_Line ("AdaDoc by Julien Burdy and Vincent Decorges");
      Put_Line ("Web => http://adadoc.sf.net");
      Put_Line ("AdaDoc is freely available under the GPL license.");
      Put_Line ("Version 2.1");
      New_Line;
   end Print_Help;



   -- Parse the XML file and generate module output
   procedure Parse_XML ( XML_Name : String;
                         Module_Instance : Module.Object_Access;
                         Dict_Tags : in Dict_Tda.Dict_Type;
                         Module_Option : String) is

      XML_File : Input_Sources.File.File_Input;
      Reader  : Module_Interface.Object;
   begin
      Module_Interface.Set_Module(Reader, Module_Instance, Dict_Tags, Module_Option);

      Input_Sources.File.Open(XML_Name, XML_File);
      Input_Sources.File.Set_Character_Set(XML_File,
                                        Unicode.CCS.Iso_8859_1.Iso_8859_1_Character_Set);
      Input_Sources.File.Set_Encoding(XML_File,
                                      Unicode.CES.Basic_8bit.Basic_8bit_Encoding);

      Module_Interface.Parse(Reader, XML_File);

      Input_Sources.File.Close(XML_File);
   end Parse_XML;


   -- return the file_name without path and extension
   function Extract_Name (File_Name : String) return String is

      Ind_First : Natural;
      Ind_Last  : Positive := File_Name'Last;

      Ext_Sep   : Character := '.';
      Dir_Sep   : Character := GNAT.OS_Lib.Directory_Separator;

   begin

      -- search the name and extension separation
      if Ada.Strings.Fixed.Count(File_Name,"" & Ext_Sep) > 0 then
         loop
            exit when File_Name(Ind_Last) = Ext_Sep or Ind_Last = File_Name'First;
            Ind_Last := Ind_Last - 1;
         end loop;
      end if;
      Ind_First := Ind_Last;

      loop

         exit when File_Name(Ind_First) = Dir_Sep or
                   Ind_First = File_Name'First;

         Ind_First := Ind_First - 1;

      end loop;

      if Ind_First /= File_Name'First then
         Ind_First := Ind_First + 1;
      end if;

      return File_Name(Ind_First..Ind_Last);

   end Extract_Name;


   -- Parsing ADS and writing XML
   procedure Parsing_ADS (ADS_File_Name, XML_File_Name : in String;
                          Dict_Tags : in Dict_Tda.Dict_Type;
                          Extract_Comment : Boolean := True) is
   begin
      --if false Don't extract comment but look only in the header
      if Extract_Comment then
         --Initalize the abstract machine to extract the comment
         AdaDoc_Tree.Comment.Init(Mode => AdaDoc_Tree.Comment.Before,
                                  Dict_Tag => Dict_Tags, Extract => Extract_Comment);
      end if;

      Ada95_Parser.Run(ADS_File_Name);
      AdaDoc_Tree.Xml.Write_Xml (XML_File_Name);
      AdaDoc_Tree.Comment.Delete;

   end Parsing_ADS;



   -- Extract options from the command line
   procedure Extracting_Option (Option_Tab : in out Option_Tab_Type;
                                Argument_Index : in Integer;
                                Is_Option : out Boolean) is
   begin
      Is_Option := False;
      for Opt in Option_Tab'Range loop
         -- Argument(n)(1..2) is the two first char or argument (ex. "-h")
         if Option_Tab(Opt).Switch.All(1..2) = Argument(Argument_Index)(1..2) then
            -- Invert the stat
            Option_Tab(Opt).Stat := not Option_Tab(Opt).Stat;
            Is_Option := True;

            -- Options after two car (ex "-hOption")
            if Argument(Argument_Index)'Length > 2 then
               Option_Tab(Opt).Options := S(Argument(Argument_Index)
               (3..Argument(Argument_Index)'Length));
            end if;
         end if;
      end loop;
   exception
      when others =>
         raise Option_Error;
   end Extracting_Option;


   -- Option tab with stat, switch, comment, default options
   Option_Tab  : Option_Tab_Type :=
   (
   Delete_Xml => (False, Switch  => S("-x"),
                         Comment => S("don't delete XML file after generating output."),
                         Options => S("")),
   Extract_Comment => (True, Switch  => S("-c"),
                         Comment => S("don't try to extract comments from "&
                                                                   "the specification."),
                         Options => S("")),
   HTML       => (False, Switch  => S("-Hstylsheet"),
                         Comment => S("generate HTML file. stylsheet is optional"),
                         Options => S("")),
   LaTeX      => (False, Switch  => S("-L"),
                         Comment => S("generate LaTeX file."),
                         Options => S("")),
   StrongHelp => (False, Switch  => S("-S"),
                         Comment => S("generate StrongHelp file."),
                         Options => S("")),
   Texinfo    => (False, Switch  => S("-Tns"),
                         Comment => S("generate Texinfo file. -n : numbered output -s : stand-alone output."),
                         Options => S(""))                         
   );


begin

   if Argument_Count = 0 then
      Print_Help(Option_Tab);
   else

      for Arg in 1..Argument_Count loop

         Argument_Loop:declare
            Is_Option : Boolean := False;
         begin
            -- Extract command line option and update the array
            Extracting_Option(Option_Tab, Arg, Is_Option);

            if not Is_Option then
               -- Initialize name's info
               ADS_File_Name  := S(Argument(Arg));
               XML_File_Name  := S(Extract_Name(ADS_File_Name.all) & "xml");

               -- Parsing ADS and create XML
               Put_Line("Parsing "&ADS_File_Name.All&" and generating XML...");
               Parsing_ADS(ADS_File_Name.All, XML_File_Name.All, Dict_Tags,
                          Option_Tab(Extract_Comment).Stat);


               --Module declaration
               Modules:declare

                  --If default we use the html module
                  Use_Default : Boolean := True;

               begin

                  -- write .tex
                  if Option_Tab(LaTeX).Stat then
                     Put_Line("Generating LaTeX file...");
                     Parse_XML(XML_File_Name.All,
                               new Module.LaTeX.Object,
                               Dict_Tags,
                               Option_Tab(LaTeX).Options.All);

                     Use_Default := False;
                  end if;

                  --
                  --!!Add your module here!!
                  --
                  -- write StrongHelp
                  if Option_Tab(StrongHelp).Stat then
                     Put_Line("Generating StrongHelp...");
                     Parse_XML(XML_File_Name.All,
                               new Module.StrongHelp.Object,
                               Dict_Tags,
                               Option_Tab(StrongHelp).Options.All);
                               
                     Use_Default := False;
                  end if; 
                  
                  -- write Texinfo
                  if Option_Tab(Texinfo).Stat then
                     Put_Line("Generating Texinfo...");
                     Parse_XML(XML_File_Name.All,
                               new Module.Texinfo.Object,
                               Dict_Tags,
                               Option_Tab(Texinfo).Options.All);

                     Use_Default := False;
                  end if;                                   

                  -- write HTML
                  if Option_Tab(HTML).Stat or Use_Default then
                     Put_Line("Generating HTML...");
                     Parse_XML(XML_File_Name.All,
                               new Module.HTML.Object,
                               Dict_Tags,
                               Option_Tab(HTML).Options.All);
                  end if;

               end Modules;


               -- not delete the XML
               if not Option_Tab(Delete_Xml).Stat then
                  declare
                     XML_File : File_Type;
                  begin
                     Put_Line("Deleting "&XML_File_Name.All);
                     Create(XML_File,Name => XML_File_Name.All);
                     Delete(XML_File);
                  end;
               end if;
            end if; --Is_Option
         end Argument_Loop;
      end loop;
   end if;

   Put_Line("Done!");

   exception

      when Name_Error =>
         Put_Line ("Error the file doesn't exist");

      when Option_Error  =>
         Put_Line ("Options Error");

      --Error the file is not a spec
      when AdaDoc_Tree.XMl.Package_Spec_Error =>
         Put_Line ("The file is not a compliant Ada 95 specification");

      --Syntax error
      when Ada95_Parser_Tokens.Syntax_Error =>
         Put_Line ("Aborting...");

end AdaDoc;



