------------------------------------------------------------------------------
--                                                                          --
--                            Matreshka Project                             --
--                                                                          --
--         Localization, Internationalization, Globalization for Ada        --
--                                                                          --
--                        Runtime Library Component                         --
--                                                                          --
------------------------------------------------------------------------------
--                                                                          --
-- Copyright © 2010-2011, Vadim Godunko <vgodunko@gmail.com>                --
-- All rights reserved.                                                     --
--                                                                          --
-- Redistribution and use in source and binary forms, with or without       --
-- modification, are permitted provided that the following conditions       --
-- are met:                                                                 --
--                                                                          --
--  * Redistributions of source code must retain the above copyright        --
--    notice, this list of conditions and the following disclaimer.         --
--                                                                          --
--  * Redistributions in binary form must reproduce the above copyright     --
--    notice, this list of conditions and the following disclaimer in the   --
--    documentation and/or other materials provided with the distribution.  --
--                                                                          --
--  * Neither the name of the Vadim Godunko, IE nor the names of its        --
--    contributors may be used to endorse or promote products derived from  --
--    this software without specific prior written permission.              --
--                                                                          --
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS      --
-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT        --
-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR    --
-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT     --
-- HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,   --
-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED --
-- TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR   --
-- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF   --
-- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING     --
-- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS       --
-- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.             --
--                                                                          --
------------------------------------------------------------------------------
--  $Revision: 1446 $ $Date: 2011-02-11 09:10:40 +0300 (Fri, 11 Feb 2011) $
------------------------------------------------------------------------------
with Interfaces;

with Matreshka.Internals.Strings.Constants;

