with Gtk.Adjustment;                    use Gtk.Adjustment;
with Gtk.Dialog;                        use Gtk.Dialog;
with Gnat.Os_Lib;                       use Gnat.Os_Lib;
with System.Os_Constants;               use System.Os_Constants;
with Gtk.Scrolled_Window;               use Gtk.Scrolled_Window;
with Gtk.Enums;                         use Gtk.Enums;
with Ada.Strings.Fixed;                 use Ada.Strings.Fixed;
use Ada.Strings;

with Gtk.Widget;                        use Gtk.Widget;

with Gtk.Text_Buffer;                   use Gtk.Text_Buffer;
with Gtk.Text_Iter;                     use Gtk.Text_Iter;
with Gtk.Text_Mark;                     use Gtk.Text_Mark;


with Gtk.Text_View;                     use Gtk.Text_View;
with Gtk.Frame;                         use Gtk.Frame;
with Gtk.Aspect_Frame;                  use Gtk.Aspect_Frame;

with Gtk.Gentry;                        use Gtk.Gentry;

with Gtk.Stock;                         use Gtk.Stock;

with Gtk.Action_Group;
pragma Elaborate_All(Gtk.Action_Group);
use Gtk.Action_Group;

with Gtk.Action;                        use Gtk.Action;

with Glib;                              use Glib;
with Gtk.UI_Manager;                    use Gtk.UI_Manager;
with Gtk.Box;                           use Gtk.Box;
with Glib.Error;                        use Glib.Error;
with Gtk.Handlers;                      use Gtk.Handlers;

with Gtk.Window;                        use Gtk.Window;
with Gtk.Main;

with Gn.Identity;                       use Gn.Identity;
with Gn.Logos.X.Gtk_lib;                use Gn.Logos.X.Gtk_lib;
with Gn.Logos.Gnu_Ada;                  use Gn.Logos.Gnu_Ada;
with Gn.Entity;                         use Gn.Entity;

