with Ada.Text_IO;  use Ada.Text_IO;
use Ada;
with GNAT.OS_Lib;

package body BMP is

   procedure Debug (Line : String);

   procedure Print_Palette (File_Out     : Ada.Text_IO.File_Type;
                            Palette_Data : Palette;
                            Pixel_Size   : Integer);

   -----------
   -- Debug --
   -----------

   procedure Debug (Line : String) is
   begin
      if Verbose then
         Put_Line (Line);
      end if;
   end Debug;

   procedure Standard
     (File_In : Stream_IO.File_Type;
      Input    : Stream_IO.Stream_Access;
      File_Out : Ada.Text_IO.File_Type;
      Package_Name : String;
      Header   : BMP.Header;
      Info     : BMP.Info;
      Row_Size : Integer)
   is
      procedure Put_Package_Header;
      procedure Put_Package_Footer;

      ------------------------
      -- Put_Package_Header --
      ------------------------

      procedure Put_Package_Header is
      begin
         Put_Line (File_Out, "--  This file was generated by bmp2ada");

         New_Line (File_Out);

         --  Dependecies
         if DMA2D_Format then
            Put_Line (File_Out, "with Giza.Image;");
            Put_Line (File_Out, "with Giza.Image.DMA2D;");
            Put_Line (File_Out, "use Giza.Image.DMA2D;");
         else
            Put_Line (File_Out, "with Giza.Bitmaps; use Giza.Bitmaps;");
            Put_Line (File_Out, "with Giza.Image.Bitmap;");
         end if;

         New_Line (File_Out);

         --  Package name
         Put_Line (File_Out, "package " & Package_Name & " is");

         --  Disable style checks
         Put_Line (File_Out, "   pragma Style_Checks (Off);");

         New_Line (File_Out);
      end Put_Package_Header;

      ------------------------
      -- Put_Package_Footer --
      ------------------------

      procedure Put_Package_Footer is
      begin
         Put_Line (File_Out, "   pragma Style_Checks (On);");
         Put_Line (File_Out, "end " & Package_Name & ";");
      end Put_Package_Footer;

   begin
      if Info.Pixel_Size /= 24 then
         Put_Line ("Only 24bits/pix supported.");
         GNAT.OS_Lib.OS_Exit (-1);
      end if;

      declare
         subtype Row is BMP.Image_RGB24 (1 .. Integer (Info.Width));
         I : Row;
         Offset : Integer;
         Pix_Cnt : Positive := 1;
      begin
         Put_Package_Header;

         if DMA2D_Format then
            Put_Line (File_Out,
                      "   Data : aliased constant RGB888_Data_T := (");
         else
            Put_Line
              (File_Out, "   Data : aliased constant Bitmap :=");
            Put_Line
              (File_Out,
               "     (W =>" & Info.Width'Img &
                 ", H =>" & Info.Height'Img &
                 ", Length =>" & Integer_32'Image (Info.Width * Info.Height) &
                 ", Data => (");
         end if;

         for Row_Cnt in reverse 0 .. Integer (Info.Height) - 1 loop
            Offset := Integer (Header.Offset) + Row_Size * Row_Cnt;

            --  Move read pointer to where the row data starts.
            Stream_IO.Set_Index (File_In,
                                 Stream_IO.Positive_Count (Offset + 1));
            Row'Read (Input, I);

            for Index in I'Range loop
               Text_IO.Put (File_Out,
                            "        " &
                              Pix_Cnt'Img & " => (R => " & I (Index).R'Img &
                              ", G =>" & I (Index).G'Img &
                              ", B =>" & I (Index).B'Img & ")");
               Pix_Cnt := Pix_Cnt + 1;
               if Index /= I'Last or else Row_Cnt /= 0 then
                  Text_IO.Put_Line (File_Out, ",");
               end if;
            end loop;
         end loop;

         if  DMA2D_Format then
            Text_IO.Put_Line (File_Out, ");");
            --  with Linker_Section => "".rodata"";");

            Text_IO.New_Line (File_Out);

            Put_Line
              (File_Out,
               "   Image : constant Giza.Image.Ref :=" &
                 "     new Giza.Image.DMA2D.Instance'");
            Put_Line
              (File_Out,
               "     (Mode => RGB888" &
                 ", W =>" & Info.Width'Img &
                 ", H =>" & Info.Height'Img &
                 ", Length =>" & Integer_32'Image (Info.Width * Info.Height) &
                 ", RGB888_Data => Data'Access);");
         else
            Text_IO.Put_Line (File_Out, "));");
            --  with Linker_Section => "".rodata"";");

            Text_IO.New_Line (File_Out);

            Text_IO.Put_Line
              (File_Out, "    Image : " &
                 "aliased Giza.Image.Bitmap.Instance (Data'Access);");
         end if;

         Put_Package_Footer;
      end;
   end Standard;

   -------------------
   -- Print_Palette --
   -------------------

   procedure Print_Palette (File_Out     : Ada.Text_IO.File_Type;
                            Palette_Data : Palette;
                            Pixel_Size   : Integer)
   is
      C   : Color_Definition;
      Require_Palette_Size : constant Natural :=
        (if DMA2D_Format then
         --  DMA2D only support 4 and 8 bits palettes, for 1 and 2 bits bmp
         --  files we use 4bit format and pad with zeros
           (case Pixel_Size is
               when 1 | 2 | 4 => 2**4,
               when 8 => 2**8,
               when others => 0)
            else
               2**Pixel_Size);
   begin
      for Index in Palette_Data'Range loop
         C := Palette_Data (Index);
         Debug ("Index:" & Index'Img & " R:" & C.R'Img & " G:" &
                     C.G'Img & " B:" & C.B'Img);
         Text_IO.Put (File_Out, "(R =>" & C.R'Img &
                        ", G =>" & C.G'Img &
                        ", B =>" & C.B'Img &
                        ")");
         if Index = Palette_Data'Last then
            if Palette_Data'Length = Require_Palette_Size then
               Text_IO.Put (File_Out, ")");
            else
               Text_IO.Put (File_Out, ", others => (0, 0, 0))");
            end if;
         else
            Text_IO.Put_Line (File_Out, ",");
         end if;
      end loop;
   end Print_Palette;

   ----------------
   -- Palettized --
   ----------------

   procedure Palettized
     (File_In : Stream_IO.File_Type;
      Input    : Stream_IO.Stream_Access;
      File_Out : Ada.Text_IO.File_Type;
      Package_Name : String;
      Header   : BMP.Header;
      Info     : BMP.Info;
      Row_Size : Integer)
   is
      Raw_palette : P8_Array (0 .. Integer (Info.Palette_Size / 2));
      type P16_Array is array (Integer range <>) of Unsigned_16;
      Raw_palette_16 : P16_Array (0 .. Integer (Info.Palette_Size - 1));
      Palette_Data : Palette (0 .. Unsigned_8 (Info.Palette_Size - 1));
      Offset : Integer;
      Pix_Values : Pix_Index_Array (0 .. Row_Size - 1);
      Raw_Bytes : P8_Array (0 .. Row_Size - 1);
      Byte_Index, Pix_Index : Integer;
      Palette_Index : Unsigned_8;
      --  Size_Str : constant String := (case Info.Pixel_Size is
      --                                    when 1 => "1bit",
      --                                    when 2 => "2bits",
      --                                    when 4 => "4bits",
      --                                    when 8 => "8bits",
      --                                    when others => "invalid_pix_size");

      procedure Put_Package_Header;
      procedure Put_Package_Footer;
      procedure Push_Pix_Index (Index : Unsigned_8; Last : Boolean);
      function Output_Pix_Size return Natural;
      function Length_In_Bytes return Natural;

      ------------------------
      -- Put_Package_Header --
      ------------------------

      procedure Put_Package_Header is
      begin
         --  Put_Line (File_Out, "--  This file was generated by bmp2ada");

         --  Dependecies
         if DMA2D_Format then
            Put_Line (File_Out, "with Giza.Image;");
            Put_Line (File_Out, "with Giza.Image.DMA2D;");
            Put_Line (File_Out, "use Giza.Image.DMA2D;");
         else
            --  Put_Line (File_Out, "with Giza.Bitmaps.Indexed_" & Size_Str & ";");
            --  Put_Line (File_Out, "use Giza.Bitmaps.Indexed_" & Size_Str & ";");
            --  Put_Line (File_Out, "with Giza.Image.Bitmap.Indexed_" &
            --              Size_Str & ";");

            Put_Line (File_Out, "with WNM.Screen;");
         end if;

         New_Line (File_Out);

         --  Package name
         Put_Line (File_Out, "package " & Package_Name & " is");

         --  Disable style checks
         Put_Line (File_Out, "   pragma Style_Checks (Off);");

         New_Line (File_Out);
      end Put_Package_Header;

      ------------------------
      -- Put_Package_Footer --
      ------------------------

      procedure Put_Package_Footer is
      begin
         --  Put_Line (File_Out, "   pragma Style_Checks (On);");
         New_Line (File_Out);
         Put_Line (File_Out, "end " & Package_Name & ";");
      end Put_Package_Footer;

      --------------------
      -- Push_Pix_Index --
      --------------------

      Push_Val : Pix_Index_Rec := (Size => Pix_8, P8 => (others => 0));
      Push_Cnt : Natural := 0;
      Overall_Cnt : Natural := 0;

      procedure Push_Pix_Index (Index : Unsigned_8; Last : Boolean) is
      begin
         case Output_Pix_Size is
            when 1 =>
               Push_Val.P1 (Push_Cnt) := Unsigned_1 (Index);
            when 2 =>
               Push_Val.P2 (Push_Cnt) := Unsigned_2 (Index);
            when 4 =>
               Push_Val.P4 (Push_Cnt) := Unsigned_4 (Index);
            when 8 =>
               Push_Val.P8 (Push_Cnt) := Index;
            when others =>
               raise Program_Error;
         end case;
         Push_Cnt := Push_Cnt + 1;
         Overall_Cnt := Overall_Cnt + 1;

         if Push_Cnt = 8 / Output_Pix_Size or else Last then
            Text_IO.Put (File_Out, Push_Val.P8 (0)'Img);
            Push_Cnt := 0;
            if not Last then
               Text_IO.Put (File_Out, ",");
            end if;
            if Overall_Cnt mod 100 = 0 then
               Text_IO.New_Line (File_Out);
            end if;
         end if;
      end Push_Pix_Index;

      ---------------------
      -- Output_Pix_Size --
      ---------------------

      function Output_Pix_Size return Natural is
        (if DMA2D_Format and then Info.Pixel_Size in 1 .. 4 then
            4
         else
            Natural (Info.Pixel_Size));

      ---------------------
      -- Length_In_Bytes --
      ---------------------

      function Length_In_Bytes return Natural is
         Pix_Size : constant Natural := Output_Pix_Size;
         Pix_Nbr : constant Natural := Natural (Info.Width * Info.Height);
         Full_Data_Size_Bits : constant Natural := Pix_Size * Pix_Nbr;
         Bits_Pad : constant Natural :=
           (8 - (Full_Data_Size_Bits mod 8)) mod 8;
      begin
         return (Full_Data_Size_Bits + Bits_Pad) / 8;
      end Length_In_Bytes;

      Format_Str : constant String :=
        (case Info.Pixel_Size is
            when 1 | 2 | 4 => "L4",
            when 8 => "L8",
            when others => "Invalid_DMA2D_Pixel_Size");

   begin
      if Info.Pixel_Size /= 1
        and then Info.Pixel_Size /= 2
        and then Info.Pixel_Size /= 4
        and then Info.Pixel_Size /= 8
      then
         Put_Line ("Pix size not supported for palettized BMP.");
         GNAT.OS_Lib.OS_Exit (-1);
      end if;

      Stream_IO.Set_Index (File_In,
                           Stream_IO.Positive_Count (55));
      Palette'Read (Input, Palette_Data);
      Put_Line ("Reading palette and converting to 24bits RGB...");

      Stream_IO.Set_Index (File_In,
                           Stream_IO.Positive_Count (55));
      P8_Array'Read (Input, Raw_palette);
      Stream_IO.Set_Index (File_In,
                           Stream_IO.Positive_Count (55));
      P16_Array'Read (Input, Raw_palette_16);

      Put_Package_Header;

      if DMA2D_Format then

         Put_Line (File_Out, "   CLUT : aliased constant " & Format_Str &
                     "_CLUT_T := (");
         Print_Palette (File_Out, Palette_Data, Integer (Info.Pixel_Size));
         Put_Line (File_Out, ";");

         New_Line (File_Out);

         Put_Line (File_Out, "   Data : aliased constant " & Format_Str &
                     "_Data_T := (");
      else
         Text_IO.Put (File_Out, "   Data : WNM.Screen.Bitmap");

         --  Text_IO.Put (File_Out, "Bitmap_Indexed");

         Text_IO.Put_Line
           (File_Out, " := (W =>" &
              Info.Width'Img &
              ", H =>" & Info.Height'Img &
              ", Length_Byte =>" & Length_In_Bytes'Img & ",");
         --  Text_IO.Put_Line (File_Out, "Palette => (");
         --  Print_Palette (File_Out, Palette_Data, Integer (Info.Pixel_Size));

         Text_IO.Put_Line (File_Out, "Data => (");
      end if;

      Put_Line ("Reading bitmap data...");

      for Row_Cnt in reverse 0 .. Integer (Info.Height) - 1 loop
         Offset := Integer (Header.Offset) + Row_Size * Row_Cnt + 1;
         Debug ("Read Row at offset:" & Offset'Img);

         --  Move read pointer to where the row data starts.
         Stream_IO.Set_Index (File_In,
                              Stream_IO.Positive_Count (Offset));
         P8_Array'Read (Input, Raw_Bytes);

         for Index in Raw_Bytes'Range loop
            Pix_Values (Index).P8 (0) := Raw_Bytes (Index);
         end loop;

         for Index in 0 .. Integer (Info.Width - 1) loop
            Byte_Index := Index / (8 / Integer (Info.Pixel_Size));
            Pix_Index := Index mod (8 / Integer (Info.Pixel_Size));

            Debug ("Byte_Index:" & Byte_Index'Img);
            Debug ("Pix_Index:" & Pix_Index'Img);
            case Info.Pixel_Size is
               when 1 =>
                  Palette_Index :=
                    Unsigned_8 (Pix_Values (Byte_Index).P1 (7 - Pix_Index));
               when 2 =>
                  Palette_Index :=
                    Unsigned_8 (Pix_Values (Byte_Index).P2 (3 - Pix_Index));
               when 4 =>
                  Palette_Index :=
                    Unsigned_8 (Pix_Values (Byte_Index).P4 (1 - Pix_Index));
               when 8 =>
                  Palette_Index := Pix_Values (Byte_Index).P8 (Pix_Index);
               when others =>
                  raise Program_Error;
            end case;

            Debug ("Palette_Index:" & Palette_Index'Img);

            Push_Pix_Index
              (Palette_Index,
               Last => Index = Integer (Info.Width - 1)
               and then
               Row_Cnt = 0);
         end loop;
      end loop;

      if DMA2D_Format then
         Text_IO.Put_Line (File_Out, ");");

         Put_Line (File_Out,
                   "   Image : constant Giza.Image.Ref :=" &
                     "     new Giza.Image.DMA2D.Instance'");
         Put_Line
           (File_Out,
            "     (Mode => " & Format_Str &
              ", W =>" & Info.Width'Img &
              ", H =>" & Info.Height'Img &
              ", Length =>" & Length_In_Bytes'Img &
              ", " & Format_Str & "_CLUT => CLUT'Access" &
              ", " & Format_Str & "_Data => Data'Access);");

      else
         Text_IO.Put_Line (File_Out, "));");
         --  with Linker_Section => "".rodata"";");

         --  Text_IO.New_Line (File_Out);
         --
         --  Text_IO.Put_Line (File_Out, "   Image :");
         --  Text_IO.Put_Line (File_Out, "   aliased Giza.Image.Bitmap.Indexed_" &
         --                      Size_Str & ".Instance");
         --  Text_IO.Put_Line (File_Out, "     (Data'Access);");
      end if;

      Put_Package_Footer;
   end Palettized;

end BMP;
