--------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Gnostic is Ada Generic Neural Object System Engineering.                                                                                                --
-- Gnostic (C) Copyright 2016 Manuel De Girardi.                                                                                                           --
--------------------------------------------------------------------------------------------------------------------------------------------------------------
  
--------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Date        := 2016-12-05 01:23:25 ; 
-- Description : Ada Generic Neural Object System Engineering.                                                                                              --
-- Version     := 2016.34.25a ; 
-- Authors     : Manuel De Girardi.                                                                                                                         --
--------------------------------------------------------------------------------------------------------------------------------------------------------------
with Gnat.OS_Lib;
use Gnat.Os_Lib;

with Ada.Strings.Fixed;
use Ada.Strings.Fixed;
use Ada.Strings;

with GNAT.Directory_Operations;
use GNAT.Directory_Operations;
with GNAT.Directory_Operations.Iteration;
with Gnat.Regexp;

use Gnat;
with Ada.Calendar.Formatting;
use Ada;

with Ada.Text_Io;
use Ada.Text_Io;
package body Gnos.Tools is


   
   function Integer_Image (Value : in Integer) return String is
   begin
      if Value < 0 then
	 return 
	   '-' & Integer'Image(Value)(Fixed.Index_Non_Blank(Integer'Image(Value)(2..Integer'Image(Value)'Last))..Integer'Image(Value)'Last);
      else
	 return 
	   Integer'Image(Value)(Fixed.Index_Non_Blank(Integer'Image(Value))..Integer'Image(Value)'Last);
      end if;
   end Integer_Image;
   
   
   function YMD_Elapsed_String(Years    : in Natural;
                               Months   : in Natural;
                               Days     : in Natural) return String is
   begin
      return Integer_Image(Years) &
        "y, " &
        Integer_Image(Months) &
        "m, " &
        Integer_Image(Days) &
        "d, ";        
   end YMD_Elapsed_String;
   
   
   
   procedure Difference_In_Years(Top_Date : in Time;
                                 Bot_Date : in Time;
                                 Years    : out Natural;
                                 Months   : out Natural;
                                 Days     : out Natural;
                                 Houres   : out Natural;
                                 Minutes  : out Natural;
                                 Second   : out Natural;
                                 Rest     : out Duration) is      
      
      function Is_Leap_Year (Year : Integer) return Boolean is
      begin
         return (Year rem 4 = 0) and ((Year rem 100 /= 0) or (Year rem 16 = 0));
      end Is_Leap_Year;
      
      pragma Inline (Is_Leap_Year);
      
      Days_Months_Count : constant array (Month_Number) of Day_Number := (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
      
      
      function Last_Days(Years , Months : in Natural) return Natural is         
      begin         
         if Months = 1 then
            return Days_Months_Count(12);
         elsif Months /= 3 then
            return Days_Months_Count(Months-1);
         end if;
         
         if Is_Leap_Year(Years) then
            return Days_Months_Count(2) + 1;
         else
            return Days_Months_Count(2);
         end if;
      end Last_Days;

      
      Top_Seconds : constant Day_Duration := Seconds(Top_Date);
      Bot_Seconds : constant Day_Duration := Seconds(Bot_Date);
      Top_Day     : constant Day_Number := Day(Top_Date);
      Bot_Day     : constant Day_Number := Day(Bot_Date);
      Top_Year    : constant Year_Number := Year(Top_Date);
      Bot_Year    : constant Year_Number := Year(Bot_Date);
      Top_Month   : constant Month_Number := Month(Top_Date);
      Bot_Month   : constant Month_Number := Month(Bot_Date);
      
      Years_Number  : Integer := 0;
      Months_Number : Integer := 0;
      Days_Number   : Integer := 0;            
      
      Total_Duration : Duration := Bot_Seconds - Top_Seconds;
   begin
      
      if Top_Date > Bot_Date then
         raise Constraint_Error;
      end if;
      
      Years    := 0;
      Months   := 0;
      Days     := 0;
      Houres   := 0;
      Minutes  := 0;
      Second   := 0;
      Rest     := 0.0;
      
      Years_Number := (Bot_Year - Top_Year - 1);
      
      Months_Number := Bot_Month;
      
      if (Bot_Month > Top_Month) or ((Bot_Month = Top_Month) and (Bot_Day >= Top_Day)) then         

         Years_Number := Years_Number + 1;
         
      else         
         Months_Number := Bot_Month + 12;
      end if;
      
      Months_Number := (Months_Number - Top_Month - 1);
      
      
      if Bot_Day > Top_Day then

         Months_Number := Months_Number + 1;
         Days_Number := Bot_Day - Top_Day;
	 
      elsif Bot_Day < Top_Day then
	 
         Days_Number := Last_Days (Bot_Year, Bot_Month) - Top_Day;
         if Days_Number < 0 then
            Days_Number := Days_Number + Bot_Day;
         end if;    
	 
      else
	 Months_Number := Months_Number + 1;	 
	 Days_Number := 0;
      end if;
      
      
      Days := Days_Number;
      Months := Months_Number;
      Years := Years_Number;
      
      if Bot_Seconds > Top_seconds then
         Total_Duration := (Bot_Seconds - Top_Seconds);	 
      elsif Bot_Seconds < Top_Seconds then
         Total_Duration := (86400.0 - Top_Seconds) + Bot_Seconds;         	 
	 Days := Days - 1;
      else
	 Total_Duration := 0.0;	 
      end if;
      Formatting.Split(Total_Duration, Houres, Minutes, Second, Rest);         
   end Difference_In_Years;

   
   
      function Normalize_Quoted_Argument(Arg : in String) return String is
      Local : String(1..Arg'Length) := Arg;
      Local_Index : Natural := 0;
   begin
      for I in Arg'First .. Arg'Last loop 
	 if Arg(i) /= '"' then
	    Local(Local_Index + 1) := Arg(I);
	    Local_Index := Local_Index + 1;
	 end if;
      end loop;
      if Local_Index /= 0 then
	 return Local(Local'First..Local_index);
      else
	 return Arg;
      end if;
   end Normalize_Quoted_Argument;

   
   Local_Prefix : constant String := "./";
   
   function Delete_Local_Prefix(Filename : in String) return String is
   begin
      if Filename'Length > 2 then
	 if Filename(Filename'First..Filename'First+1) = Local_Prefix then
	    return Filename(Filename'First+2..Filename'Last);
	 else
	    return Filename;
	 end if;
      else
	 return Filename;
      end if;
   end Delete_Local_Prefix;
   
   function Search_Regexp (Path : in String;
			   Pattern : in String) return String is
      
      Full_Line : Os_Lib.String_Access := new String ' ("");
      
      
      procedure Action (Filename : in String;
			Index    : in Positive;
			Verax    : in out Boolean) is
	 
	 Buffer : Os_Lib.String_Access := new String ' ("");
	 Tmp    : Os_Lib.String_Access := new String ' ("");
	 
	 Is_Out : Boolean := True;
	 Regular_Exp : Regexp.Regexp;
      begin
	 
	 --Text_Io.Put_line("File : "& Filename & "; Pattern : " & Pattern); 
	 Regular_Exp := Regexp.Compile("*" & Pattern, True, False);
	 
	 
	 if Regexp.Match(Filename, Regular_Exp) then
	    if Ada.Strings.Fixed.Index(Filename, " ") /= 0 then
	       
	       for I in Filename'First..Filename'Last-1 loop
		  if Filename(I) = Character'Val(32) then		     
		     Buffer := new String ' (Tmp.all & " ");
		     Free(Tmp);
		     Tmp := new String ' (Buffer.all);
		     Free(Buffer);
		  elsif Filename(I) = '/' then
		     if Ada.Strings.Fixed.Index(Filename, " ", I) > I and Ada.Strings.Fixed.Index(Filename, "/", I+1) > Ada.Strings.Fixed.Index(Filename, " ", I) then
			if Is_Out then
			   Buffer := new String ' (Tmp.all & Filename(I) & """");
			   Free(Tmp);
			   Tmp := new String ' (Buffer.all);
			   Free(Buffer);
			   Is_Out := False;
			else
			   Buffer := new String ' (Tmp.all & Filename(I));
			   Free(Tmp);
			   Tmp := new String ' (Buffer.all);
			   Free(Buffer);
			end if;
			
		     elsif Ada.Strings.Fixed.Index(Filename, " ", I) > I and Ada.Strings.Fixed.Index(Filename, "/", I+1) = 0 then
			
			if Is_Out then
			   Buffer := new String ' (Tmp.all & Filename(I) & """");
			   Free(Tmp);
			   Tmp := new String ' (Buffer.all);
			   Free(Buffer);
			   Is_Out := False;
			else
			   Buffer := new String ' (Tmp.all & Filename(I));
			   Free(Tmp);
			   Tmp := new String ' (Buffer.all);
			   Free(Buffer);
			end if;
		     else
			Buffer := new String ' (Tmp.all & Filename(I));
			Free(Tmp);
			Tmp := new String ' (Buffer.all);
			Free(Buffer);
		     end if;
		     
		  else
		     Buffer := new String ' (Tmp.all & Filename(I));
		     Free(Tmp);
		     Tmp := new String ' (Buffer.all);
		     Free(Buffer);
		  end if;
		  
	       end loop;
	       Buffer := new String ' (Tmp.all & Filename(Filename'Last));
	       if not Is_Out then		  
		  Buffer := new String ' (Tmp.all & """");
		  Free(Tmp);
		  Tmp := new String ' (Buffer.all);
		  Free(Buffer);
	       end if;
	       --Text_Io.Put_Line("Matched :" & Full_Line.all);
	       if Full_Line'Length > 0 then
		  Buffer := new String ' (Full_Line.all & ' ' & "" & Delete_Local_Prefix(Tmp.all) & "");
		  Free(Full_Line);
		  Full_Line := new String ' (Buffer.all);
		  Free(Buffer);
		  
	       else
		  Buffer := new String ' (Full_Line.all & "" & Delete_Local_Prefix(Tmp.all) & "");
		  Free(Full_Line);
		  Full_Line := new String ' (Buffer.all);
		  Free(Buffer);		  
	       end if;
	       --Text_Io.Put_Line("Matche :" & Tmp.all);
	    else
	       --Text_Io.Put_Line("Matched :" & filename);
	       if Full_Line'Length > 0 then
		  Buffer := new String ' (Full_Line.all & ' ' &  Delete_Local_Prefix(filename));
		  Free(Full_Line);
		  Full_Line := new String ' (Buffer.all);
		  Free(Buffer);		  
	       else
		  Buffer := new String ' (Full_Line.all & "" & Delete_Local_Prefix(filename) & "");
		  Free(Full_Line);
		  Full_Line := new String ' (Buffer.all);
		  Free(Buffer);
		  
	       end if;
	       --Text_Io.Put_Line("Matche :" & filename);
	    end if;
	 end if;
	 Verax := False;
      end Action;
      
      
      procedure File_Search is
	 new Iteration.Wildcard_iterator(Action);
      
      
   begin
      
      
      --Text_Io.Put_Line("pattern for : " & Expand_Path(Pattern));
      
      
      if Expand_Path(Pattern) /= "" then
	 
	 File_Search(Path);
      end if;
      if Full_Line'Length > 0 then
	 declare
	    Result : constant String := Full_Line.all;
	 begin
	    Free(Full_Line);
	    return Result;
	 end;
      else
	 return Expand_Path(Pattern);
      end if;
   exception 
      when others =>
	 if Full_Line'Length > 0 then
	    declare
	       Result : constant String := Full_Line.all;
	    begin
	       Free(Full_Line);
	       return Result;
	    end;
	 else
	    return Expand_Path(Pattern);
	 end if;
   end Search_Regexp;
   
   
   
   function Expand_filename (Line : in String) return String is
      
      
      Current_Dir : constant String := ".";
      Absolute_Path : constant Character := '/';

      
      procedure Recursive_Expand (Line : in String; Result : in out Os_Lib.String_Access) is
	 
	 Start, Stop : Natural := 0;
      begin
	 if Line'Length > 1 then	    
	    if Start = 0 then
	       Start := Line'First;
	    end if;
	    
	    Stop := Ada.Strings.Fixed.Index(Line, " ", Start+1);
	    if Stop /= 0 then
	       if Line(Start) /= Absolute_Path then
		  
		  Result := new String ' (Result.all & ' ' & Search_Regexp(Expand_Path(Line(Start..Stop-1)), Line(Start..Stop-1)));
	       else		  
		  
		  Result := new String ' (Result.all & ' ' & Search_Regexp(Expand_Path(Line(Start..Stop-1)), Line(Start..Stop-1)));
	       end if;	 	
	       Recursive_Expand(Line(Stop+1..Line'last), Result);
	       
	       
	    else
	       
	       
	       if Line(Start) /= Absolute_Path then
		  
		  Result := new String ' (Result.all & ' ' & Search_Regexp(Expand_Path(Line(Start..Line'last)), Line(Start..Line'Last)));
	       else		  
		  
		  Result := new String ' (Result.all & ' ' & Search_Regexp(Expand_Path(Line(Start..Line'last)), Line(Start..Line'Last)));
	       end if;	 	
	       
	       
	    end if;
	    
	 else
	    if Start = 0 then
	       Start := Line'First;
	    end if;
	    if Line(Start) /= Absolute_Path then
	       
	       Result := new String ' (Result.all & ' ' & Search_Regexp(Expand_Path(Line(Start..Line'Last)), Line(Start..Line'Last)));
	    else		  
	       
	       Result := new String ' (Result.all & ' ' & Search_Regexp(Expand_Path(Line(Start..Line'last)), Line(Start..Line'Last)));
	    end if;	 		  
	    
	 end if;
      end Recursive_Expand;
      Full_Set : Os_Lib.String_Access := new String ' ("");
   begin
      
      Recursive_Expand(Line(Ada.Strings.Fixed.Index_Non_Blank(Line)..Ada.Strings.Fixed.Index_Non_Blank(Line, Backward)), Full_Set);
      --Text_Io.Put_Line("Matched :" & Full_Set.all);      
      if Full_Set'Length > 0 then
	 declare
	    Result : constant String := Full_Set.all(Ada.Strings.Fixed.Index_Non_Blank(Full_Set.all)..Ada.Strings.Fixed.Index_Non_Blank(Full_Set.all, Backward));
	 begin
	    Free(Full_Set);
	    return Result;
	 end;	 	 
      else
	 return "";
      end if;
      
   end Expand_Filename;
   
   
   
   
end Gnos.Tools;