with System.Address_To_Access_Conversions;
with Text_Io;                           use Text_Io;
package body Gn.Main is
            
   type Main_Record;
   
   task type Main_Process (Main : access Main_Record) is
      entry Init;      
      entry Ok;
      entry Halt;
      entry Receive
	(Line         : in     String);
      entry Send
	(Response     :    out Std_String;
	 Command_Line :    out Ada_Command_Access);
   end Main_Process;
   
   type State_Flags_Enum is (Initialized, End_Of_Task);
   
   type State_Register is array (State_Flags_Enum) of Boolean;
   
   
   type Main_Record is tagged limited
      record	 
	 Id                 : Identity_Access;
	 
	 Window             : Gtk_Window;	 	 	 
	 Main_Vbox          : Gtk_Vbox;	 	 
	 Menus_Hbox         : Gtk_Hbox;	 
	 Lang_Actions       : Gtk_Action_Group;
	 Lang_UI            : Gtk_UI_Manager;
	 
	 Main_Actions       : Gtk_Action_Group;
	 Main_UI            : Gtk_UI_Manager;
	 
	 Process            : Main_Process(Main_Record'Access);
	 States             : State_Register := (False, False);
	 
	 --Applications       : Gtk_Application;
	 
	 Lang               : Lang_Enum := En;
	 
	 
	 Dialog_Frame       : Gtk_Aspect_Frame;
	 Dialog_Vbox        : Gtk_Vbox;
	 
	 
	 
	 Responses_Text      : Gtk_Text_View;
	 Scroll_Text         : Gtk_Scrolled_Window;
	 Responses_Frame     : Gtk_Frame;
	 Responses_Buffer    : Gtk_Text_Buffer;
	 
	 Line_Frame         : Gtk_Frame;
	 Main_Line          : Gtk_Gentry;
	 
	 
      end record;
   
   
   package Main_Conversions is
      new System.Address_To_Access_Conversions
     (Main_Record);
   
   subtype Main_Pointer is Main_Conversions.Object_Pointer;
   
   type Env_Var_Array is array (Positive range <>) of access String;
   
   Windows_Environment : constant Env_Var_Array(1..3) :=
     (new String '("HOMEPATH"),
      new String '("USERNAME"),
      new String '("PATH"));
   Other_Environment : constant Env_Var_Array(1..3) :=
     (new String '("HOME"),
      new String '("LOGNAME"),
      new String '("PATH"));
   
   
   procedure Initialize(Id : out Identity_Access) is
   begin
      Id := new Identity_Record;
      -- if get_env /= empty then      
      case Target_Os is
	 when Windows =>
	    for Var in Windows_Environment'Range loop
	       Id.Entities(Var) := new Entity_Record;
	       Id.Entities(Var).Name := 
		 new String ' (Windows_Environment(Var).all);
	       Id.Entities(Var).Image := 
		 new String ' (Getenv(Windows_Environment(Var).all).all);	       
	    end loop;
	 when Other_Os =>	    
	    for Var in Other_Environment'Range loop
	       Id.Entities(Var) := new Entity_Record;
	       Id.Entities(Var).Name := 
		 new String ' (Other_Environment(Var).all);
	       Id.Entities(Var).Image := 
		 new String ' (Getenv(Other_Environment(Var).all).all);
	    end loop;
	 when others =>
	    null;
      end case;
   end Initialize;
   
   
   task body Main_Process is
      
      Input_String : Std_String := (others => ' ');
      
      Ada_Command  : Ada_Command_Access;
   begin
      accept Init do
	 Initialize(Main.Id);
	 -- if file_exist(Main_Identity(1).Image.all & Dir_Separator & 
	 --       Identity_Filename) then
	 --  Load
	 --    (Main.Identity, 
	 --     Main_Identity(1).Image.all & Dir_Separator & 
	 --       Identity_Filename);	 	 
         -- end if;
	 Main.States(Initialized) := True;	 
      end Init;                  
           
      loop
	 select	        
	    accept Ok;
	 or
	    accept Halt;
	    exit;
	 or
	    accept Receive
	      (Line         : in     String) do
	       Move(Line, Input_String, Ada.Strings.Error, Left, ' ');	       
	       Dialog.Respond(Input_String, Ada_Command);
	    end Receive;	    
	 or	    
	    accept Send
	      (Response     :    out Std_String;
	       Command_Line :    out Ada_Command_Access) do
	       Response := Input_String;
	       Command_Line := Ada_Command;
	    end Send;
	 end select;
      end loop;
   end Main_Process;
   
   function Main_Quit (Window : access Gtk_Window_Record'Class;
		       Main : Main_Pointer) return Boolean is
   begin
      Destroy(Window);
      Gtk.Main.Main_Quit;
      return False;
   end Main_Quit;
     
   
   package Gn_Main_User_Return_CB is
      new Gtk.Handlers.User_Return_Callback
     (Gtk_Window_Record, Boolean, Main_Pointer);
   
   package Gn_Main_User_CB is
      new Gtk.Handlers.User_Callback
     (Gtk_Widget_Record, Main_Pointer);
      
   function Make_Menu(Lang : in Lang_Enum) return Action_Entry_Array;
   
   procedure On_Entry_Activated(The_Entry : access Gtk_Widget_Record'Class;
				Gn_Main   : in Main_Pointer);
   
   
   procedure Set_Lang_English (Action, Gn_Main : in System.Address);
   pragma Convention (C, Set_Lang_English);
   --  Called when Lang->English actions has been selected
   
   procedure Set_Lang_French (Action, Gn_Main : in System.Address);
   pragma Convention (C, Set_Lang_French);
   --  Called when Lang->French actions has been selected
   
   
   
   Main_Menu_Entries : access Action_Entry_Array;
   
   Lang_Menu_Entries : constant Action_Entry_Array :=
     (1 => Create (Name => "LangMenu",        Label => "_Lang"),
      2 => Create (Name => "English",                   
		   --Sock_Id     =>,
		   Label       => "_English",
		   --Accelerator => "<control>N",
		   Tooltip     => "English",
		   Callback    => Set_Lang_English'Access),
      3=> Create (Name => "French",                   
		  --Sock_Id     =>,
		  Label       => "_Français",
		  --Accelerator => "<control>N",
		  Tooltip     => "French",
		  Callback    => Set_Lang_French'Access));
   
   Lang_UI_Info : constant String :=
     "<ui>"
     & "  <menubar name='MenuBar'>"
     & "    <menu action='LangMenu'>"          
     & "      <menuitem action='English'/>"      
     & "      <menuitem action='French'/>"
     & "    </menu>"     
     & "  </menubar>"
     & "</ui>";
   
   
   
   Main_UI_Info : constant String :=
     "<ui>"
     & "  <menubar name='MenuBar'>"
     & "    <menu action='FileMenu'>"          
     & "      <menuitem action='Quit'/>"
     & "    </menu>"     
     & "  </menubar>"
     & "</ui>";
   
   procedure Gtk_New(Gn_Main : Main_Pointer) is
      
     Error : aliased Gerror;
   
   begin
      
      Gtk_New(Gn_Main.Window);
      
      Set_Default_Size(Gn_Main.Window, 480, 300);
      
      Gn_Main_User_Return_CB.Connect
	(
	 Gn_Main.Window,
	 "delete-event",
	 Main_Quit'Access,
	 Gn_Main
	);
      
      
      ---------------------------------------------------
      --             Language menu application         --
      ---------------------------------------------------
      Gtk_New (Gn_Main.Lang_Actions, "Actions");
      Add_Actions (Gn_Main.Lang_Actions, Lang_Menu_Entries,
		   Main_Conversions.To_Address (Gn_Main));
      
      Gtk_New (Gn_Main.Lang_UI);
      
      Insert_Action_Group (Gn_Main.Lang_UI, Gn_Main.Lang_Actions, 0);

      Add_Accel_Group
	(Gn_Main.Window, Get_Accel_Group (Gn_Main.Lang_UI));

      if Add_UI_From_String
      	(Gn_Main.Lang_UI, Lang_UI_Info, Error'Unchecked_Access) = 0 then
      	 Put_Line
      	   ("Building menus failed: " & Get_Message (Error));
      	 Error_Free (Error);
      end if;            
      
      case Gn_Main.Lang is
	  when En =>
	     Set_Sensitive(Get_Action(Gn_Main.Lang_Actions, "English"), False);
	 when Fr =>
	    Set_Sensitive(Get_Action(Gn_Main.Lang_Actions, "French"), False);
      end case;
      
      Gtk_New_Hbox(Gn_Main.Menus_Hbox, False, 0);      
      Gtk.Box.Pack_End
	(Gn_Main.Menus_Hbox,
	 Get_Widget (Gn_Main.Lang_UI, "/MenuBar"),
	 Expand => False, Padding => 0);      
      
      ---------------------------------------------------
      --             Main menu application             --
      ---------------------------------------------------
      Main_Menu_Entries := new Action_Entry_Array ' (Make_Menu(Gn_Main.Lang));
      
      
      Gtk_New (Gn_Main.Main_Actions, "Actions");
      Add_Actions (Gn_Main.Main_Actions, Main_Menu_Entries.all,
		   Main_Conversions.To_Address (Gn_Main));
      
      Gtk_New (Gn_Main.Main_UI);
      
      Insert_Action_Group (Gn_Main.Main_UI, Gn_Main.Main_Actions, 0);

      Add_Accel_Group
	(Gn_Main.Window, Get_Accel_Group (Gn_Main.Main_UI));

      if Add_UI_From_String
      	(Gn_Main.Main_UI, Main_UI_Info, Error'Unchecked_Access) = 0 then
      	 Put_Line
      	   ("Building menus failed: " & Get_Message (Error));
      	 Error_Free (Error);
      end if;                  
		                
      Gtk.Box.Pack_Start
	(Gn_Main.Menus_Hbox,
	 Get_Widget (Gn_Main.Main_UI, "/MenuBar"),
	 Expand => False, Padding => 0);      
                  
      --              End Menus                        --
      ---------------------------------------------------
      
      
      Gtk_New_Vbox(Gn_Main.Main_Vbox, False, 4);      
      
      Pack_Start(Gn_Main.Main_Vbox, Gn_Main.Menus_Hbox, False);
      
      --------------------------------------------------
      --            Dialog Aspect_Frame               --
      --------------------------------------------------
                  
      
      Gtk_New(Gn_Main.Responses_Buffer);
      Gtk_New(Gn_Main.Responses_Text, Gn_Main.Responses_Buffer);
      
      Set_Editable(Gn_Main.Responses_Text, False);      
      
      Gtk_New(Gn_Main.Scroll_Text);
      Add(Gn_Main.Scroll_Text, Gn_Main.Responses_Text);
      Set_Size_Request(Gn_Main.Scroll_Text, 335, 155);     
      Set_Wrap_Mode(Gn_Main.Responses_Text,
		    --Wrap_Char
		    Wrap_Word
		    --Wrap_Word_Char
		   );

      
      Gtk_New(Gn_Main.Main_Line);
      Set_Width_Chars(Gn_Main.Main_Line, 48);
      Set_Max_Length(Gn_Main.Main_Line, 256);
      Set_Activates_Default(Gn_Main.Main_Line, True);
      
      Gn_Main_User_CB.Connect
	(
	 Gn_Main.Main_Line,
	 "activate",
	 On_Entry_Activated'Access,
	 Gn_Main
	);
      
      
      case Gn_Main.Lang is
	 when En =>
	    
	    Gtk_New(Gn_Main.Responses_Frame, "Responses : ");
	    
	    Gtk_New(Gn_Main.Dialog_Frame, "Dialog", 0.5, 0.5, 2.0, True);
	    
	    Gtk_New(Gn_Main.Line_Frame, "Text : ");
	 when Fr =>
	    
	    Gtk_New(Gn_Main.Responses_Frame, "Réponses : ");
	    
	    Gtk_New(Gn_Main.Dialog_Frame, "Dialogue", 0.5, 0.5, 2.0, True);
	    
	    Gtk_New(Gn_Main.Line_Frame, "Texte : ");
      end case;
      
      
      Set_Size_Request(Gn_Main.Line_Frame, 335, 35);
                  
      Gtk_New_Vbox(Gn_Main.Dialog_Vbox, False);
                  
      Add(Gn_Main.Responses_Frame, Gn_Main.Scroll_Text);
      Pack_Start(Gn_Main.Dialog_Vbox, Gn_Main.Responses_Frame, True);
      
      Add(Gn_Main.Line_Frame, Gn_Main.Main_Line);
      Pack_Start(Gn_Main.Dialog_Vbox, Gn_Main.Line_Frame, True);
      
      Add(Gn_Main.Dialog_Frame, Gn_Main.Dialog_Vbox);
      
      Pack_start(Gn_Main.Main_Vbox, Gn_Main.Dialog_Frame, False);
      Add (Gn_Main.Window, Gn_Main.Main_Vbox);
      
      Set_Focus(Gn_Main.Window, Gtk_Widget(Gn_Main.Main_Line));
      
      Show_All(Gn_Main.Window);
   end Gtk_New;
   
   
   procedure Main is
      
      Gn_Main : constant Main_Pointer := new Main_Record;
      
      Reuse : Boolean := False;
      
   begin            
      Dialog.Init(Reuse);
      
      Gn_Main.Process.Init;
      if not Gn_Main.States(Initialized) then
	 return;
      end if;
      Dialog.New_Dialog(Gn_Main.Id);
      loop
	 
	 declare
	    
	    ----------------------
	    --                  --
	    --                  --
	    --                  --
	    ----------------------	    
	 begin
	    
	    Gtk.Main.Init;
	    
	    Gtk_New(Gn_Main);
	    	    
	    Gn_Main.Process.Ok;
	    
	    Gtk.Main.Main;
	 end;
	 
	 exit when Gn_Main.States(End_Of_Task);
	 
	 
      end loop;
      Dialog.End_Dialog;
      Gn_Main.Process.Halt;      
      Dialog.Halt;
   end Main;
   
   
   
   procedure Quit (Action, Gn_Main : in System.Address);
   pragma Convention (C, Quit);
   --  Called when Quit actions has been selected
   
   
   procedure Quit (Action, Gn_Main : in System.Address) is
      pragma Unreferenced (Action);
      
      Main : constant Main_Pointer := Main_Conversions.To_Pointer(Gn_Main);
   begin
      
      Main.States(End_Of_Task) := True;
      
      Destroy(Main.Window);
      
      Gtk.Main.Main_Quit;      
   end Quit;
      
   
   
   
   function Make_Menu(Lang : in Lang_Enum) return Action_Entry_Array is      
            
      Menu : access Action_Entry_Array;
   begin
      case Lang is
	 when En =>
	    Menu := new Action_Entry_Array '
	      (1 => Create (Name => "FileMenu",        Label => "_File"),
	       2 => Create (Name => "Quit",
			    Stock_Id    => Stock_Quit,
			    Label       => "_Quit",
			    Accelerator => "<control>Q",
			    Tooltip     => "Quit",
			    Callback    => Quit'Access));
	 when Fr =>
	    
	    Menu := new Action_Entry_Array '
	      (1 => Create (Name => "FileMenu",        Label => "_Fichier"),
	       2 => Create (Name => "Quit",
			    Stock_Id    => Stock_Quit,
			    Label       => "_Quitter",
			    Accelerator => "<control>Q",
			    Tooltip     => "Quit",
			    Callback    => Quit'Access));
      end case;
      
      return Menu.all;
      
   end Make_Menu;
   
   procedure On_Entry_Activated(The_Entry : access Gtk_Widget_Record'Class;
				Gn_Main   : in Main_Pointer) is
      
      The_Response : Std_String := (others => ' ');
      The_Last     : Natural := 0;
      
      Input_String : constant String := Get_Text(Gtk_Entry(The_Entry));
      
      Iter, Top, Bot : Gtk_Text_Iter;            
      Mark : Gtk.Text_Mark.Gtk_Text_Mark;
      
      Ada_Command : Ada_Command_Access;
      
      Ada_Response : Gtk_Response_Type := Gtk_Response_None;
            
   begin
      
      Set_Text(Gtk_Entry(The_Entry), "");
      
      Gn_Main.Process.Receive(Input_String);
      Gn_Main.Process.Send(The_Response, Ada_Command);
      
      The_Last := Index_Non_Blank(The_Response, Backward);
      
      Get_End_iter(Gn_Main.Responses_Buffer, Iter);
      if Ada_Command = null then
	 Insert(Gn_Main.Responses_Buffer,
		Iter,
		The_Response(The_Response'First .. The_last) & Character'Val(13));	 
      else	 	 
      	 Insert(Gn_Main.Responses_Buffer,
      		Iter,
      		The_Response(The_Response'First .. The_last) & Character'Val(13));	 
	 
	 declare
	    Iteration : Natural := 0;
	 begin
	    loop
	       begin
		  		  
		  Ada_Response := Ada_Command.Command(Ada_Command.Parameters, Gn_Main.Window, Gn_Main.Id, Iteration);
		  
		  
		  case Ada_Response is
		     when Gtk_Response_None =>
			case Gn_Main.Lang is
			   when Fr =>
			      Move("Aucun ! ", The_Response, Ada.Strings.Error, Left, ' ');
			   when En =>
			      Move("None ! ", The_Response, Ada.Strings.Error, Left, ' ');
			end case;
		     when Gtk_Response_No =>
			case Gn_Main.Lang is
			   when Fr =>
			      
			      Move("Non ! ", The_Response, Ada.Strings.Error, Left, ' ');
			      
			   when En =>
			      Move("No !", The_Response, Ada.Strings.Error, Left, ' ');
			      
			end case;
		     when Gtk_Response_Cancel =>
			case Gn_Main.Lang is
			   when Fr =>
			      
			      Move("Annulé ! ", The_Response, Ada.Strings.Error, Left, ' ');
			      
			   when En =>
			      Move("Canceled !", The_Response, Ada.Strings.Error, Left, ' ');
			      
			end case;
		     when Gtk_Response_Yes =>
			case Gn_Main.Lang is
			   when Fr =>
			      
			      Move("Oui ! ", The_Response, Ada.Strings.Error, Left, ' ');
			      
			   when En =>
			      Move("Yes !", The_Response, Ada.Strings.Error, Left, ' ');
			      
			end case;
		     
		     when Gtk_Response_Ok =>
			Move("Ok ! ", The_Response, Ada.Strings.Error, Left, ' ');
		     when others =>
			null;
		  end case;
		  The_Last := Index_Non_Blank(The_Response, Backward);
		  Get_End_iter(Gn_Main.Responses_Buffer, Iter);
		  Insert(Gn_Main.Responses_Buffer,
			 Iter,
			 The_Response(The_Response'First .. The_last) & Character'Val(13));	 	 
		  
		  
		  
		  

	       exception
		  when others =>
		     Move("Exception !", The_Response, Ada.Strings.Error, Left, ' ');
		     The_Last := Index_Non_Blank(The_Response, Backward);
		     Insert(Gn_Main.Responses_Buffer,
			    Iter,
			    The_Response(The_Response'First .. The_last) & Character'Val(13));	 	 
	       end;
	       
	       case Ada_Response is
		  when Gtk_Response_None =>
		     null;
		  when Gtk_Response_No =>
		     exit;
		  when Gtk_Response_Yes =>
		     null;
		  when Gtk_Response_Cancel =>
		     exit;
		  when Gtk_Response_Ok =>
		     exit;
		  when others =>
		     null;
	       end case;
	       
	       -- Put(Character'Val(7));
	       
	       Get_Bounds(Gn_Main.Responses_Buffer, Top, Bot);
	       Mark := Create_Mark(Gn_Main.Responses_Buffer, Where => Bot);
	       Scroll_To_Mark(Gn_Main.Responses_Text, Mark, 0.0, false, 0.0, 0.0);
      
	       if Iteration = 2 then
		  Iteration := 0;
	       else
		  Iteration := Iteration + 1;
	       end if;
	    end loop;
	 end;
      end if;
      Get_Bounds(Gn_Main.Responses_Buffer, Top, Bot);
      Mark := Create_Mark(Gn_Main.Responses_Buffer, Where => Bot);
      Scroll_To_Mark(Gn_Main.Responses_Text, Mark, 0.0, false, 0.0, 0.0);
      	       
   exception
      when others =>
	 null;
   end On_Entry_Activated;
   
   procedure Set_Lang_English (Action, Gn_Main : in System.Address) is
      pragma Unreferenced (Action);                  
      
      Main : constant Main_Pointer := Main_Conversions.To_Pointer(Gn_Main);
   begin
      Main.Lang := En;
      Destroy(Main.Window);
      Gtk.Main.Main_Quit;
   end Set_Lang_English;
   
   procedure Set_Lang_French (Action, Gn_Main : in System.Address) is
      pragma Unreferenced (Action);
      
      Main : constant Main_Pointer := Main_Conversions.To_Pointer(Gn_Main);
   begin
      Main.Lang := Fr;
      Destroy(Main.Window);
      Gtk.Main.Main_Quit;
   end Set_Lang_French;
   
   
   
end Gn.Main;