-------------------------------------------------------------------------------
-- (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.
--
--=============================================================================

with SystemErrors, LexTokenStacks;

separate (Dictionary)
function GetAnyPrefixNeeded
  (Sym       : Symbol;
   Scope     : Scopes;
   Separator : String)
  return      E_Strings.T
is
   Prefix                        : E_Strings.T;
   Declared_Scope, Current_Scope : Scopes;
   Sym_Local                     : Symbol;

   function Is_Abstract_Proof_Function_In_Local_Scope (Sym : Symbol) return Boolean
   --# global in Dict;
   is
   begin
      return RawDict.GetSymbolDiscriminant (Sym) = ImplicitProofFunctionSymbol
        and then RawDict.Get_Subprogram_Implicit_Proof_Function
        (The_Subprogram => RawDict.GetImplicitProofFunctionAdaFunction (Sym),
         Abstraction    => IsAbstract) =
        Sym
        and then RawDict.Get_Subprogram_Implicit_Proof_Function
        (The_Subprogram => RawDict.GetImplicitProofFunctionAdaFunction (Sym),
         Abstraction    => IsRefined) /=
        NullSymbol;
   end Is_Abstract_Proof_Function_In_Local_Scope;

begin -- GetAnyPrefixNeeded
   if (RawDict.GetSymbolDiscriminant (Sym) = Type_Symbol
         and then RawDict.Get_Type_Info_Ref (Item => Sym) = Get_Unknown_Type_Mark) -- GAA External
     or else Sym = NullSymbol
     or else (RawDict.GetSymbolDiscriminant (Sym) = Variable_Symbol
                and then RawDict.Get_Variable_Info_Ref (Item => Sym) = Get_Null_Variable) then -- GAA External
      Prefix := E_Strings.Empty_String;
   elsif RawDict.GetSymbolDiscriminant (Sym) = Subprogram_Symbol
     and then RawDict.Get_Subprogram_Info_Ref (Item => Sym) = Dict.Subprograms.Unchecked_Conversion then -- GAA External

      -- special handling for Unchecked_Conversion which is a strange beast -
      -- child subprogram in 95 and 2005/KCG, library-level subprogram in 83
      case CommandLineData.Content.Language_Profile is
         when CommandLineData.SPARK95_Onwards =>
            Prefix := E_Strings.Copy_String (Str => "Ada");
         when CommandLineData.SPARK83 =>
            Prefix := E_Strings.Empty_String;
      end case;
   else
      -- if the symbol is of an access type we dereference it first (access will get put back
      -- in GenerateSimpleName
      if RawDict.GetSymbolDiscriminant (Sym) = Type_Symbol
        and then Is_Type (Type_Mark => RawDict.Get_Type_Info_Ref (Item => Sym)) -- GAA External
        and then RawDict.Get_Type_Discriminant (Type_Mark => RawDict.Get_Type_Info_Ref (Item => Sym)) = -- GAA External
        Access_Type_Item then
         Sym_Local := RawDict.Get_Type_Symbol
           (RawDict.Get_Type_Accesses (Type_Mark => RawDict.Get_Type_Info_Ref (Item => Sym))); -- GAA External
      else
         Sym_Local := Sym;
      end if;

      -- if the symbol is an implicit in stream associated with a protected own variable
      -- then the prefix is that applicable to the associated own variable
      if RawDict.GetSymbolDiscriminant (Sym_Local) = Implicit_In_Stream_Symbol then
         Sym_Local :=
           RawDict.Get_Variable_Symbol
           (Get_Own_Variable_Of_Protected_Implicit_In_Stream
              (The_Implicit_In_Stream => RawDict.Get_Implicit_In_Stream_Info_Ref (Item => Sym_Local)));
      end if;

      -- if the symbol is a special on loop entry variable we use the original variable
      -- to determine whether a prefix is needed
      if RawDict.GetSymbolDiscriminant (Sym_Local) = LoopEntryVariableSymbol then
         Sym_Local := RawDict.GetLoopEntryVariableOriginalVar (Sym_Local);
      end if;

      -- call to getmost enclosing object added so that when looking for
      -- prefix of X.F we do so in terms of X (the X.F part of the full name
      -- will be added by GenerateSimpleName
      Declared_Scope := GetScope (GetMostEnclosingObject (Sym_Local));
      if IsPredefinedScope (Declared_Scope) or else RawDict.GetSymbolDiscriminant (Sym_Local) = Quantified_Variable_Symbol then
         -- no prefix needed
         Prefix := E_Strings.Empty_String;
      else -- prefix may be needed so do search
         Current_Scope := Scope;
         loop
            exit when Current_Scope = Declared_Scope;
            exit when IsGlobalScope (Current_Scope);
            exit when IsPredefinedScope (Current_Scope);

            if RawDict.GetSymbolDiscriminant (GetRegion (Current_Scope)) = Package_Symbol then
               Current_Scope := Set_Visibility (The_Visibility => Privat,
                                                The_Unit       => GetRegion (Current_Scope));
            end if;

            exit when Current_Scope = Declared_Scope;

            if RawDict.GetSymbolDiscriminant (GetRegion (Current_Scope)) = Package_Symbol then
               Current_Scope := Set_Visibility (The_Visibility => Visible,
                                                The_Unit       => GetRegion (Current_Scope));
               exit;
            end if;
            Current_Scope := GetEnclosingScope (Current_Scope);
         end loop;

         -- The new error handling scheme intrduced by SEPR 1883 defers generation of error strings until after
         -- an entire source file has been processed.  This causes ones spceial-case problem: a package spec and
         -- its body are in a single file, the package declares an own variable, there is an error in the
         -- package spec involvignthe own variable, the own variable is declared in the package body.  This
         -- combination mens that we  might have Current_Scope equal to VISIBLE part of P and Declared_Scope
         -- being LOCAL scope of P.  This then triggers unwanted generation of a prefix.
         -- In all other situations, this "inversion" can't happen.  We don't want a prefix if both declaration
         -- and viewpoint are in the same REGION.  The test that the SCOPES be identical is too strict.  So we
         -- replace "if Current_Scope = Declared_Scope" with:
         if GetRegion (Current_Scope) = GetRegion (Declared_Scope)
           and then not Is_Abstract_Proof_Function_In_Local_Scope (Sym => Sym_Local) then
            Prefix := E_Strings.Empty_String;
         else
            Prefix :=
              GenerateSimpleName (Item      => GetRegion (GetScope (GetMostEnclosingObject (Sym_Local))),
                                  Separator => Separator);
         end if;
      end if;
   end if;
   return Prefix;
end GetAnyPrefixNeeded;
