# -*- coding: binary -*-

module Rex
module Zip

#
# An Entry represents a logical file or directory to be stored in an Archive
#
class Entry

  attr_accessor :name, :flags, :info, :xtra, :comment, :attrs, :central_dir_name
  attr_reader :data

  def initialize(fname, data, compmeth, timestamp=nil, attrs=nil, xtra=nil, comment=nil, central_dir_name=nil)
    @name = fname.unpack("C*").pack("C*")
    @central_dir_name = (central_dir_name ? central_dir_name.unpack("C*").pack("C*") : nil)
    @data = data.unpack("C*").pack("C*")
    @xtra = xtra
    @xtra ||= ''
    @comment = comment
    @comment ||= ''
    @attrs = attrs
    @attrs ||= 0

    # XXX: sanitize timestmap (assume now)
    timestamp ||= Time.now
    @flags = CompFlags.new(0, compmeth, timestamp)

    if (@data)
      compress
    else
      @data = ''
      @info = CompInfo.new(0, 0, 0)
    end
    @compdata ||= ''
  end

  def data=(val)
    @data = val.unpack("C*").pack("C*")
    compress
  end

  #
  # Compress the #data and store it for later use.  If this entry's compression method
  # produces a larger blob than the original data, the method is changed to CM_STORE.
  #
  def compress
    @crc = Zlib.crc32(@data, 0)
    case @flags.compmeth

    when CM_STORE
      @compdata = @data

    when CM_DEFLATE
      z = Zlib::Deflate.new(Zlib::BEST_COMPRESSION)
      @compdata = z.deflate(@data, Zlib::FINISH)
      z.close
      @compdata = @compdata[2, @compdata.length-6]

    else
      raise 'Unsupported compression method: %u' % @flags.compmeth
    end

    # if compressing doesn't help, just store it
    if (@compdata.length > @data.length)
      @compdata = @data
      @flags.compmeth = CM_STORE
    end

    @info = CompInfo.new(@crc, @compdata.length, @data.length)
  end


  def relative_path
    get_relative_path(@name)
  end

  def central_dir_path
    return nil if @central_dir_name.to_s.strip.empty?
    get_relative_path(@central_dir_name)
  end


  #
  # Return the compressed data in a format suitable for adding to an Archive
  #
  def pack
    #  - lfh 1
    lfh = LocalFileHdr.new(self)
    ret = lfh.pack

    #  - data 1
    if (@compdata)
      ret << @compdata
    end

    if (@gpbf & GPBF_USE_DATADESC)
      #  - data desc 1
      dd = DataDesc.new(@info)
      ret << dd.pack
    end

    ret
  end

  def inspect
    "#<#{self.class} name:#{name}, data:#{@data.length} bytes>"
  end

  private

  def get_relative_path(path)
    if (path[0,1] == '/')
      return path[1, path.length]
    end
    path
  end

end

end
end
