with Ada.Text_Io;
use Ada.Text_Io;
with Ada.Strings.Unbounded;
use Ada.Strings.Unbounded;
with Gui.Window;
with Gui.Widget;
with Gui.Widget.Button;
with Gui.Menu;
with Ada.Characters.Handling;
use Ada.Characters.Handling;
with Ada.Strings.Fixed;
use Ada.Strings.Fixed;

package body Novice_Mode is
   type Ada_File is array(Natural range <>) of Unbounded_String;
   
   function Get_Line (File : in File_Type) return Unbounded_String is
      Line : String (1..80);
      Last : Natural;
   begin
      Get_Line (
         File => File,
         Item => Line,
         Last => Last);
      if Last < Line'Last then
         return To_Unbounded_String(Line(1..Last));
      else
         return Line & Get_Line(File);
      end if;
   end Get_Line;
         
   function Count_Lines (Filename : in String) return Natural is
      Count : Natural := 0;
      File : File_Type;
   begin
      Open(File => File, Name => Filename, Mode => In_File);
      while not End_Of_File(File) loop
         Skip_Line(File);
         Count := Count + 1;
      end loop;
      Close(File);
      return Count;
   exception when others =>
      return Count;
   end Count_Lines;
   
   procedure Read_File(
         Filename : in     String;
         Lines    :    out Ada_File) is
      File : File_Type;
   begin
      if Lines'Length > 0 then
         Open (File => File, Name => Filename, Mode => In_File);
         for I in Lines'range loop
            Lines(I) := Get_Line(File);
         end loop;
         Close(File);
      end if;
   end Read_File;
   
   --------------------
   -- Generate_Files --
   --------------------

   procedure Generate_Files
     (Window_Name : in String;
      Window      : in Gui.Window.GUI_Window)
   is
      Spec_Count : Natural := Count_Lines(window_name & "_actions.ads");         
      Body_Count : Natural := Count_Lines(Window_Name & "_actions.adb");
      Spec_Lines : Ada_File(1..Spec_Count); 
      Body_Lines : Ada_File(1..Body_Count);
      Spec_File  : File_Type; 
      Body_File  : File_Type;
      Widgets    : Gui.Widget.Widget_List := Window.Widget_List;
      Output_Something : Boolean := False;
      procedure Output_Headers_Or_Previous is
      begin
         if Spec_Count = 0 then
            Put_Line (
               Spec_File,
               "with Mcc.Gui.Widget.Button;");
            Put_Line (
               Spec_File,
               "with Mcc.Gui.Menu;");
            Put_Line (
               Spec_File,
               "package " & Window_Name & "_Actions is");
         else
            for I in Spec_Lines'range loop
               exit when Index(
                  Source  => To_Lower(To_String(Spec_Lines(I))),
                  Pattern => "end " & Window_Name & "_actions") > 0;
               Put_Line (
                  File => Spec_File,
                  Item => To_String(Spec_Lines(I)));
            end loop;      
            for I in Body_Lines'range loop
               exit when Index(
                  Source  => To_Lower(To_String(Body_Lines(I))),
                  Pattern => "end " & Window_Name & "_actions") > 0;
               Put_Line (
                  File => Body_File,
                  Item => To_String(Body_Lines(I)));
            end loop;      
            Output_Something := True;
         end if;
         if Body_Count = 0 then
            Put_Line (
               Body_File,
               "with " & Window_Name & "_Window;");
            Put_Line (
               Body_File,
               "package body " & Window_Name & "_Actions is");
         end if;
      end Output_Headers_Or_Previous;
      
      procedure Update_Button (Widget : Gui.Widget.Button.Button'Class) is
         Found : Boolean;
      begin
         -- if this wasn't specified to be in the actions package,
         -- then ignore it
         if Index(To_Lower(Widget.Action.all),
               To_Lower(Window_Name) & "_actions") <= 0 then
            return;
         end if;
         Output_Something := True;
         Found := False;
         for I in Spec_Lines'range loop
            if Index(To_Lower(To_String(Spec_Lines(I))),
                  "procedure " & To_Lower(Widget.Name.All) & "_pushed") > 0 then
               Found := True;
            end if;
         end loop;
         if not Found then
            Put_Line(
               File => Spec_File,
               Item => "   procedure " & Widget.Name.all & "_Pushed" &
               "(Obj : in out Mcc.Gui.Widget.Button.Button'Class);");
         end if;
         for I in Body_Lines'range loop
            if Index(To_Lower(To_String(Body_Lines(I))),
                  "procedure " & To_Lower(Widget.Name.All) & "_pushed") > 0 then
               Found := True;
            end if;
         end loop;
         if not Found then
            New_Line(Body_File);
            Put_Line(
               File => Body_File,
               Item => "   procedure " & Widget.Name.all & "_Pushed" &
               "(Obj : in out Mcc.Gui.Widget.Button.Button'Class) is");
            Put_Line(
               File => Body_File,
               Item => "   begin");
            Put_Line(
               File => Body_File,
               Item => "      null;");
            Put_Line(
               File => Body_File,
               Item => "   end " & Widget.Name.all & "_Pushed;");
         end if;
      end Update_Button;
      function Already_Present_Callback (
         Spec        : in Boolean;
         Action_Name : in String;
         Name        : in String) return Boolean is
      begin
         -- if not a {Window_Name}_Actions. then skip
         if Index(To_Lower(Action_Name),To_Lower(Window_Name) & "_actions.") <= 0 then
            return True;
         end if;
         if Spec then
            for I in Spec_Lines'range loop
               if Index(To_Lower(To_String(Spec_Lines(I))),
                     "procedure " & To_Lower(Name) & "_selected") > 0 then
                  return True;
               end if;
            end loop;
            return False;
         else
            for I in Body_Lines'range loop
               if Index(To_Lower(To_String(Body_Lines(I))),
                     "procedure " & To_Lower(Name) & "_selected") > 0 then
                  return True;
               end if;
            end loop;
            return False;
         end if;
      end Already_Present_Callback;
   begin
      Read_File (
         Filename => Window_Name & "_actions.ads",
         Lines    => Spec_Lines);  
      Read_File (
         Filename => Window_Name & "_actions.adb",
         Lines    => Body_Lines);
      Create (
         File => Body_File,
         Mode => Out_File,
         Name => Window_Name & "_actions.adb");
      Create (
         File => Spec_File,
         Mode => Out_File,
         Name => Window_Name & "_actions.ads");
      Output_Headers_Or_Previous;
      declare
         Current_Widget_Position : Gui.Widget.Widget_List_Package.Position;
         Found                   : Boolean := False;
         Widget_Ptr              : Gui.widget.Widget_Access;
      begin
         Current_Widget_Position := Gui.Widget.Widget_List_Package.First(
            L => Widgets);
         while not Gui.Widget.Widget_List_Package.Ispastend(
            L => Widgets,
            P => Current_Widget_Position) loop
            Widget_Ptr := Gui.Widget.Widget_List_Package.Retrieve(
               L => Widgets,
               P => Current_Widget_Position);      
            if Widget_Ptr.all in Gui.Widget.button.Button'Class then
               Update_Button(
                  Gui.widget.button.Button'Class(Widget_Ptr.all));
            end if;
            Gui.Widget.Widget_List_Package.Goahead(
               L => Widgets,
               P => Current_Widget_Position);
         end loop;
      end;
      Gui.Menu.Generate_Menu_Novice(
         Menu      => Window.Menu,
         Spec_File => Spec_File,
         Body_File => Body_File,
         Callback  => Already_Present_Callback'Unrestricted_Access);
         
      Put_Line (
         File => Body_File,
         Item => "end " & Window_Name & "_Actions;");
      if not Output_Something then
         Put_Line (
            File => Spec_File,
            Item => "   pragma Elaborate_Body;");
      end if;
      Put_Line (
         File => Spec_File,
         Item => "end " & Window_Name & "_Actions;");
         
      Close (Body_File);
      Close (Spec_File);
   end Generate_Files;

end Novice_Mode;