package body Matreshka.Internals.Strings.Handlers.Portable is

   use Interfaces;
   use Matreshka.Internals.Unicode;
   use Matreshka.Internals.Utf16;

   type Unsigned_32_Unrestricted_Array is
     array (Utf16_String_Index) of Unsigned_32;

   --------------------------
   -- Fill_Null_Terminator --
   --------------------------

   overriding procedure Fill_Null_Terminator
    (Self : Portable_String_Handler;
     Item : not null Shared_String_Access)
   is
      pragma Unreferenced (Self);

      use Matreshka.Internals.Strings.Constants;

      pragma Suppress (Access_Check);
      --  Suppress not null check of Self which is generated by compiler; but
      --  not needed actually.
      pragma Suppress (Alignment_Check);
      --  Suppress alignment check of Value below which is generated because of
      --  bug in the GNAT GPL 2010 compiler.

      SV     : Unsigned_32_Unrestricted_Array;
      for SV'Address use Item.Value'Address;
      Index  : constant Utf16_String_Index := Item.Unused / 2;
      Offset : constant Utf16_String_Index := Item.Unused mod 2;

   begin
      SV (Index) := SV (Index) and Terminator_Mask_32 (Offset);
   end Fill_Null_Terminator;

   --------------
   -- Is_Equal --
   --------------

   overriding function Is_Equal
    (Self  : Portable_String_Handler;
     Left  : not null Shared_String_Access;
     Right : not null Shared_String_Access) return Boolean
   is
      pragma Unreferenced (Self);

      pragma Suppress (Access_Check);
      pragma Suppress (Alignment_Check);

   begin
      if Left = Right then
         return True;
      end if;

      if Left.Unused /= Right.Unused then
         return False;
      end if;

      declare
         LV   : Unsigned_32_Unrestricted_Array;
         for LV'Address use Left.Value'Address;
         RV   : Unsigned_32_Unrestricted_Array;
         for RV'Address use Right.Value'Address;
         Last : constant Utf16_String_Index := Left.Unused / 2;

      begin
         for J in 0 .. Last loop
            if LV (J) /= RV (J) then
               return False;
            end if;
         end loop;
      end;

      return True;
   end Is_Equal;

   ----------------
   -- Is_Greater --
   ----------------

   overriding function Is_Greater
    (Self  : Portable_String_Handler;
     Left  : not null Shared_String_Access;
     Right : not null Shared_String_Access) return Boolean
   is
      pragma Unreferenced (Self);

      pragma Suppress (Access_Check);
      pragma Suppress (Alignment_Check);
      pragma Suppress (Index_Check);

   begin
      if Left = Right then
         return False;
      end if;

      declare
         Last : constant Utf16_String_Index
           := Utf16_String_Index'Min (Left.Unused, Right.Unused) / 2;

         LV   : Unsigned_32_Unrestricted_Array;
         for LV'Address use Left.Value'Address;
         RV   : Unsigned_32_Unrestricted_Array;
         for RV'Address use Right.Value'Address;

      begin
         for J in 0 .. Last loop
            if LV (J) /= RV (J) then
               if Left.Value (J * 2) /= Right.Value (J * 2) then
                  return Is_Greater (Left.Value (J * 2), Right.Value (J * 2));

               else
                  return
                    Is_Greater
                     (Left.Value (J * 2 + 1), Right.Value (J * 2 + 1));
               end if;
            end if;
         end loop;
      end;

      return Left.Unused > Right.Unused;
   end Is_Greater;

   -------------------------
   -- Is_Greater_Or_Equal --
   -------------------------

   overriding function Is_Greater_Or_Equal
    (Self  : Portable_String_Handler;
     Left  : not null Shared_String_Access;
     Right : not null Shared_String_Access) return Boolean
   is
      pragma Unreferenced (Self);

      pragma Suppress (Access_Check);
      pragma Suppress (Alignment_Check);
      pragma Suppress (Index_Check);

   begin
      if Left = Right then
         return True;
      end if;

      declare
         Last : constant Utf16_String_Index
           := Utf16_String_Index'Min (Left.Unused, Right.Unused) / 2;

         LV   : Unsigned_32_Unrestricted_Array;
         for LV'Address use Left.Value'Address;
         RV   : Unsigned_32_Unrestricted_Array;
         for RV'Address use Right.Value'Address;

      begin
         for J in 0 .. Last loop
            if LV (J) /= RV (J) then
               if Left.Value (J * 2) /= Right.Value (J * 2) then
                  return Is_Greater (Left.Value (J * 2), Right.Value (J * 2));

               else
                  return
                    Is_Greater
                     (Left.Value (J * 2 + 1), Right.Value (J * 2 + 1));
               end if;
            end if;
         end loop;
      end;

      return Left.Unused >= Right.Unused;
   end Is_Greater_Or_Equal;

   -------------
   -- Is_Less --
   -------------

   overriding function Is_Less
    (Self  : Portable_String_Handler;
     Left  : not null Shared_String_Access;
     Right : not null Shared_String_Access) return Boolean
   is
      pragma Unreferenced (Self);

      pragma Suppress (Access_Check);
      pragma Suppress (Alignment_Check);
      pragma Suppress (Index_Check);

   begin
      if Left = Right then
         return False;
      end if;

      declare
         Last : constant Utf16_String_Index
           := Utf16_String_Index'Min (Left.Unused, Right.Unused) / 2;

         LV   : Unsigned_32_Unrestricted_Array;
         for LV'Address use Left.Value'Address;
         RV   : Unsigned_32_Unrestricted_Array;
         for RV'Address use Right.Value'Address;

      begin
         for J in 0 .. Last loop
            if LV (J) /= RV (J) then
               if Left.Value (J * 2) /= Right.Value (J * 2) then
                  return Is_Less (Left.Value (J * 2), Right.Value (J * 2));

               else
                  return
                    Is_Less (Left.Value (J * 2 + 1), Right.Value (J * 2 + 1));
               end if;
            end if;
         end loop;
      end;

      return Left.Unused < Right.Unused;
   end Is_Less;

   ----------------------
   -- Is_Less_Or_Equal --
   ----------------------

   overriding function Is_Less_Or_Equal
    (Self  : Portable_String_Handler;
     Left  : not null Shared_String_Access;
     Right : not null Shared_String_Access) return Boolean
   is
      pragma Unreferenced (Self);

      pragma Suppress (Access_Check);
      pragma Suppress (Alignment_Check);
      pragma Suppress (Index_Check);

   begin
      if Left = Right then
         return True;
      end if;

      declare
         Last : constant Utf16_String_Index
           := Utf16_String_Index'Min (Left.Unused, Right.Unused) / 2;

         LV   : Unsigned_32_Unrestricted_Array;
         for LV'Address use Left.Value'Address;
         RV   : Unsigned_32_Unrestricted_Array;
         for RV'Address use Right.Value'Address;

      begin
         for J in 0 .. Last loop
            if LV (J) /= RV (J) then
               if Left.Value (J * 2) /= Right.Value (J * 2) then
                  return Is_Less (Left.Value (J * 2), Right.Value (J * 2));

               else
                  return
                    Is_Less (Left.Value (J * 2 + 1), Right.Value (J * 2 + 1));
               end if;
            end if;
         end loop;
      end;

      return Left.Unused <= Right.Unused;
   end Is_Less_Or_Equal;

end Matreshka.Internals.Strings.Handlers.Portable;
