##############################################################################
#
# Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is free software: you can Use, Study, Modify and Redistribute
# it under the terms of the GNU General Public License version 3, or (at your
# option) any later version, as published by the Free Software Foundation.
#
# You can also Link and Combine this program with other software covered by
# the terms of any of the Free Software licenses or any of the Open Source
# Initiative approved licenses and Convey the resulting work. Corresponding
# source of such a combination shall include the source code for all other
# software used.
#
# This program is distributed WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See COPYING file for full licensing terms.
# See https://www.nexedi.com/licensing for rationale and options.
#
##############################################################################
from __future__ import print_function
import hashlib
import shutil
import os

DEFAULT_HASH = 'sha512'

class Hash(object):

  def __init__(self, name):
    self._hash = hashlib.new(name)

  def write(self, data):
    self._hash.update(data)

  def read(self):
    return self._hash.hexdigest()

def filehash(filename, type_=DEFAULT_HASH):
  if not os.path.isfile(filename):
    raise ValueError("%r isn't a file" % filename)

  digest = Hash(type_)
  with open(filename, 'r') as file_:
    shutil.copyfileobj(file_, digest)
  return digest.read()

def generateHashFromFiles(file_list):
  hasher = hashlib.md5()
  for path in file_list:
    with open(path, 'rb') as afile:
      buf = afile.read()
    hasher.update(b"%u\n" % len(buf))
    hasher.update(buf)
  return hasher.hexdigest()

# Home made hashdeep <http://md5deep.sourceforge.net/>
def dirhash(dirname, type_=DEFAULT_HASH):
  """Walk into a directory an return a unique hash for
  the directory structure and its files content."""

  if not os.path.isdir(dirname):
    raise ValueError("%r isn't a directory" % dirname)

  digest = Hash(type_)

  # List the directory structure
  path_list = []
  for dirname, dirlist, filelist in os.walk(dirname, followlinks=False):
    for filename in filelist:
      path_list.append(os.path.join(dirname, filename))
  path_list.sort()

  for path in path_list:
    # Change the hash even if the file or the directory is empty
    digest.write(path)

    # Update the hash with file content
    if os.path.isfile(path):
      with open(path, 'r') as file_:
        shutil.copyfileobj(file_, digest)

  return digest.read()

def pathhash(path, type_=DEFAULT_HASH):
  if os.path.isdir(path):
    return dirhash(path, type_)
  elif os.path.isfile(path):
    return filehash(path, type_)
  raise ValueError("%r isn't a directory nor a file" % path)

# you can use python -m slapos.recipe.librecipe.filehash [hash] path
if __name__ == '__main__':
  import sys
  if len(sys.argv) == 1:
    raise ValueError("Not enough command line arguments")
  if len(sys.argv) == 2:
    print(sys.argv[1], '-', pathhash(sys.argv[1]))
  else:
    print(sys.argv[2], '-', pathhash(sys.argv[2], sys.argv[1]))
