-------------------------------------------------------------------------------
-- (C) Altran Praxis Limited
-------------------------------------------------------------------------------
--
-- The SPARK toolset 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. The SPARK toolset 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 distributed with the SPARK toolset; see file
-- COPYING3. If not, go to http://www.gnu.org/licenses for a complete copy of
-- the license.
--
--=============================================================================

separate (STree)
function FindLastItemInDependencyRelation (Node : SyntaxNode) return LexTokenManager.Token_Position is
   The_Next_Node : SyntaxNode;

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

   function Find_Last_Import_Or_Export (Node : SyntaxNode) return SyntaxNode
   --# global in Table;
   --# pre Syntax_Node_Type (Node, Table) = SP_Symbols.dependency_clause;
   --# return Return_Node => (Syntax_Node_Type (Return_Node, Table) = SP_Symbols.entire_variable
   --#                          or Syntax_Node_Type (Return_Node, Table) = SP_Symbols.multiply);
   is
      The_Next_Node : SyntaxNode;
   begin
      --first see if there are any imports at all
      The_Next_Node := Child_Node (Current_Node => Next_Sibling (Current_Node => Child_Node (Current_Node => Node)));
      -- ASSUME The_Next_Node = multiply OR dependency_clause_optrep OR NULL
      if Syntax_Node_Type (Node => The_Next_Node) = SP_Symbols.multiply
        or else Syntax_Node_Type (Node => The_Next_Node) = SP_Symbols.dependency_clause_optrep then
         -- ASSUME The_Next_Node = multiply OR dependency_clause_optrep
         -- there are some imports
         -- first find dependency_clause_optrep if there is one.
         -- we must be pointing at one or at a star
         if Syntax_Node_Type (Node => The_Next_Node) = SP_Symbols.multiply then
            -- ASSUME The_Next_Node = multiply
            if Syntax_Node_Type (Node => Next_Sibling (Current_Node => The_Next_Node)) =
              SP_Symbols.dependency_clause_optrep then
               The_Next_Node := Next_Sibling (Current_Node => The_Next_Node);
            end if;
         end if;
         if Syntax_Node_Type (Node => The_Next_Node) = SP_Symbols.dependency_clause_optrep then
            -- ASSUME The_Next_Node = dependency_clause_optrep
            -- we need to find rightmost entire variable
            The_Next_Node := Child_Node (Current_Node => The_Next_Node);
            -- ASSUME The_Next_Node = dependency_clause_optrep OR entire_variable
            if Syntax_Node_Type (Node => The_Next_Node) = SP_Symbols.dependency_clause_optrep then
               -- ASSUME The_Next_Node = dependency_clause_optrep
               The_Next_Node := Next_Sibling (Current_Node => The_Next_Node);
               -- ASSUME The_Next_Node = entire_variable
               SystemErrors.RT_Assert
                 (C       => Syntax_Node_Type (Node => The_Next_Node) = SP_Symbols.entire_variable,
                  Sys_Err => SystemErrors.Invalid_Syntax_Tree,
                  Msg     => "Expect The_Next_Node = entire_variable in Find_Last_Import_Or_Export");
            elsif Syntax_Node_Type (Node => The_Next_Node) /= SP_Symbols.entire_variable then
               The_Next_Node := NullNode;
               SystemErrors.Fatal_Error
                 (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
                  Msg     => "Expect The_Next_Node = dependency_clause_optrep OR entire_variable " &
                    "in Find_Last_Import_Or_Export");
            end if;
         end if;
      elsif The_Next_Node = NullNode then
         -- ASSUME The_Next_Node = NULL
         -- there are no imports so look at the exports
         The_Next_Node := Child_Node (Current_Node => Child_Node (Current_Node => Node));
         -- ASSUME The_Next_Node = dependency_clause_optrep OR entire_variable
         if Syntax_Node_Type (Node => The_Next_Node) = SP_Symbols.dependency_clause_optrep then
            -- ASSUME The_Next_Node = dependency_clause_optrep
            The_Next_Node := Next_Sibling (Current_Node => The_Next_Node);
            -- ASSUME The_Next_Node = entire_variable
            SystemErrors.RT_Assert
              (C       => Syntax_Node_Type (Node => The_Next_Node) = SP_Symbols.entire_variable,
               Sys_Err => SystemErrors.Invalid_Syntax_Tree,
               Msg     => "Expect The_Next_Node = entire_variable in Find_Last_Import_Or_Export");
         elsif Syntax_Node_Type (Node => The_Next_Node) /= SP_Symbols.entire_variable then
            The_Next_Node := NullNode;
            SystemErrors.Fatal_Error
              (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
               Msg     => "Expect The_Next_Node = dependency_clause_optrep OR entire_variable in Find_Last_Import_Or_Export");
         end if;
      else
         SystemErrors.Fatal_Error
           (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
            Msg     => "Expect The_Next_Node = multiply OR dependency_clause_optrep OR NULL in Find_Last_Import_Or_Export");
      end if;
      return The_Next_Node;
   end Find_Last_Import_Or_Export;

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

   function Find_Last_Import_Of_Null_Import_List (Node : SyntaxNode) return SyntaxNode
   --# global in Table;
   --# pre Syntax_Node_Type (Node, Table) = SP_Symbols.null_import_list;
   --# return Return_Node => Syntax_Node_Type (Return_Node, Table) = SP_Symbols.entire_variable;
   is
      The_Next_Node : SyntaxNode;
   begin
      The_Next_Node := Child_Node (Current_Node => Next_Sibling (Current_Node => Child_Node (Current_Node => Node)));
      -- ASSUME The_Next_Node = dependency_clause_optrep OR entire_variable
      if Syntax_Node_Type (Node => The_Next_Node) = SP_Symbols.dependency_clause_optrep then
         -- ASSUME The_Next_Node = dependency_clause_optrep
         The_Next_Node := Next_Sibling (Current_Node => The_Next_Node);
         -- ASSUME The_Next_Node = entire_variable
         SystemErrors.RT_Assert
           (C       => Syntax_Node_Type (Node => The_Next_Node) = SP_Symbols.entire_variable,
            Sys_Err => SystemErrors.Invalid_Syntax_Tree,
            Msg     => "Expect The_Next_Node = entire_variable in Find_Last_Import_Of_Null_Import_List");
      elsif Syntax_Node_Type (Node => The_Next_Node) /= SP_Symbols.entire_variable then
         The_Next_Node := NullNode;
         SystemErrors.Fatal_Error
           (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
            Msg     => "Expect The_Next_Node = dependency_clause_optrep OR entire_variable " &
              "in Find_Last_Import_Of_Null_Import_List");
      end if;
      return The_Next_Node;
   end Find_Last_Import_Of_Null_Import_List;

