--                                                                    --
--  package                         Copyright (c)  Dmitry A. Kazakov  --
--     Object.Handle.Bounded_Array                 Luebeck            --
--  Implementation                                 Spring, 2003       --
--                                                                    --
--                                Last revision :  22:45 07 Apr 2016  --
--                                                                    --
--  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 System;  use System;

package body Object.Handle.Generic_Bounded_Array is
--
-- Reset -- Decrement references in an array
--
   procedure Reset (Vector : in out Object_Ptr_Array_Type) is
      pragma Inline (Reset);
   begin
      for Index in Vector'Range loop
         Release (Vector (Index));
         Vector (Index) := null;
      end loop;
   end Reset;
--
-- Inc -- Increment references in an array
--
   procedure Inc (Vector : in out Object_Ptr_Array_Type) is
      pragma Inline (Inc);
      Item : Object_Type_Ptr;
   begin
      for Index in Vector'Range loop
         Item := Vector (Index);
         if Item /= null then
            Increment_Count (Item.all);
         end if;
      end loop;
   end Inc;
--
-- Copy -- References from a source to an empty target
--
   procedure Copy
             (  Target : in out Object_Ptr_Array_Type;
                Index  : Index_Type;
                Source : Object_Ptr_Array_Type
             )  is
      pragma Inline (Copy);
      To : constant Index_Type :=
              Index_Type'Val
              (  Index_Type'Pos (Index)
              +  (Source'Length - 1)
              );
   begin
      Target (Index..To) := Source;
      Inc (Target (Index..To));
   end Copy;
--
-- Replace -- References in the target by ones from the source
--
   procedure Replace
             (  Target : in out Object_Ptr_Array_Type;
                Index  : Index_Type;
                Source : Object_Ptr_Array_Type
             )  is
   begin
      if Target'Address <= Source'Address then
         for From in Source'Range loop
            declare
               Item : Object_Type_Ptr renames
                         Target
                         (  Index_Type'Val
                            (  Index_Type'Pos (Index)
                            +  (  Index_Type'Pos (From)
                               -  Index_Type'Pos (Source'First)
                         )  )  );
            begin
               if Source (From) = null then
                  Release (Item);
                  Item := null;
               elsif Item = null then
                  Item := Source (From);
                  Increment_Count (Item.all);
               elsif not Equal (Item.all, Source (From).all) then
                  Release (Item);
                  Item := Source (From);
                  Increment_Count (Item.all);
               end if;
            end;
         end loop;
      else
         for From in reverse Source'Range loop
            declare
               Item : Object_Type_Ptr renames
                         Target
                         (  Index_Type'Val
                            (  Index_Type'Pos (Index)
                            +  (  Index_Type'Pos (From)
                               -  Index_Type'Pos (Source'First)
                         )  )  );
            begin
               if Source (From) = null then
                  Release (Item);
                  Item := null;
               elsif Item = null then
                  Item := Source (From);
                  Increment_Count (Item.all);
               elsif not Equal (Item.all, Source (From).all) then
                  Release (Item);
                  Item := Source (From);
                  Increment_Count (Item.all);
               end if;
            end;
         end loop;
      end if;
   end Replace;

   procedure Adjust (Container : in out Bounded_Array) is
   begin
      Inc (Container.Vector);
   end Adjust;

   function Append
            (  Container : Bounded_Array;
               Element   : Object_Type_Ptr := null;
               Count     : Natural         := 1
            )  return Bounded_Array is
   begin
      if Count = 0 then
         return Container;
      else
         declare
            Last   : constant Index_Type :=
                        Index_Type'Val
                        (  Index_Type'Pos (Container.Last)
                        +  Count
                        );
            Result : Bounded_Array :=
                     (  (  Ada.Finalization.Controlled
                        with
                           Container.First,
                           Last,
                           (  Container.Vector
                           &  (  Index_Type'Succ (Container.Last)
                              .. Last =>
                                    Element
                     )  )  )  );
         begin
            Inc (Result.Vector);
            return Result;
         end;
      end if;
   end Append;

   function Append
            (  Container : Bounded_Array;
               Element   : Handle_Type;
               Count     : Natural := 1
            )  return Bounded_Array is
   begin
      return Append (Container, Ptr (Element), Count);
   end Append;

   function Delete
            (  Container : Bounded_Array;
               From      : Index_Type;
               Count     : Natural := 1
            )  return Bounded_Array is
   begin
      if From not in Container.First..Container.Last then
         raise Constraint_Error;
      elsif Count = 0 then
         return Container;
      elsif From = Container.First then
         --
         -- Removing a prefix
         --
         if Container.Vector'Length <= Count then
            return  -- Empty result
            (  (  Ada.Finalization.Controlled
               with
                  Index_Type'Succ (Index_Type'First),
                  Index_Type'First,
                  (others => null)
            )  );
         else
            declare
               Last : constant Index_Type :=
                         Index_Type'Val
                         (  Index_Type'Pos (Container.Last)
                         -  Count
                         );
               Result : Bounded_Array :=
                        (  (  Ada.Finalization.Controlled
                           with
                              Container.First,
                              Last,
                              Container.Vector
                              (  Index_Type'Val
                                    (Index_Type'Pos (From) + Count)
                              .. Container.Last
                        )  )  );
            begin
               Inc (Result.Vector);
               return Result;
            end;
         end if;
      else
         --
         -- Removing from the middle or a suffix
         --
         if (  (  Index_Type'Pos (Container.Last)
               -  Index_Type'Pos (From)
               +  1
               )
            <= Count
            )
         then -- Removing all suffix
            declare
               Last   : constant Index_Type := Index_Type'Pred (From);
               Result : Bounded_Array :=
                        (  (  Ada.Finalization.Controlled
                           with
                              Container.First,
                              Last,
                              Container.Vector (Container.First..Last)
                        )  );
            begin
               Inc (Result.Vector);
               return Result;
            end;
         else -- Removing a slice
            declare
               Last : constant Index_Type :=
                  Index_Type'Val
                  (  Index_Type'Pos (Container.Last)
                  -  Count
                  );
               Result : Bounded_Array :=
                        (  (  Ada.Finalization.Controlled
                           with
                              Container.First,
                              Last,
                              (  Container.Vector
                                 (  Container.First
                                 .. Index_Type'Pred (From)
                                 )
                              &  Container.Vector
                                 (  Index_Type'Val
                                    (  Index_Type'Pos (From)
                                    +  Count
                                    )
                                 .. Container.Last
                        )  )  )  );
            begin
               Inc (Result.Vector);
               return Result;
            end;
         end if;
      end if;
   end Delete;

   procedure Finalize (Container : in out Bounded_Array) is
   begin
      Reset (Container.Vector);
   end Finalize;

   procedure Fill
             (  Container : in out Bounded_Array;
                From      : Index_Type;
                To        : Index_Type;
                Element   : Object_Type_Ptr := null
             )  is
      subtype Index_Range is Index_Type range Container.Vector'Range;
   begin
      if From > To then
         return;
      else
         if Element = null then
            Reset (Container.Vector (From..To));
         else
            declare
               Object : Object_Type'Class renames Element.all;
            begin
               for Index in Index_Range (From)..Index_Range (To) loop
                  if (  Container.Vector (Index) = null
                     or else
                        not Equal
                            (  Container.Vector (Index).all,
                               Object
                     )      )
                  then
                     Release (Container.Vector (Index));
                     Container.Vector (Index) := Element;
                     Increment_Count (Object);
                  end if;
               end loop;
            end;
         end if;
      end if;
   end Fill;

   procedure Fill
             (  Container : in out Bounded_Array;
                From      : Index_Type;
                To        : Index_Type;
                Element   : Handle_Type
             )  is
   begin
      Fill (Container, From, To, Ptr (Element));
   end Fill;

   function Get
            (  Container : Bounded_Array;
               Index     : Index_Type
            )  return Object_Type_Ptr is
   begin
      if Index not in Container.Vector'Range then
         return null;
      else
         return Container.Vector (Index);
      end if;
   end Get;

   function Get
            (  Container : Bounded_Array;
               From      : Index_Type;
               To        : Index_Type
            )  return Bounded_Array is
   begin
      if From > To then
         declare
            Result : Bounded_Array (From, To);
         begin
            return Result;
         end;
      else
         if From < Container.First or else To > Container.Last then
            raise Constraint_Error;
         else
            declare
               Result : Bounded_Array (From, To);
            begin
               Result.Vector := Container.Vector (From..To);
               Inc (Result.Vector);
               return Result;
            end;
         end if;
      end if;
   end Get;

   function Prepend
            (  Container : Bounded_Array;
               Element   : Object_Type_Ptr := null;
               Count     : Natural         := 1
            )  return Bounded_Array is
   begin
      if Count = 0 then
         return Container;
      else
         declare
            First  : constant Index_Type :=
                        Index_Type'Val
                        (  Index_Type'Pos (Container.First)
                        -  Count
                        );
            Result : Bounded_Array :=
                     (  (  Ada.Finalization.Controlled
                        with
                           First,
                           Container.Last,
                           (  (  First
                              .. Index_Type'Pred (Container.First) =>
                                    Element
                              )
                           &  Container.Vector
                     )  )  );
         begin
            Inc (Result.Vector);
            return Result;
         end;
      end if;
   end Prepend;

   function Prepend
            (  Container : Bounded_Array;
               Element   : Handle_Type;
               Count     : Natural := 1
            )  return Bounded_Array is
   begin
      return Prepend (Container, Ptr (Element), Count);
   end Prepend;

   procedure Put
             (  Container : in out Bounded_Array;
                Index     : Index_Type;
                Element   : Object_Type_Ptr
             )  is
      Item : Object_Type_Ptr renames Container.Vector (Index);
   begin
      if Element = null then
         Release (Item);
         Item := null;
      else
         declare
            Object : Object_Type'Class renames Element.all;
         begin
            if Item /= null and then Equal (Item.all, Object) then
               return;
            end if;
            Release (Item);
            Item := Element;
            Increment_Count (Object);
         end;
      end if;
   end Put;

   procedure Put
             (  Container : in out Bounded_Array;
                Index     : Index_Type;
                Element   : Handle_Type
             )  is
   begin
      Put (Container, Index, Ptr (Element));
   end Put;

   procedure Put
             (  Container : in out Bounded_Array;
                From      : Index_Type;
                To        : Index_Type;
                Elements  : Bounded_Array
             )  is
   begin
      if From > To then
         return;
      elsif From < Container.First or else To > Container.Last then
         raise Constraint_Error;
      else
         if (  Elements.Vector'Length
            >= Index_Type'Pos (To) - Index_Type'Pos (From) + 1
            )
         then
            Replace
            (  Container.Vector,
               From,
               Elements.Vector
               (  Elements.Vector'First
               .. Index_Type'Val
                  (  Index_Type'Pos (Elements.Vector'First)
                  +  (  Index_Type'Pos (To)
                     -  Index_Type'Pos (From)
            )  )  )  );
         else
            Replace (Container.Vector, From, Elements.Vector);
            Reset
            (  Container.Vector
               (  Index_Type'Val
                  (  Index_Type'Pos (From)
                  +  (Elements.Vector'Length - 1)
                  )
               .. To
            )  );
         end if;
      end if;
   end Put;

   function Ref
            (  Container : Bounded_Array;
               Index     : Index_Type
            )  return Handle_Type is
      Ptr : constant Object_Type_Ptr := Get (Container, Index);
   begin
      if Ptr = null then
         raise Constraint_Error;
      else
         return Ref (Ptr);
      end if;
   end Ref;

   function "&" (Left, Right : Bounded_Array) return Bounded_Array is
   begin
      if Right.Last < Right.First then
         return Left;
      elsif Left.Last < Left.First then
         return Right;
      else
         declare
            Result : Bounded_Array
                     (  Index_Type'First,
                        Index_Type'Val
                        (  Index_Type'Pos (Index_Type'First)
                        +  (Left.Vector'Length)
                        +  (Right.Vector'Length)
                        -  1
                     )  );
         begin
            Copy (Result.Vector, Result.First, Left.Vector);
            Copy
            (  Result.Vector,
               Index_Type'Val
               (  Index_Type'Pos (Index_Type'First)
               +  (Left.Vector'Length)
               -  1
               ),
               Right.Vector
            );
            return Result;
         end;
      end if;
   end "&";

end Object.Handle.Generic_Bounded_Array;
