with
     collada.Library.geometries,
     collada.Library.controllers,
     collada.Library.animations,
     collada.Library.visual_scenes,

     XML,

     ada.Calendar.formatting,
     ada.Strings.fixed,
     ada.Characters.latin_1,
     ada.Text_IO;


package body collada.Document
is
   use ada.Strings.unbounded;


   ------------
   -- Utilities
   --

   function "+" (From : in String) return unbounded_String
     renames to_unbounded_String;


   function to_Time (From : in String) return ada.Calendar.Time
   is
      Pad   :          String  := From;
      Index : constant Natural := ada.Strings.fixed.Index (Pad, "T");
   begin
      if Index /= 0 then
         Pad (Index) := ' ';
      end if;

      return ada.Calendar.formatting.Value (Pad);

   exception
      when constraint_Error =>
         return ada.Calendar.Clock;   -- TODO: Temporary debug measure to handle unknown date formats.
   end to_Time;



   function to_int_Array (From : in String) return int_Array
   is
      use ada.Strings.fixed;

      the_Array : int_Array (1 .. 500_000);
      Count     : math.Index := 0;

      Start     : Natural := 1;
      Cursor    : Natural := Index (From, " ");

   begin
      if Cursor = 0
      then
         return [1 => Integer'Value (From)];
      end if;

      loop
         if         From (Start .. Cursor-1) /= ""
           and then From (Start .. Cursor-1) /= "" & ada.Characters.latin_1.LF
         then
            Count             := Count + 1;
            the_Array (Count) := Integer'Value (From (Start .. Cursor-1));
         end if;

         Start  := Cursor + 1;
         Cursor := Index (From, " ", Start);

         exit when Cursor = 0;
      end loop;

      if Start <= From'Last
      then
         Count             := Count + 1;
         the_Array (Count) := Integer'Value (From (Start .. From'Last));
      end if;

      return the_Array (1 .. Count);
   end to_int_Array;



   function to_float_Array (From : in String) return float_Array
   is
   begin
      if From = ""
      then
         return float_Array' (1 .. 0 => <>);
      end if;

      declare
         use ada.Strings.fixed;

         the_Array : float_Array (1 .. 500_000);
         Count     : math.Index := 0;

         Start     : Integer    := 1;
         Cursor    : Integer    := Index (From, " ");

      begin
         if Cursor = 0
         then
            return [1 => math.Real'Value (From)];
         end if;

         loop
            if         From (Start .. Cursor-1) /= ""
              and then From (Start .. Cursor-1) /= "" & ada.Characters.latin_1.LF
            then
               Count             := Count + 1;
               the_Array (Count) := math.Real'Value (From (Start .. Cursor-1));
            end if;

            Start  := Cursor + 1;
            Cursor := Index (From, " ", Start);

            exit when Cursor = 0;
         end loop;

         if From (Start .. From'Last) /= ""
         then
            Count             := Count + 1;
            the_Array (Count) := math.Real'Value (From (Start .. From'Last));
         end if;

         return the_Array (1 .. Count);
      end;
   end to_float_Array;



   function to_Text_array (From : in String) return Text_array
   is
   begin
      if From = ""
      then
         return Text_array' (1 .. 0 => <>);
      end if;

      declare
         use ada.Strings.fixed;

         the_Array : Text_array (1 .. 40_000);
         Count     : math.Index := 0;

         Start     : Integer    := 1;
         Cursor    : Integer    := Index (From, " ");

      begin
         if Cursor = 0
         then
            return [1 => +From];
         end if;

         loop
            if         From (Start .. Cursor-1) /= ""
              and then From (Start .. Cursor-1) /= "" & ada.Characters.latin_1.LF
            then
               Count             := Count + 1;
               the_Array (Count) := +From (Start .. Cursor-1);
            end if;

            Start  := Cursor + 1;
            Cursor := Index (From, " ", Start);

            exit when Cursor = 0;
         end loop;

         if From (Start .. From'Last) /= ""
         then
            Count             := Count + 1;
            the_Array (Count) := +From (Start .. From'Last);
         end if;

         return the_Array (1 .. Count);
      end;
   end to_Text_array;



   function to_Matrix (From : in String) return Matrix_4x4
   is
      the_Floats : constant math.Vector_16 := math.Vector_16 (to_float_Array (From));
   begin
      return math.to_Matrix_4x4 (the_Floats);
   end to_Matrix;



   function to_Source (From : in xml.Element) return collada.Library.Source
   is
      the_xml_Id          : constant access xml.Attribute_t := From.Attribute ("id");
      the_xml_float_Array : constant access xml.Element     := From.Child ("float_array");
      the_xml_text_Array  : constant access xml.Element     := From.Child ("Name_array");

      the_array_Length    : Natural;
      pragma Unreferenced (the_array_Length);
      the_Source          : Library.source;

   begin
      the_Source.Id := +the_xml_Id.Value;

      if the_xml_float_Array /= null
      then
         the_Source.array_Id := +the_xml_float_Array.Attribute ("id").Value;

         the_array_Length    := Natural'Value (the_xml_float_Array.Attribute ("count").Value);
         the_Source.Floats   := new float_Array' (to_float_Array (the_xml_float_Array.Data));

      elsif the_xml_text_Array /= null
      then
         the_Source.array_Id := +the_xml_text_Array.Attribute ("id").Value;

         the_array_Length    := Natural'Value (the_xml_text_Array.Attribute ("count").Value);
         the_Source.Texts    := new Text_array' (to_Text_array (the_xml_text_Array.Data));
      end if;

      return the_Source;
   end to_Source;



   function to_Input (From : in xml.Element) return collada.Library.Input_t
   is
      use collada.Library;

      the_xml_Semantic : constant access xml.Attribute_t := From.Attribute ("semantic");
      the_xml_Source   : constant access xml.Attribute_t := From.Attribute ("source");
      the_xml_Offset   : constant access xml.Attribute_t := From.Attribute ("offset");

      the_Input : Input_t;

   begin
      the_Input.Semantic := Semantic'Value (the_xml_Semantic.Value);
      the_Input.Source   :=                +the_xml_Source  .Value;

      if the_xml_Offset /= null
      then
         the_Input.Offset := Natural'Value (the_xml_Offset.Value);
      end if;

      return the_Input;
   end to_Input;



   function to_Vertices (From : in xml.Element) return collada.Library.geometries.Vertices
   is
      use collada.Library,
          collada.Library.geometries;

      the_xml_Id     : constant access xml.Attribute_t := From.Attribute ("id");
      the_xml_Inputs : constant        xml.Elements    := From.Children  ("input");

      the_Vertices   : geometries.Vertices;

   begin
      the_Vertices.Id     := +the_xml_Id.Value;
      the_Vertices.Inputs := new Inputs (the_xml_Inputs'Range);

      for i in the_xml_Inputs'Range
      loop
         the_Vertices.Inputs (i) := to_Input (the_xml_Inputs (i).all);
      end loop;

      return the_Vertices;
   end to_Vertices;



   function to_Polylist (From : in xml.Element) return collada.Library.geometries.Primitive
   is
      use collada.Library,
          collada.Library.geometries;

      the_xml_Count    : constant access xml.Attribute_t := From.Attribute ("count");
      the_xml_Material : constant access xml.Attribute_t := From.Attribute ("material");

      the_xml_Inputs   : constant        xml.Elements    := From.Children ("input");
      the_xml_vCount   : constant access xml.Element     := From.Child    ("vcount");
      the_xml_P        : constant access xml.Element     := From.Child    ("p");

      the_Polylist     : geometries.Primitive (polyList);

   begin
      the_Polylist.Count := Natural'Value (the_xml_Count.Value);

      if the_xml_Material /= null
      then
         the_Polylist.Material := +the_xml_Material.Value;
      end if;

      the_Polylist.Inputs := new Inputs (the_xml_Inputs'Range);

      for i in the_xml_Inputs'Range
      loop
         the_Polylist.Inputs (i) := to_Input (the_xml_Inputs (i).all);
      end loop;

      the_Polylist.vCount := new int_Array' (to_int_Array (the_xml_vCount.Data));
      the_Polylist.P_List := new int_array_List' (1 => new int_Array' (to_int_Array (the_xml_P.Data)));

      return the_Polylist;
   end to_Polylist;



   function to_Polygon (From : in xml.Element) return collada.Library.geometries.Primitive
   is
      use collada.Library,
          collada.Library.geometries;

      the_xml_Count    : constant access xml.Attribute_t := From.Attribute ("count");
      the_xml_Material : constant access xml.Attribute_t := From.Attribute ("material");

      the_xml_Inputs   : constant        xml.Elements    := From.Children ("input");
      the_xml_Ps       : constant        xml.Elements    := From.Children ("p");

      the_Polygons     : geometries.Primitive (Polygons);

   begin
      the_Polygons.Count := Natural'Value (the_xml_Count.Value);

      if the_xml_Material /= null
      then
         the_Polygons.Material := +the_xml_Material.Value;
      end if;

      -- Do inputs.
      --
      the_Polygons.Inputs := new Inputs (the_xml_Inputs'Range);

      for i in the_xml_Inputs'Range
      loop
         the_Polygons.Inputs (i) := to_Input (the_xml_Inputs (i).all);
      end loop;

      -- Do P list.
      --
      the_Polygons.P_List := new int_array_List (1 .. the_xml_Ps'Length);

      for i in the_Polygons.P_List'Range
      loop
         the_Polygons.P_List (i) := new int_Array' (to_int_Array (the_xml_Ps (i).Data));
      end loop;

      return the_Polygons;
   end to_Polygon;



   function to_Triangles (From : in xml.Element) return collada.Library.geometries.Primitive
   is
      use collada.Library,
          collada.Library.geometries;

      the_xml_Count    : constant access xml.Attribute_t := From.Attribute ("count");
      the_xml_Material : constant access xml.Attribute_t := From.Attribute ("material");

      the_xml_Inputs   : constant        xml.Elements    := From.Children ("input");
      the_xml_Ps       : constant        xml.Elements    := From.Children ("p");

      the_Triangles    : geometries.Primitive (Triangles);

   begin
      the_Triangles.Count := Natural'Value (the_xml_Count.Value);

      if the_xml_Material /= null
      then
         the_Triangles.Material := +the_xml_Material.Value;
      end if;

      -- Do inputs.
      --
      the_Triangles.Inputs := new Inputs (the_xml_Inputs'Range);

      for i in the_xml_Inputs'Range
      loop
         the_Triangles.Inputs (i) := to_Input (the_xml_Inputs (i).all);
      end loop;

      -- Do P list.
      --
      the_Triangles.P_List := new int_array_List (1 .. the_xml_Ps'Length);

      for i in the_Triangles.P_List'Range
      loop
         the_Triangles.P_List (i) := new int_Array' (to_int_Array (the_xml_Ps (i).Data));
      end loop;

      return the_Triangles;
   end to_Triangles;



   function to_Joints (From : in xml.Element) return collada.Library.controllers.Joints
   is
      use collada.Library,
          collada.Library.controllers;

      the_xml_Inputs : constant xml.Elements := From.Children  ("input");
      the_Joints     : controllers.Joints;
   begin
      the_Joints.Inputs := new Inputs (the_xml_Inputs'Range);

      for i in the_xml_Inputs'Range
      loop
         the_Joints.Inputs (i) := to_Input (the_xml_Inputs (i).all);
      end loop;

      return the_Joints;
   end to_Joints;



   function to_vertex_Weights (From : in xml.Element) return collada.Library.controllers.vertex_Weights
   is
      use collada.Library,
          collada.Library.controllers;

      the_xml_Count    : constant access xml.Attribute_t := From.Attribute ("count");

      the_xml_Inputs   : constant        xml.Elements    := From.Children ("input");
      the_xml_vCount   : constant access xml.Element     := From.Child    ("vcount");
      the_xml_V        : constant access xml.Element     := From.Child    ("v");

      the_Weights      : controllers.vertex_Weights;

   begin
      the_Weights.Count  := Natural'Value (the_xml_Count.Value);
      the_Weights.Inputs := new Inputs (the_xml_Inputs'Range);

      for i in the_xml_Inputs'Range
      loop
         the_Weights.Inputs (i) := to_Input (the_xml_Inputs (i).all);
      end loop;

      the_Weights.v_Count := new int_Array' (to_int_Array (the_xml_vCount.Data));
      the_Weights.V       := new int_array' (to_int_Array (the_xml_V.Data));

      return the_Weights;
   end to_vertex_Weights;



   function to_Sampler (From : in xml.Element) return collada.Library.animations.Sampler
   is
      use collada.Library,
          collada.Library.animations;

      the_xml_Id     : constant access xml.Attribute_t := From.Attribute ("id");
      the_xml_Inputs : constant        xml.Elements    := From.Children  ("input");

      the_Sampler    : animations.Sampler;

   begin
      the_Sampler.Id     := +the_xml_Id.Value;
      the_Sampler.Inputs := new Inputs (the_xml_Inputs'Range);

      for i in the_xml_Inputs'Range
      loop
         the_Sampler.Inputs (i) := to_Input (the_xml_Inputs (i).all);
      end loop;

      return the_Sampler;
   end to_Sampler;



   function to_Channel (From : in xml.Element) return collada.Library.animations.Channel
   is
      use collada.Library,
          collada.Library.animations;

      the_xml_Source : constant access xml.Attribute_t := From.Attribute ("source");
      the_xml_Target : constant access xml.Attribute_t := From.Attribute ("target");

      the_Channel    : animations.Channel;
   begin
      the_Channel.Source := +the_xml_Source.Value;
      the_Channel.Target := +the_xml_Target.Value;

      return the_Channel;
   end to_Channel;


   ---------------
   -- Construction
   --

   function to_Document (Filename : in String) return Item
   is
      use XML;

      the_xml_Tree     : constant        xml.Element := xml.to_XML (Filename);
      the_collada_Tree : constant access xml.Element := the_xml_Tree.Child (named => "COLLADA");

      the_Document     : Document.item;

   begin
      parse_the_asset_Element:
      declare
         the_Asset             : constant access xml.Element := the_collada_Tree.Child (named => "asset");

         the_Contributor       : constant access xml.Element := the_Asset.Child (named => "contributor");
         the_creation_Date     : constant access xml.Element := the_Asset.Child (named => "created");
         the_modification_Date : constant access xml.Element := the_Asset.Child (named => "modified");

         the_Unit              : constant access xml.Element := the_Asset.Child (named => "unit");
         the_up_Axis           : constant access xml.Element := the_Asset.Child (named => "up_axis");

      begin
         -- Parse the 'contributor' element.
         --
         if the_Contributor /= null
         then
            declare
               the_Author         : constant access xml.Element := the_Contributor .Child (named => "author");
               the_authoring_Tool : constant access xml.Element := the_Contributor .Child (named => "authoring_tool");
            begin
               if the_Author /= null
               then
                  the_Document.Asset.Contributor.Author := +the_Author.Data;
               end if;

               if the_authoring_Tool /= null
               then
                  the_document.asset.contributor.authoring_Tool := +the_authoring_Tool.Data;
               end if;
            end;
         end if;

         -- Parse the creation and modification dates.
         --
         if the_creation_Date /= null
         then
            the_document.asset.Created := to_Time (the_creation_Date.Data);
         end if;

         if the_modification_Date /= null
         then
            the_document.asset.Modified := to_Time (the_modification_Date.Data);
         end if;

         -- Parse the 'unit' element.
         --
         if the_Unit /= null
         then
            the_document.asset.Unit.Name  :=             +the_Unit.Attribute (named => "name") .Value;
            the_document.asset.Unit.Meter := Float'Value (the_Unit.Attribute (named => "meter").Value);
         end if;

         -- Parse the 'up_axis' element.
         --
         if the_up_Axis /= null
         then
            the_document.asset.up_Axis := collada.asset.up_Direction'Value (the_up_Axis.Data);
         end if;
      end parse_the_asset_Element;


      ---------------------------------
      --- Parse the 'library' elements.
      --

      parse_the_geometries_Library:
      declare
         the_Library : constant access xml.Element := the_collada_Tree.Child (named => "library_geometries");
      begin
         if the_Library /= null
         then
            declare
               use collada.Library.geometries;
               the_Geometries : constant xml.Elements := the_Library.Children (named => "geometry");
            begin
               the_Document.Libraries.Geometries.Contents := new Geometry_array (the_Geometries'Range);

               for Each in the_Geometries'Range
               loop
                  declare
                     the_xml_Geometry : access xml.Element renames the_Geometries (Each);
                     the_Geometry     :        Geometry    renames the_Document.Libraries.Geometries.Contents (Each);

                     the_xml_Id       : constant access xml.Attribute_t'Class := the_xml_Geometry.Attribute ("id");
                     the_xml_Name     : constant access xml.Attribute_t'Class := the_xml_Geometry.Attribute ("name");

                  begin
                     the_Geometry.Id := +the_xml_Id.Value;

                     if the_xml_Name /= null
                     then
                        the_Geometry.Name := +the_xml_Name.Value;
                     end if;

                     parse_Mesh:
                     declare
                        the_xml_Mesh       :          access xml.Element  renames the_xml_Geometry.Child ("mesh");
                        the_xml_Vertices   : constant access xml.Element  :=      the_xml_Mesh    .Child ("vertices");
                        the_xml_Sources    : constant        xml.Elements :=      the_xml_Mesh.Children  ("source");
                     begin
                        the_Geometry.Mesh.Sources := new library.Sources (the_xml_Sources'Range);

                        -- Parse sources.
                        --
                        for i in the_xml_Sources'Range
                        loop
                           the_Geometry.Mesh.Sources (i) := to_Source (the_xml_Sources (i).all);
                        end loop;

                        -- Parse vertices.
                        --
                        the_Geometry.Mesh.Vertices := to_Vertices (the_xml_Vertices.all);

                        -- Parse primitives.
                        --
                        declare
                           the_xml_Polylists : constant xml.Elements := the_xml_Mesh.Children (named => "polylist");
                           the_xml_Polygons  : constant xml.Elements := the_xml_Mesh.Children (named => "polygons");
                           the_xml_Triangles : constant xml.Elements := the_xml_Mesh.Children (named => "triangles");

                           primitive_Count   :          Natural := 0;
                           primitive_Total   : constant Natural :=   the_xml_Polylists'Length
                                                                   + the_xml_Polygons 'Length
                                                                   + the_xml_Triangles'Length;
                        begin
                           the_Geometry.Mesh.Primitives := new Primitives (1 .. primitive_Total);

                           -- polylists
                           --
                           for i in the_xml_Polylists'Range
                           loop
                              primitive_Count                                := primitive_Count + 1;
                              the_Geometry.Mesh.Primitives (primitive_Count) := to_Polylist (the_xml_Polylists (i).all);
                           end loop;

                           -- polygons
                           --
                           for i in the_xml_Polygons'Range
                           loop
                              primitive_Count                                := primitive_Count + 1;
                              the_Geometry.Mesh.Primitives (primitive_Count) := to_Polygon (the_xml_Polygons (i).all);
                           end loop;

                           -- Triangles
                           --
                           for i in the_xml_Triangles'Range
                           loop
                              primitive_Count                                := primitive_Count + 1;
                              the_Geometry.Mesh.Primitives (primitive_Count) := to_Triangles (the_xml_Triangles (i).all);
                           end loop;
                        end;
                     end parse_Mesh;

                  end;
               end loop;
            end;

         end if;
      end parse_the_geometries_Library;


      -- Parse the controllers library.
      --
      declare
         the_Library : constant access xml.Element := the_collada_Tree.Child (named => "library_controllers");
      begin
         if the_Library /= null
         then
            declare
               use collada.Library.controllers;
               the_Controllers : constant xml.Elements := the_Library.Children (named => "controller");
            begin
               the_Document.Libraries.controllers.Contents := new Controller_array (the_Controllers'Range);

               for Each in the_Controllers'Range
               loop
                  declare
                     the_xml_Controller : access xml.Element renames the_Controllers (Each);
                     the_Controller     :        Controller  renames the_Document.Libraries.controllers.Contents (Each);

                     the_xml_Id   : constant access xml.Attribute_t'Class := the_xml_Controller.Attribute ("id");
                     the_xml_Name : constant access xml.Attribute_t'Class := the_xml_Controller.Attribute ("name");

                  begin
                     the_Controller.Id := +the_xml_Id.Value;

                     if the_xml_Name /= null
                     then
                        the_Controller.Name := +the_xml_Name.Value;
                     end if;

                     parse_Skin:
                     declare
                        the_xml_Skin    : access xml.Element renames the_xml_Controller.Child ("skin");

                        the_xml_Sources : constant        xml.Elements := the_xml_Skin.Children ("source");
                        the_xml_Matrix  : constant access xml.Element  := the_xml_Skin.Child    ("bind_shape_matrix");
                        the_xml_Joints  : constant access xml.Element  := the_xml_Skin.Child    ("joints");
                        the_xml_Weights : constant access xml.Element  := the_xml_Skin.Child    ("vertex_weights");
                     begin
                        the_Controller.Skin.main_Source       := +the_xml_Skin.Attribute ("source").Value;
                        the_Controller.Skin.bind_shape_Matrix :=  to_float_Array (the_xml_Matrix.Data);

                        -- Parse sources.
                        --
                        the_Controller.Skin.Sources := new library.Sources (the_xml_Sources'Range);

                        for i in the_xml_Sources'Range
                        loop
                           the_Controller.Skin.Sources (i) := to_Source (the_xml_Sources (i).all);
                        end loop;

                        the_Controller.Skin.Joints         := to_Joints         (the_xml_Joints.all);
                        the_Controller.Skin.vertex_Weights := to_vertex_Weights (the_xml_Weights.all);
                     end parse_Skin;

                  end;
               end loop;
            end;
         end if;
      end;


      -- Parse the visual_Scenes library.
      --
      declare
         the_Library : constant access xml.Element := the_collada_Tree.Child (named => "library_visual_scenes");
      begin
         if the_Library /= null
         then
            declare
               use collada.Library.visual_scenes;
               the_visual_Scenes : constant xml.Elements := the_Library.Children (named => "visual_scene");
            begin
               the_Document.Libraries.visual_Scenes.Contents := new visual_Scene_array (the_visual_Scenes'Range);

               for Each in the_visual_Scenes'Range
               loop
                  declare
                     the_visual_Scene   :        visual_Scene renames the_document.Libraries.visual_Scenes.Contents (Each);
                     the_xml_Scene      : access xml.Element  renames the_visual_Scenes (Each);

                     the_xml_Id         : constant access xml.Attribute_t'Class := the_xml_Scene.Attribute ("id");
                     the_xml_Name       : constant access xml.Attribute_t'Class := the_xml_Scene.Attribute ("name");

                  begin
                     the_visual_Scene.Id := +the_xml_Id.Value;

                     if the_xml_Name /= null
                     then
                        the_visual_Scene.Name := +the_xml_Name.Value;
                     end if;

                     parse_Nodes:
                     declare
                        the_xml_root_Node : constant access xml.Element := the_xml_Scene.Child ("node");


                        function to_Node (the_XML : access xml.Element;
                                          Parent  : in     Library.visual_scenes.Node_view) return Library.visual_scenes.Node_view
                        is
                           the_xml_Sid       : constant access xml.Attribute_t'Class := the_xml.Attribute ("sid");
                           the_xml_Id        : constant access xml.Attribute_t'Class := the_xml.Attribute ("id");
                           the_xml_Name      : constant access xml.Attribute_t'Class := the_xml.Attribute ("name");
                           the_xml_Type      :          access xml.Attribute_t'Class := the_xml.Attribute ("type");

                           the_xml_Translate :          access xml.Element  := the_xml.Child    ("translate");
                           the_xml_Scale     :          access xml.Element  := the_xml.Child    ("scale");
                           the_xml_Rotates   :                 xml.Elements := the_xml.Children ("rotate");
                           the_xml_Children  :                 xml.Elements := the_xml.Children ("node");

                           the_Node : constant Library.visual_scenes.Node_view := new Library.visual_scenes.Node;

                        begin
                           if the_xml_Id /= null
                           then
                              the_Node.Id_is  (+the_xml_Id.Value);
                           end if;

                           if the_xml_Sid /= null
                           then
                              the_Node.Sid_is (+the_xml_Sid.Value);
                           end if;

                           if the_xml_Name /= null
                           then
                              the_Node.Name_is (+the_xml_Name.Value);
                           end if;

                           the_Node.Parent_is (Parent);

                           -- Parse children.
                           --
                           declare
                              the_xml_Children : constant xml.Elements := the_XML.Children;
                              the_Child        : access   xml.Element;
                           begin
                              for i in the_xml_Children'Range
                              loop
                                 the_Child := the_xml_Children (i);

                                 if    the_Child.Name = "translate"
                                 then
                                    the_Node.add (Transform' (Kind   => Translate,
                                                              Sid    => to_Text (the_Child.Attribute ("sid").Value),
                                                              Vector => Vector_3 (to_Float_array (the_Child.Data))));

                                 elsif the_Child.Name = "rotate"
                                 then
                                    declare
                                       use collada.Math;
                                       the_Data : constant Vector_4 := Vector_4 (to_Float_array (the_Child.Data));
                                    begin
                                       the_Node.add (Transform' (Kind  => Rotate,
                                                                 Sid   => to_Text (the_Child.Attribute ("sid").Value),
                                                                 Axis  => Vector_3 (the_Data (1 .. 3)),
                                                                 Angle => to_Radians (math.Degrees (the_Data (4)))));
                                    end;

                                 elsif the_Child.Name = "scale"
                                 then
                                    the_Node.add (Transform' (Kind  => Scale,
                                                              Sid   => to_Text (the_Child.Attribute ("sid").Value),
                                                              Scale => Vector_3 (to_Float_array (the_Child.Data))));

                                 elsif the_Child.Name = "matrix"
                                 then
                                    declare
                                       the_Data      : constant        Matrix_4x4            := to_Matrix (the_Child.Data);   -- Will be column vectors.
                                       the_child_Sid : constant access xml.Attribute_t'Class := the_Child.Attribute ("sid");
                                       the_sid_Text  : Text;
                                    begin
                                       if the_child_Sid = null
                                       then
                                          the_sid_Text := to_Text ("");
                                       else
                                          the_sid_Text := to_Text (the_child_Sid.Value);
                                       end if;

                                       the_Node.add (Transform' (Kind   => full_Transform,
                                                                 Sid    => the_sid_Text,
                                                                 Matrix => the_Data));
                                    end;

                                 elsif the_Child.Name = "node"
                                 then
                                    the_Node.add (the_Child => to_Node (the_Child, Parent => the_Node));   -- Recurse.

                                 elsif the_Child.Name = "instance_controller"
                                 then
                                    declare
                                       the_skeleton_Child : constant access xml.Element := the_Child.Child ("skeleton");
                                    begin
                                       the_Document.Libraries.visual_Scenes.skeletal_Root := +the_skeleton_Child.Data (2 .. the_skeleton_Child.Data'Last);
                                    end;

                                 elsif the_Child.Name = "instance_geometry"
                                 then
                                    ada.Text_IO.put_Line ("TODO: Handle instance_geometry.");

                                 else
                                    ada.Text_IO.put_Line ("TODO: Unhandled collada 'visual scene element' found: " & the_Child.Name & ".");
                                 end if;
                              end loop;
                           end;

                           return the_Node;
                        end to_Node;

                     begin
                        the_visual_Scene.root_Node := to_Node (the_xml_root_Node, Parent => null);
                     end parse_Nodes;
                  end;
               end loop;
            end;
         end if;
      end;


      -- Parse the animations library.
      --
      declare
         the_Library : constant access xml.Element := the_collada_Tree.Child (named => "library_animations");
      begin
         if the_Library /= null
         then
            declare
               use collada.Library.animations;
               the_Animations : constant xml.Elements := the_Library.Children (named => "animation");
            begin
               the_document.Libraries.animations.Contents := new Animation_array (the_Animations'Range);

               for Each in the_Animations'Range
               loop
                  declare
                     the_Animation     :        Animation   renames the_document.Libraries.animations.Contents (Each);

                     child_Animation   : constant access xml.Element := the_Animations (Each).Child ("animation");
                     the_xml_Animation : constant access xml.Element := (if child_Animation = null then the_Animations (Each) else child_Animation);
                     --  the_xml_Animation : access xml.Element renames the_Animations (Each); --.Child ("animation");

                     the_xml_Id   : constant access xml.Attribute_t'Class := the_xml_Animation.Attribute ("id");
                     the_xml_Name : constant access xml.Attribute_t'Class := the_xml_Animation.Attribute ("name");

                  begin
                     the_Animation.Id := +the_xml_Id.Value;

                     if the_xml_Name /= null
                     then
                        the_Animation.Name := +the_xml_Name.Value;
                     end if;

                     the_Animation.Sampler := to_Sampler (the_xml_Animation.Child ("sampler").all);
                     the_Animation.Channel := to_Channel (the_xml_Animation.Child ("channel").all);

                     parse_Sources:
                     declare
                        the_xml_Sources : constant xml.Elements := the_xml_Animation.Children ("source");
                     begin
                        the_Animation.Sources := new library.Sources (the_xml_Sources'Range);

                        for i in the_xml_Sources'Range
                        loop
                           the_Animation.Sources (i) := to_Source (the_xml_Sources (i).all);
                        end loop;
                     end parse_Sources;
                  end;
               end loop;
            end;
         end if;
      end;


      --- Parse the 'scene' element.
      --
      -- TODO

      return the_Document;
   end to_Document;



   function Asset (Self : in Item) return collada.Asset.item
   is
   begin
      return Self.Asset;
   end Asset;



   function Libraries (Self : in Item) return collada.Libraries.item
   is
   begin
      return Self.Libraries;
   end Libraries;


end collada.Document;