begin -- FindLastItemInDependencyRelation

   -- Position to report error
   -- First see if there is a null_import_list.  If there is, we know it is the last clause
   The_Next_Node := Child_Node (Current_Node => Child_Node (Current_Node => Node));
   -- ASSUME The_Next_Node = dependency_relation_rep OR null_import_list OR NULL
   if Syntax_Node_Type (Node => The_Next_Node) = SP_Symbols.dependency_relation_rep then
      -- ASSUME The_Next_Node = dependency_relation_rep
      -- we don't have a singleton null_import_list but there might still be one to our right; thus:
      The_Next_Node := Next_Sibling (Current_Node => The_Next_Node);
      -- ASSUME The_Next_Node = ampersand OR NULL
      if Syntax_Node_Type (Node => The_Next_Node) = SP_Symbols.ampersand then
         -- ASSUME The_Next_Node = ampersand
         The_Next_Node := Next_Sibling (Current_Node => The_Next_Node);
         -- ASSUME The_Next_Node = null_import_list
         SystemErrors.RT_Assert
           (C       => Syntax_Node_Type (Node => The_Next_Node) = SP_Symbols.null_import_list,
            Sys_Err => SystemErrors.Invalid_Syntax_Tree,
            Msg     => "Expect The_Next_Node = null_import_list in FindLastItemInDependencyRelation");
      elsif The_Next_Node /= NullNode then
         The_Next_Node := NullNode;
         SystemErrors.Fatal_Error
           (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
            Msg     => "Expect The_Next_Node = ampersand OR NULL in FindLastItemInDependencyRelation");
      end if;
   elsif The_Next_Node /= NullNode and then Syntax_Node_Type (Node => The_Next_Node) /= SP_Symbols.null_import_list then
      The_Next_Node := NullNode;
      SystemErrors.Fatal_Error
        (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
         Msg     => "Expect The_Next_Node = dependency_relation_rep OR null_import_list OR NULL " &
           "in FindLastItemInDependencyRelation");
   end if;
   --# check The_Next_Node = NullNode or Syntax_Node_Type (The_Next_Node, Table) = SP_Symbols.null_import_list;
   if Syntax_Node_Type (Node => The_Next_Node) = SP_Symbols.null_import_list then
      -- ASSUME The_Next_Node = null_import_list
      -- the place to report the error is the last import of the null_import_list
      The_Next_Node := Find_Last_Import_Of_Null_Import_List (Node => The_Next_Node);
   elsif The_Next_Node = NullNode then
      -- ASSUME The_Next_Node = NULL
      -- next choice is last of several derives imports if there is one
      The_Next_Node := Child_Node (Current_Node => Child_Node (Current_Node => Node));
      -- ASSUME The_Next_Node = dependency_relation_rep OR NULL
      if Syntax_Node_Type (Node => The_Next_Node) = SP_Symbols.dependency_relation_rep then
         -- ASSUME The_Next_Node = dependency_relation_rep
         The_Next_Node := Child_Node (Current_Node => The_Next_Node);
         -- ASSUME The_Next_Node = dependency_relation_rep OR dependency_clause
         if Syntax_Node_Type (Node => The_Next_Node) = SP_Symbols.dependency_relation_rep then
            -- ASSUME The_Next_Node = dependency_relation_rep
            The_Next_Node := Next_Sibling (Current_Node => Next_Sibling (Current_Node => The_Next_Node));
            -- ASSUME The_Next_Node = dependency_clause
            SystemErrors.RT_Assert
              (C       => Syntax_Node_Type (Node => The_Next_Node) = SP_Symbols.dependency_clause,
               Sys_Err => SystemErrors.Invalid_Syntax_Tree,
               Msg     => "Expect The_Next_Node = dependency_clause in FindLastItemInDependencyRelation");
            The_Next_Node := Find_Last_Import_Or_Export (Node => The_Next_Node);
         elsif Syntax_Node_Type (Node => The_Next_Node) = SP_Symbols.dependency_clause then
            -- ASSUME The_Next_Node = dependency_clause
            The_Next_Node := Find_Last_Import_Or_Export (Node => The_Next_Node);
         else
            SystemErrors.Fatal_Error
              (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
               Msg     => "Expect The_Next_Node = dependency_relation_rep OR dependency_clause " &
                 "in FindLastItemInDependencyRelation");
         end if;
      elsif The_Next_Node = NullNode then
         -- ASSUME The_Next_Node = NULL
         -- there is no derives so start trawling the globals

         -- first move to mode_global node, if this fails then there 3rd and 4th choice fail
         The_Next_Node := Child_Node (Current_Node => Child_Node (Current_Node => Parent_Node (Current_Node => Node)));
         if Syntax_Node_Type (Node => The_Next_Node) = SP_Symbols.global_definition_rep then
            --3rd or 4th choice are possibilities
            --The_Next_Node is the topmost global_definition_rep; this is the new
            --start point for the search for a suitable reporting place.
            --find rightmost global_variable_clause
            The_Next_Node := Child_Node (Current_Node => The_Next_Node);
            if Syntax_Node_Type (Node => Next_Sibling (Current_Node => The_Next_Node)) = SP_Symbols.global_variable_clause then
               The_Next_Node := Next_Sibling (Current_Node => The_Next_Node);
            end if;
            --The_Next_Node is now rightmost global_variable_clause
            --now find right most gloabl variable in that clause
            --first advance to global_variable_list
            The_Next_Node :=
              Child_Node (Current_Node => Next_Sibling (Current_Node => Child_Node (Current_Node => The_Next_Node)));
            -- now find rightmost global variable
            if Syntax_Node_Type (Node => Next_Sibling (Current_Node => The_Next_Node)) = SP_Symbols.global_variable then
               The_Next_Node := Next_Sibling (Current_Node => The_Next_Node);
            end if;
         else --only place we can point is the subprogram declaration
            The_Next_Node := Parent_Node (Current_Node => Parent_Node (Current_Node => Node));
         end if;
      else
         SystemErrors.Fatal_Error
           (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
            Msg     => "Expect The_Next_Node = dependency_relation_rep OR NULL in FindLastItemInDependencyRelation");
      end if;
   else
      SystemErrors.Fatal_Error
        (Sys_Err => SystemErrors.Invalid_Syntax_Tree,
         Msg     => "Expect The_Next_Node =  null_import_list OR NULL in FindLastItemInDependencyRelation");
   end if;
   return Node_Position (Node => The_Next_Node);
end FindLastItemInDependencyRelation;
