-- elhoim is full object organizer with editor and command interpreter.
-- Elhoim is Copyright (C) 2023 Manuel De Girardi ; 
--
--   This program 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 of the License, or
--   (at your option) any later version.
--
--   This program 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 this program; if not, write to the Free Software
--   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
--
-- Date := "2023-05-26 17:40:38"
-- Version := "0.6.6b"
with Ada.Characters;
with Ada.Wide_Characters.Handling;
with Ada.Characters.Handling;
with Ada.Numerics.Discrete_Random;
with Ada.Strings.UTF_Encoding.Wide_Strings;
with Ada.Wide_Text_Io;
use Ada;

package body El.Lexics is
   use Ada.Characters.Handling;
   package body Tree is
      
      procedure Add(Item : in T_Item;
		    Tree : in out T_Tree) is
	 New_Node : constant Node_Access := new T_Node'(Item, null, null);
	 Key : constant T_Key := Key_Of(Item);
	 Current_Node : Node_Access;
      begin
	 if Tree.Root = null then
	    Tree.Root := New_Node;
	 else
	    Current_Node := Tree.Root;
	    loop
	       if Key < Key_Of(Current_Node.Item) then
		  if Current_Node.Left /= null then
		     Current_Node := Current_Node.Left;
		  else
		     Current_Node.Left := New_Node;
		     exit;
		  end if;
	       elsif Key > Key_Of(Current_Node.Item) then
		  if Current_Node.Right /= null then
		     Current_Node := Current_Node.Right;
		  else
		     Current_Node.Right := New_Node;
		     exit;
		  end if;
	       else
		  
		  return;
	       end if;
	    end loop;
	 end if;
	 Key_Index := Key_Index + 1;
      end Add;
      
      function Find(Key : in T_Key;
		    Tree : in T_Tree) return T_Item is
	 current_node : node_access;
      begin
	 if Tree.Root = null then
	    raise Specification_Error;
	 end if;
	 Current_Node := Tree.Root;
	 loop
	    if Key < Key_Of(Current_Node.Item) then
	       if Current_Node.Left = null then
		  raise Specification_Error;
	       end if;
	       Current_Node := Current_Node.Left;
	    elsif Key > Key_Of(Current_Node.Item) then
	       if Current_Node.Right = null then
		  raise Specification_Error;
	       end if;
	       Current_Node := Current_Node.Right;
	    else
	       return Current_Node.Item;
	    end if;
	 end loop;
      end Find;

   end Tree;
   
   
      package body Glossary is
      
      
      function Key_Count(Glossary : in Glossary_Type) return T_Language is
      begin
	 return T_Language(Key_Tree.Key_Index);
      end Key_Count;
      
      function "<"(Left, Right : in T_word) return Boolean is
	 Left_String, Right_String : Wide_String(1..80) :=
	   (others => Wide_Character'Val(32));
      begin
	 Left_String(1..Ada.Strings.Wide_Unbounded.Length(Left.word)) := Ada.Strings.Wide_Unbounded.To_Wide_String(Left.word);
	 Right_String(1..Ada.Strings.Wide_Unbounded.Length(Right.word)) := Ada.Strings.Wide_Unbounded.To_Wide_String(Right.word);
	 for I in 1..Positive'Min(Ada.Strings.Wide_Unbounded.Length(Left.word), Ada.Strings.Wide_Unbounded.Length(Right.word)) loop
	    if Left_String(I) < Right_String(I) then
	       return True;
	    elsif Left_String(I) > Right_String(I) then
	       return false;
	    end if;
	 end loop;
	 if Ada.Strings.Wide_Unbounded.Length(Left.word) < Ada.Strings.Wide_Unbounded.Length(Right.word) then
	    return True;
	 end if;
	 return False;
      end "<";

      function ">"(Left, Right : in T_word) return Boolean is
	 Left_String, Right_String : Wide_String(1..80) :=
	   (others => Wide_Character'Val(32));
      begin
	 Left_String(1..Ada.Strings.Wide_Unbounded.Length(Left.word)) := Ada.Strings.Wide_Unbounded.To_Wide_String(Left.word);
	 Right_String(1..Ada.Strings.Wide_Unbounded.Length(Right.word)) := Ada.Strings.Wide_Unbounded.To_Wide_String(Right.word);
	 for I in 1..Positive'Min(Ada.Strings.Wide_Unbounded.Length(Left.word), Ada.Strings.Wide_Unbounded.Length(Right.word)) loop
	    if Left_String(I) > Right_String(I) then
	       return True;
	    elsif Left_String(I) < Right_String(I) then
	       return false;
	    end if;
	 end loop;
	 if Ada.Strings.Wide_Unbounded.Length(Left.word) > Ada.Strings.Wide_Unbounded.Length(Right.word) then
	    return True;
	 end if;
	 return False;
      end ">";

      function Key_Of(Word : T_Word) return T_Word is
      begin
	 return Word;
      end Key_Of;



      function inf(Left, Right : in T_word) return Boolean is
      begin
	 if Left.Item < Right.Item then
	    return True;
	 else
	    return False;
	 end if;
      end inf;

      function sup(Left, Right : in T_word) return Boolean is
      begin
	 if Left.Item > Right.Item then
	    return True;
	 else
	    return False;
	 end if;
      end sup;



      function Image(Item : T_Language;
		     Glossary : in Glossary_type) return Wide_String is
	 word : T_Word;
      begin
	 word.Item := Item;
	 return Ada.Strings.Wide_Unbounded.To_Wide_String(key_Tree.Find(word, Glossary.key_Root).Word);
      exception
	 when others =>
	    raise Specification_Error;
      end Image;


      function Keyword(Word : in Wide_String;
		       Glossary : in Glossary_type) return T_Language is
	 Item : T_Word;
      begin
	 item.word := Ada.Strings.Wide_Unbounded.To_Unbounded_Wide_String(word);
	 Item := word_Tree.Find(Item, Glossary.word_Root);
	 return Item.item;
      exception
	 when others =>
	    raise Specification_Error;

      end Keyword;
      
      
      -- Ajouter un item.
      procedure Add(Word : in Wide_String;
		    key : in T_language;
		    Glossary : in out Glossary_Type) is
	 Item : constant T_Word := (Item => Key,
				    Word => Ada.Strings.Wide_Unbounded.To_Unbounded_Wide_String(Word));
	 
	 
      begin
	 
	 begin
	    
	    Word_Tree.Add(item, Glossary.Word_Root);
	    
	    key_Tree.Add(item, Glossary.Key_Root);
	    
	    Glossary.Key_Count := T_Language'Succ(Glossary.Key_Count);
	    
	    
	 exception
	    when others  => 
	       
	       null;
	 end;
      end Add;
      
      
      
      procedure Initialize(Glossary : in out Glossary_Type;
			   Filename : in String) is
	 
	 type T_table is array (natural range <>) of T_word;
	 type Table_Access is access T_Table;

	 
	 procedure Tree_Loader(Table : in out Table_access;
			       B_Inf, B_Sup : in Natural) is

	    Middle : constant Natural := b_inf + (b_sup-b_inf) / 2;
	    New_B_Inf : Natural;
	    New_B_Sup : Natural;
	 begin	    
	    
	    begin	       	
	       
	       Add(Ada.Strings.Wide_Unbounded.To_Wide_String(Table(Middle).word), Table(Middle).item, Glossary);
	    exception
	       when Specification_Error => 
		  null;
		  
	    end;

	    if Middle-1 >= B_Inf then

	       new_b_sup := middle-1;
	       Tree_Loader(Table => Table,
			   B_Inf => b_inf,
			   B_Sup => new_b_sup);

	    end if;

	    if  middle+1 <= B_sup then
	       
	       new_b_inf := middle+1;
	       Tree_Loader(Table => Table,
			   B_Inf => new_b_inf,
			   B_Sup => b_sup);
	       
	    end if;
	    
	 end Tree_Loader;
	 
	 
	 use Ada.Wide_Characters.Handling;
	 
	 procedure Random_Table (Table : in Table_Access) is
	    
	    subtype Index_Type is Integer range Table'First..Table'Last;
	    
	    package Table_Rand is new Ada.Numerics.Discrete_Random(Index_Type);
	    Rand : Index_Type := 0;
	    Buffer : T_Word;
	    
	    I_Gen : Table_Rand.Generator;
	    
	 begin
	    for I in 1..Table'Last/2-1 loop
	       Rand := Table_Rand.Random(I_Gen);
	       Buffer := Table(Rand);
	       Table(Rand) := Table(I);
	       Table(I) := Buffer;
	    end loop;	    
	 end Random_Table;
	 
	 
	 
	 File : Wide_Text_Io.File_Type;
	 File_Length : Natural := 0;
	 Line : Wide_String(1..84) := (others => Wide_Character'Val(32));
	 Last : Natural := 0;
      begin
	 Wide_Text_Io.Put_line("Loading " & To_Wide_String(Filename) & ", please wait...");
	 Wide_Text_Io.Open(File, Wide_Text_Io.In_File, Filename, "WCEM=8");
	 
	 while not Wide_Text_Io.End_Of_File(File) loop
	    Wide_Text_Io.Get_Line(File, Line, Last);
	    if Last /= 0 then
	       File_Length := File_Length + 1;
	       Wide_Text_Io.Put(To_Wide_String(Integer'Image(File_Length)) & Wide_Character'Val(13));   
	    end if;
	 end loop;
	 Wide_Text_Io.Close(File);

	 for I in 32..255 loop
	    --if (not Is_Control(Character'Val(I)))  then -- and (not Is_Letter(Character'Val(I)))) then
	    if Is_Digit(Wide_Character'Val(I)) then
	       --File_Length := File_length + 1;
	       null;
	    else
	       case Wide_Character'Val(I) is
		  when ' ' => --| '[' | '{' | '(' | ',' | ';' | '-' | '?' | '!' | ''' | '@' | '#' | '&' | '\' | '/' | ':' | '+' | '.' | '%' | '*' | '$' | '=' | '_' | ')' | '}' | ']' | '<' | '>' =>
		     File_Length := File_length + 1;
		  when others =>
		     null;
	       end case;	      	       
	    end if;
	    
	 end loop;

	 
	 Wide_Text_Io.Put_line("Loading 1");
	 declare
	    Table : Table_Access := new T_Table(0..File_Length);
	    
	    
	    --  Item : T_Word := (Item => 1,
	    --  		      Word => Ada.Strings.Wide_Unbounded.To_Unbounded_String("que"));
	 begin
	    File_Length := 0;
	    --  Table(0).Word :=
	    --    Ada.Strings.Wide_Unbounded.To_Unbounded_String("");
	    --  Table(0).Item := T_Language'First;
	    
	    --  File_Length := File_length + 1;
	    
	    for I in reverse 32..255 loop
	       --if (not Is_Control(Character'Val(I))) then -- and  (not Is_Letter(Character'Val(I)))) then
	       
	       case Wide_Character'Val(I) is
		  when ' ' => --| '[' | '{' | '(' | ',' | ';' | '-' | '?' | '!' | ''' | '@' | '#' | '&' | '\' | '/' | ':' | '+' | '.' | '%' | '*' | '$' | '=' | '_' | ')' | '}' | ']' | '<' | '>' =>
		     
		     Table(File_Length).Word :=
		     Ada.Strings.Wide_Unbounded.To_Unbounded_Wide_String("" & Wide_Character'Val(I));
		     Table(File_Length).Item := T_Language(File_Length);
		     File_Length := File_length + 1;
		     --Text_Io.Put_line("Loading 1 " & Integer'Image(File_length));
		  when others =>
		     null;
	       end case;
	       
	    end loop;
	    
	    --  for I in 32..127 loop
	    --     if Is_Digit(Character'Val(I)) then
	    --  	  null;
	    --  	  --  Table(File_Length).Word :=
	    --  	  --    Ada.Strings.Wide_Unbounded.To_Unbounded_String("" & Wide_Character'Val(I));
	    --  	  --  Table(File_Length).Item := T_Language(File_Length);
	    --  	  --  File_Length := File_length + 1;
	    --     end if;
	    --  end loop;
	    Wide_Text_Io.Open(File, Wide_Text_Io.In_File, Filename, "WCEM=8");

	    while not Wide_Text_Io.End_Of_File(File) loop
	       Wide_Text_Io.Get_Line(File, Line, Last);
	       
	       if Last /= 0 then
		  Table(File_Length).Word := Ada.Strings.Wide_Unbounded.To_Unbounded_Wide_String(UTF_Encoding.Wide_Strings.Encode(Line(1..Last)));
		  
		  Table(File_Length).Item := T_Language(File_Length+1);
		  
		  File_Length := File_length + 1;
		  
		  Wide_Text_Io.Put(To_Wide_String(Integer'Image(File_Length)) & Wide_Character'Val(13));
	       end if;
	    end loop;
	    
	    Wide_Text_Io.Close(File);
	    Wide_Text_Io.Put("Please wait...");
	    Random_Table(Table);
	    Tree_Loader(Table, 0, File_Length-1);
	    
	 end;
	 
	 Wide_Text_Io.Put_line("Done.");
	 
	 Glossary_Filename := new String ' (Filename);
	 
      end Initialize;
   end Glossary;
   
end El.Lexics ;