with Interfaces;
with Ada.Text_IO;
with Ada.Unchecked_Deallocation;

package body UnZip.Decompress.Huffman is

  -- Note from Pascal source:
  -- C code by info-zip group, translated to pascal by Christian Ghisler
  -- based on unz51g.zip

  -- Free huffman tables starting with table where t points to

  procedure HufT_free ( tl: in out p_Table_list ) is

    procedure  Dispose is new
      Ada.Unchecked_Deallocation( HufT_table, p_HufT_table );
    procedure  Dispose is new
      Ada.Unchecked_Deallocation( Table_list, p_Table_list );

    current: p_Table_list;
    tcount : Natural; -- just a stat. Idea: replace table_list with an array

  begin
    if full_trace then
      Ada.Text_IO.Put("[HufT_Free... ");
      tcount:= 0;
    end if;
    while tl /= null loop
      Dispose( tl.table ); -- destroy the Huffman table
      current:= tl;
      tl     := tl.next;
      Dispose( current );  -- destroy the current node
      if full_trace then
        tcount:= tcount+1;
      end if;
    end loop;
    if full_trace then
      Ada.Text_IO.Put_Line( Integer'Image(tcount)& " tables]" );
    end if;
  end HufT_free;

  -- Build huffman table from code lengths given by array b

  procedure HufT_build ( b    : Length_array;
                         s    : Integer;
                         d, e : Length_array;
                         tl   :    out p_Table_list;
                         m    : in out Integer;
              huft_incomplete :    out Boolean)
  is
    use Interfaces;

    b_max  : constant:= 16;
    b_maxp1: constant:= b_max + 1;

    -- bit length count table
    count : array( 0 .. b_maxp1 ) of Integer:= (others=> 0);

    f   : Integer;                    -- i repeats in table every f entries
    g   : Integer;                    -- max. code length
    i,                                -- counter, current code
      j : Integer;                    -- counter
    kcc : Integer;                    -- number of bits in current code

    c_idx, v_idx: Natural;            -- array indices

    current_table_ptr : p_HufT_table:= null;
    current_node_ptr  : p_Table_list:= null; -- curr. node for the curr. table
    new_node_ptr      : p_Table_list;        -- new node for the new table

    new_entry: HufT;                  -- table entry for structure assignment

    u : array( 0..b_max ) of p_HufT_table;   -- table stack

    n_max : constant:= 288;
    -- values in order of bit length
    v : array( 0..n_max ) of Integer:= (others=> 0);
    el_v, el_v_m_s: Integer;

    w : Natural:= 0;                        -- bits before this table

    offset, code_stack : array( 0..b_maxp1 ) of Integer;

    table_level : Integer:= -1;
    bits : array( Integer'(-1)..b_maxp1 ) of Integer;
    -- ^bits(table_level) = # bits in table of level table_level

    y  : Integer;                     -- number of dummy codes added
    z  : Natural:= 0;                 -- number of entries in current table
    el : Integer;                     -- length of eob code=code 256

    no_copy_length_array: constant Boolean:= d'Length=0 or e'Length=0;

  begin
    if full_trace then
      Ada.Text_IO.Put("[HufT_Build...");
    end if;
    tl:= null;

    if b'Length > 256 then -- set length of EOB code, if any
      el := b(256);
    else
      el := b_max;
    end if;

    -- Generate counts for each bit length

    for k in b'Range loop
      if b(k) > b_max then
        -- m := 0; -- GNAT 2005 doesn't like it (warning).
        raise huft_error;
      end if;
      count( b(k) ):= count( b(k) ) + 1;
    end loop;

    if count(0) = b'Length then
      m := 0;
      huft_incomplete:= False; -- spotted by Tucker Taft, 19-Aug-2004
      return; -- complete
    end if;

    -- Find minimum and maximum length, bound m by those

    j := 1;
    while j <= b_max and then count(j) = 0 loop
      j:= j + 1;
    end loop;
    kcc := j;
    if m < j then
      m := j;
    end if;
    i := b_max;
    while i > 0 and then count(i) = 0 loop
      i:= i - 1;
    end loop;
    g := i;
    if m > i then
      m := i;
    end if;

    -- Adjust last length count to fill out codes, if needed

    y := Integer( Shift_Left(Unsigned_32'(1), j) ); -- y:= 2 ** j;
    while j < i loop
      y := y - count(j);
      if y < 0 then
        raise huft_error;
      end if;
      y:= y * 2;
      j:= j + 1;
    end loop;

    y:= y - count(i);
    if y < 0 then
      raise huft_error;
    end if;
    count(i):= count(i) + y;

    -- Generate starting offsets into the value table for each length

    offset(1) := 0;
    j:= 0;
    for idx in 2..i loop
      j:= j + count( idx-1 );
      offset( idx ) := j;
    end loop;

    -- Make table of values in order of bit length

    for idx in b'Range loop
      j := b(idx);
      if j /= 0 then
        v( offset(j) ) := idx-b'First;
        offset(j):= offset(j) + 1;
      end if;
    end loop;

    -- Generate huffman codes and for each, make the table entries

    code_stack(0) := 0;
    i := 0;
    v_idx:= v'First;
    bits(-1) := 0;

    -- go through the bit lengths (kcc already is bits in shortest code)
    for k in kcc .. g loop

      for am1 in reverse 0 .. count(k)-1 loop -- a counts codes of length k

        -- here i is the huffman code of length k bits for value v(v_idx)
        while k > w + bits(table_level) loop

          w:= w + bits(table_level);    -- Length of tables to this position
          table_level:= table_level+ 1;
          z:= g - w;                    -- Compute min size table <= m bits
          if z > m then
            z := m;
          end if;
          j := k - w;
          f := Integer(Shift_Left(Unsigned_32'(1), j)); -- f:= 2 ** j;
          if f > am1 + 2 then   -- Try a k-w bit table
            f:= f - (am1 + 2);
            c_idx:= k;
            loop              -- Try smaller tables up to z bits
              j:= j + 1;
              exit when j >= z;
              f := f * 2;
              c_idx:= c_idx + 1;
              exit when f - count(c_idx) <= 0;
              f:= f - count(c_idx);
            end loop;
          end if;

          if w + j > el and then  w < el  then
            j:= el - w;       -- Make EOB code end at table
          end if;
          if w = 0 then
            j := m;  -- Fix: main table always m bits!
          end if;
          z:= Integer(Shift_Left(Unsigned_32'(1), j)); -- z:= 2 ** j;
          bits(table_level) := j;

          -- Allocate and link new table

          begin
            current_table_ptr := new HufT_table ( 0..z );
            new_node_ptr      := new Table_list'( current_table_ptr, null );
          exception
            when Storage_Error =>
              raise huft_out_of_memory;
          end;

          if current_node_ptr = null then -- first table
            tl:= new_node_ptr;
          else
            current_node_ptr.next:= new_node_ptr;   -- not my first...
          end if;

          current_node_ptr:= new_node_ptr; -- always non-Null from there

          u( table_level ):= current_table_ptr;

          -- Connect to last table, if there is one

          if table_level > 0 then
            code_stack(table_level) := i;
            new_entry.bits          := bits(table_level-1);
            new_entry.extra_bits    := 16 + j;
            new_entry.next_table    := current_table_ptr;

            j :=  Integer(
              Shift_Right( Unsigned_32(i) and
                (Shift_Left(Unsigned_32'(1), w) - 1 ),
                w - bits(table_level-1) )
              );

            -- Test against bad input!

            if j > u( table_level - 1 )'Last then
              raise huft_error;
            end if;
            u( table_level - 1 ) (j) := new_entry;
          end if;

        end loop;

        -- Set up table entry in new_entry

        new_entry.bits      := k - w;
        new_entry.next_table:= null;   -- Unused

        if v_idx >= b'Length then
          new_entry.extra_bits := invalid;
        else
          el_v:= v(v_idx);
          el_v_m_s:= el_v - s;
          if el_v_m_s < 0 then -- Simple code, raw value
            if el_v < 256 then
              new_entry.extra_bits:= 16;
            else
              new_entry.extra_bits:= 15;
            end if;
            new_entry.n := el_v;
          else                    -- Non-simple -> lookup in lists
            if no_copy_length_array then
              raise huft_error;
            end if;
            new_entry.extra_bits := e( el_v_m_s );
            new_entry.n          := d( el_v_m_s );
          end if;
          v_idx:= v_idx + 1;
        end if;

        -- fill code-like entries with new_entry
        f := Integer( Shift_Left( Unsigned_32'(1) , k - w ));
        -- i.e. f := 2 ** (k-w);
        j := Integer( Shift_Right( Unsigned_32(i), w ) );
        while j < z loop
          current_table_ptr(j) := new_entry;
          j:= j + f;
        end loop;

        -- backwards increment the k-bit code i
        j := Integer( Shift_Left( Unsigned_32'(1) , k - 1 ));
        -- i.e.: j:= 2 ** (k-1)
        while ( Unsigned_32(i) and Unsigned_32(j) ) /= 0 loop
          i := Integer( Unsigned_32(i) xor Unsigned_32(j) );
          j :=  j / 2;
        end loop;
        i := Integer( Unsigned_32(i) xor Unsigned_32(j) );

        -- backup over finished tables
        while
          Integer(Unsigned_32(i) and (Shift_Left(1, w)-1)) /=
          code_stack(table_level)
        loop
          table_level:= table_level - 1;
          w:= w - bits(table_level); -- Size of previous table!
        end loop;

      end loop;  -- am1
    end loop;  -- k

    if full_trace then
      Ada.Text_IO.Put_Line("finished]");
    end if;

    huft_incomplete:= y /= 0 and g /= 1;

  exception
    when others =>
      HufT_free( tl );
      raise;
  end HufT_build;

end UnZip.Decompress.Huffman;
