--  Analysis for translation.
--  Copyright (C) 2009 Tristan Gingold
--
--  GHDL is free software; you can redistribute it and/or modify it under
--  the terms of the GNU General Public License as published by the Free
--  Software Foundation; either version 2, or (at your option) any later
--  version.
--
--  GHDL 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 GCC; see the file COPYING.  If not, write to the Free
--  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
--  02111-1307, USA.

with Iirs_Utils; use Iirs_Utils;
with Iirs_Walk; use Iirs_Walk;
with Disp_Vhdl;
with Ada.Text_IO;
with Errorout;

package body Trans_Analyzes is
   Driver_List : Iir_List;

   Has_After : Boolean;
   function Extract_Driver_Target (Target : Iir) return Walk_Status
   is
      Base : Iir;
      Prefix : Iir;
   begin
      Base := Get_Object_Prefix (Target);
      --  Assigment to subprogram interface does not create a driver.
      if Get_Kind (Base) = Iir_Kind_Interface_Signal_Declaration
        and then
        Get_Kind (Get_Parent (Base)) = Iir_Kind_Procedure_Declaration
      then
         return Walk_Continue;
      end if;

      Prefix := Get_Longuest_Static_Prefix (Target);
      Add_Element (Driver_List, Prefix);
      if Has_After then
         Set_After_Drivers_Flag (Base, True);
      end if;
      return Walk_Continue;
   end Extract_Driver_Target;

   procedure Extract_Has_After (Wf : Iir) is
   begin
      if Wf /= Null_Iir
        and then Get_Chain (Wf) = Null_Iir
        and then Get_Time (Wf) = Null_Iir
        and then Get_Kind (Get_We_Value (Wf)) /= Iir_Kind_Null_Literal
      then
         Has_After := False;
      else
         Has_After := True;
      end if;
   end Extract_Has_After;

   function Extract_Driver_Stmt (Stmt : Iir) return Walk_Status
   is
      Status : Walk_Status;
      pragma Unreferenced (Status);
   begin
      case Get_Kind (Stmt) is
         when Iir_Kind_Simple_Signal_Assignment_Statement =>
            Extract_Has_After (Get_Waveform_Chain (Stmt));
            Status := Walk_Assignment_Target
              (Get_Target (Stmt), Extract_Driver_Target'Access);
         when Iir_Kind_Conditional_Signal_Assignment_Statement =>
            declare
               Cond_Wf : Iir;
            begin
               Cond_Wf := Get_Conditional_Waveform_Chain (Stmt);
               while Cond_Wf /= Null_Iir loop
                  Extract_Has_After (Get_Waveform_Chain (Cond_Wf));
                  Cond_Wf := Get_Chain (Cond_Wf);
               end loop;
               Status := Walk_Assignment_Target
                 (Get_Target (Stmt), Extract_Driver_Target'Access);
            end;
         when Iir_Kind_Procedure_Call_Statement =>
            declare
               Call : constant Iir := Get_Procedure_Call (Stmt);
               Assoc : Iir;
               Formal : Iir;
               Inter : Iir;
            begin
               --  Very pessimist.
               Has_After := True;

               Assoc := Get_Parameter_Association_Chain (Call);
               Inter := Get_Interface_Declaration_Chain
                 (Get_Implementation (Call));
               while Assoc /= Null_Iir loop
                  Formal := Get_Formal (Assoc);
                  if Formal = Null_Iir then
                     Formal := Inter;
                     Inter := Get_Chain (Inter);
                  else
                     Formal := Get_Association_Interface (Assoc);
                  end if;
                  if Get_Kind (Assoc)
                    = Iir_Kind_Association_Element_By_Expression
                    and then
                    Get_Kind (Formal) = Iir_Kind_Interface_Signal_Declaration
                    and then Get_Mode (Formal) /= Iir_In_Mode
                  then
                     Status := Extract_Driver_Target (Get_Actual (Assoc));
                  end if;
                  Assoc := Get_Chain (Assoc);
               end loop;
            end;
         when others =>
            null;
      end case;
      return Walk_Continue;
   end Extract_Driver_Stmt;

   procedure Extract_Drivers_Sequential_Stmt_Chain (Chain : Iir)
   is
      Status : Walk_Status;
      pragma Unreferenced (Status);
   begin
      Status := Walk_Sequential_Stmt_Chain (Chain, Extract_Driver_Stmt'Access);
   end Extract_Drivers_Sequential_Stmt_Chain;

   procedure Extract_Drivers_Declaration_Chain (Chain : Iir)
   is
      Decl : Iir := Chain;
   begin
      while Decl /= Null_Iir loop

         --  Only procedures and impure functions may contain assignment.
         if Get_Kind (Decl) = Iir_Kind_Procedure_Body
           or else (Get_Kind (Decl) = Iir_Kind_Function_Body
                    and then
                      not Get_Pure_Flag (Get_Subprogram_Specification (Decl)))
         then
            Extract_Drivers_Declaration_Chain (Get_Declaration_Chain (Decl));
            Extract_Drivers_Sequential_Stmt_Chain
              (Get_Sequential_Statement_Chain (Decl));
         end if;

         Decl := Get_Chain (Decl);
      end loop;
   end Extract_Drivers_Declaration_Chain;

   function Extract_Drivers (Proc : Iir) return Iir_List
   is
   begin
      Driver_List := Create_Iir_List;
      Extract_Drivers_Declaration_Chain (Get_Declaration_Chain (Proc));
      Extract_Drivers_Sequential_Stmt_Chain
              (Get_Sequential_Statement_Chain (Proc));

      return Driver_List;
   end Extract_Drivers;

   procedure Free_Drivers_List (List : in out Iir_List)
   is
      El : Iir;
   begin
      for I in Natural loop
         El := Get_Nth_Element (List, I);
         exit when El = Null_Iir;
         Set_After_Drivers_Flag (Get_Object_Prefix (El), False);
      end loop;
      Destroy_Iir_List (List);
   end Free_Drivers_List;

   procedure Dump_Drivers (Proc : Iir; List : Iir_List)
   is
      use Ada.Text_IO;
      use Errorout;
      El : Iir;
   begin
      Put_Line ("List of drivers for " & Disp_Node (Proc) & ":");
      Put_Line (" (declared at " & Disp_Location (Proc) & ")");
      for I in Natural loop
         El := Get_Nth_Element (List, I);
         exit when El = Null_Iir;
         if Get_After_Drivers_Flag (Get_Object_Prefix (El)) then
            Put ("*  ");
         else
            Put ("   ");
         end if;
         Disp_Vhdl.Disp_Vhdl (El);
         New_Line;
      end loop;
   end Dump_Drivers;

end Trans_Analyzes;