require 'digest'

module RIM

# RimInfo is RIM's per module information written to project gits.
# The user is not meant to modify these files directly:
# Files are protected by a checksum an will become invalid if modified.
#
# Example:
#
#  4759302048574720930432049375757593827561
#  remote_url:      ssh://some/url/to/git/repo
#  revision_sha1:   8347982374198379842984562095637243593092
#  target_revision: trunk
#  ignores:         CMakeLists.txt,*.arxml
#  checksum:        9584872389474857324485873627894494726222
#
# rev_name is a symbolic name for revision
#
# ignores is a comma separated list of file patterns to be ignored
#
class RimInfo

  InfoFileName = ".riminfo"
  
  AttrsDef = [
    :remote_url,
    :revision_sha1,
    :target_revision,
    :ignores,
    :checksum,
    :subdir
  ]

  AttrsDef.each do |d|
    attr_accessor d
  end

  @@git_infos = {
    :committer_date => "%ci"
  }

  attr_accessor :infos

  def self.exists?(dir)
    File.exist?(info_file(dir))
  end

  def self.from_dir(dir)
    mi = self.new
    mi.from_dir(dir)
    mi
  end

  def self.from_s(content)
    mi = self.new
    mi.from_s(content)
    mi
  end

  def self.git_infos
    @@git_infos
  end

  def dirty?
    @dirty
  end

  def from_s(content)
    attrs = {}
    # normalize line endings
    # this way riminfo files will be valid even if line endings are changed
    content = content.gsub("\r\n", "\n")
    checksum = content[0..39]
    # exclude \n after checksum
    content = content[41..-1]
    if content
      content.split("\n").each do |l|
        col = l.index(":")
        if col
          name, value = l[0..col-1], l[col+1..-1]
          if name && value
            attrs[name.strip.to_sym] = value.strip
          end
        end
      end
      if attrs[:subdir] == ""
        attrs[:subdir] = nil
      end
    else
      return true
    end
    AttrsDef.each do |a|
      send("#{a}=".to_sym, attrs[a])
    end
    @@git_infos.each_pair do |key, value|
      if attrs.has_key?(key)
        @infos ||= {}
        @infos[key] = attrs[key]
      end
    end
    @dirty = checksum != calc_sha1(content)
  end

  def from_dir(dir)
    file = RimInfo.info_file(dir)
    if File.exist?(file)
      content = nil
      File.open(file, "rb") do |f|
        content = f.read
      end
      from_s(content)
    end
  end

  def to_dir(dir)
    file = RimInfo.info_file(dir)
    content = "\n"
    content << "RIM Info file. You're welcome to read but don't write it.\n"
    content << "Instead, use RIM commands to do the things you want to do.\n"
    content << "BEWARE: Any manual modification will invalidate the file!\n"
    content << "\n"
    content << "#{to_s}\n"
    File.open(file, "wb") do |f|
      f.write(calc_sha1(content)+"\n")
      f.write(content)
    end
  end

  def to_s
    i = @infos || {}
    max_len = (AttrsDef + i.keys).collect{|a| a.size}.max
    s = AttrsDef.collect { |a|
      "#{a.to_s.ljust(max_len)}: #{send(a)}"
    }.join("\n")
    if !i.empty?
      s << "\n\n"
      i.each_pair { |key, value| 
        s << "#{key.to_s.ljust(max_len)}: #{value.to_s}\n"
      }
    end
    s
  end

  private

  def calc_sha1(content)
    sha1 = Digest::SHA1.new
    sha1.update(content)
    sha1.hexdigest
  end

  def self.info_file(dir)
    dir + "/" + InfoFileName
  end

end

end

