--                                                                    --
--  package                         Copyright (c)  Dmitry A. Kazakov  --
--     Universally_Unique_Identifiers              Luebeck            --
--  Implementation                                 Winter, 2021       --
--                                                                    --
--                                Last revision :  13:12 05 Jan 2021  --
--                                                                    --
--  This  library  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 library  --
--  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 library; if not, write to  the  Free  Software  Foundation,  --
--  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.    --
--                                                                    --
--  As a special exception, if other files instantiate generics from  --
--  this unit, or you link this unit with other files to produce  an  --
--  executable, this unit does not by  itself  cause  the  resulting  --
--  executable to be covered by the GNU General Public License. This  --
--  exception  does not however invalidate any other reasons why the  --
--  executable file might be covered by the GNU Public License.       --
--____________________________________________________________________--

with Interfaces;  use Interfaces;

with Ada.Calendar.Formatting;
with Ada.Numerics.Discrete_Random;

package body Universally_Unique_Identifiers is
   package Random_Numbers is
      new Ada.Numerics.Discrete_Random (Unsigned_64);
   use Random_Numbers;

   Dice   : Generator;
   Epoch  : constant Time := Formatting.Time_Of (2000, 1, 1);
   --
   -- 100 nanosecods since October 15, 1582 to January 1, 2000
   --
   Shift  : constant := (12_2192_9280_0000 + 9466_8480_0000) * 1_000_0;
   Count  : Unsigned_16 := 0;

   function Create return UUID_Value is
      H : constant Unsigned_64 := Random (Dice);
      L : constant Unsigned_64 := Random (Dice);
   begin
      return
      (   1 => Byte (Shift_Right (H, 8*7) and 16#FF#),
          2 => Byte (Shift_Right (H, 8*6) and 16#FF#),
          3 => Byte (Shift_Right (H, 8*5) and 16#FF#),
          4 => Byte (Shift_Right (H, 8*4) and 16#FF#),

          5 => Byte (Shift_Right (H, 8*3) and 16#FF#),
          6 => Byte (Shift_Right (H, 8*2) and 16#FF#),

          7 => Byte (Shift_Right (H, 8*1) and 16#FF#),
          8 => Byte (             H       and 16#0F#) or 16#40#,

          9 => Byte (Shift_Right (L, 8*7) and 16#3F#) or 16#40#,
         10 => Byte (Shift_Right (L, 8*6) and 16#FF#),

         11 => Byte (Shift_Right (L, 8*5) and 16#FF#),
         12 => Byte (Shift_Right (L, 8*4) and 16#FF#),
         13 => Byte (Shift_Right (L, 8*3) and 16#FF#),
         14 => Byte (Shift_Right (L, 8*2) and 16#FF#),
         15 => Byte (Shift_Right (L, 8*1) and 16#FF#),
         16 => Byte (             L       and 16#FF#)
      );
   end Create;

   function Create
            (  ID    : Node_ID;
               Stamp : Time := Clock
            )  return UUID_Value is
      Ticks  : constant Unsigned_64 :=
                        (  Unsigned_64
                           (  Long_Float (Stamp - Epoch)
                           *  1_000_000_0.0
                           )
                        +  Shift
                        );
   begin
      Count := Count + 1;
      return
      (   1 => Byte (Shift_Right (Ticks, 8*7) and 16#FF#),
          2 => Byte (Shift_Right (Ticks, 8*6) and 16#FF#),
          3 => Byte (Shift_Right (Ticks, 8*5) and 16#FF#),
          4 => Byte (Shift_Right (Ticks, 8*4) and 16#FF#),

          5 => Byte (Shift_Right (Ticks, 8*3) and 16#FF#),
          6 => Byte (Shift_Right (Ticks, 8*2) and 16#FF#),

          7 => Byte (Shift_Right (Ticks, 8*1) and 16#FF#),
          8 => Byte (             Ticks       and 16#0F#) or 16#10#,

          9 => Byte (Shift_Right (Count, 8*1) and 16#3F#) or 16#40#,
         10 => Byte (             Count       and 16#0F#),

         11 => Byte (Character'Pos (ID (1))),
         12 => Byte (Character'Pos (ID (2))),
         13 => Byte (Character'Pos (ID (3))),
         14 => Byte (Character'Pos (ID (4))),
         15 => Byte (Character'Pos (ID (5))),
         16 => Byte (Character'Pos (ID (6)))
      );
   end Create;

   function "<" (Left, Right : UUID_Value) return Boolean is
   begin
      for Index in Left'Range loop
         if Left (Index) /= Right (Index) then
            return Left (Index) < Right (Index);
         end if;
      end loop;
      return False;
   end "<";

   function "<=" (Left, Right : UUID_Value) return Boolean is
   begin
      for Index in Left'Range loop
         if Left (Index) /= Right (Index) then
            return Left (Index) < Right (Index);
         end if;
      end loop;
      return True;
   end "<=";

   function ">=" (Left, Right : UUID_Value) return Boolean is
   begin
      return not (Left < Right);
   end ">=";

   function ">" (Left, Right : UUID_Value) return Boolean is
   begin
      return not (Left <= Right);
   end ">";

begin
   Reset (Dice);
end Universally_Unique_Identifiers;
