#!/usr/bin/python

# urllib2 makes the https request
import urllib2, datetime, sys, os, shutil
import subprocess

# python <= 2.5 requires the inclusion of "simplejson" since the json module
# is not standard on old versions of python
import simplejson as json

from optparse import OptionParser
from tempfile import mkdtemp



# log
class log:
  # Constructor (open the file handle)
  def __init__ (self, file):
    self.file = file
    self.handle = open(file, "a")

  # Deconstructor (close the file handle)
  def __del__ (self): 
    self.pad()
    self.handle.close()

  # Write a message. This is the low level print function
  def write (self, msg): print >> self.handle, msg

  # Just write a message with a stamp
  def info (self, msg): 
    timestamp = "[%d/%d/%d %s:%s:%s] " % self.time()
    self.write(timestamp + msg)

  # Write an error to the log
  def error (self, msg): self.info("ERROR: " + msg)

  # Write a *BIG ERROR* to the log
  def bigerror (self, msg): self.info("***ERROR*** " + msg)

  # Write a warning to the log
  def warning (self, msg): self.info("Warning: " + msg)

  #
  def time (self):
    now = datetime.datetime.now()
    now = (now.month, now.day, now.year, now.hour, now.minute, now.second)
    return now

  # ..
  def pad (self, lines=2, string = "\n"): self.write(string * lines)


#
#
#
def is_r_package (path):

  if not os.path.isdir(path): return False

  R = os.path.join(path, "R")
  man = os.path.join(path, "man")
  DESCRIPTION = os.path.join(path, "DESCRIPTION")
  NAMESPACE = os.path.join(path, "NAMESPACE")

  goodfile = lambda x: os.path.isfile(x) and not os.path.islink(x)

  if not goodfile(DESCRIPTION): return False
  if not goodfile(NAMESPACE): return False
  if not os.path.isdir(R): return False
  if not os.path.isdir(man): return False

  return True


# Craete Option parser
parser = OptionParser()

# Create a menu
parser.add_option("-d", "--dest", dest="dest", metavar = "REPOSITORY",
    help = "Specify the destination directory of repository"
    )
parser.add_option("-l", "--log", dest="log", default="pkg-build.log", 
    metavar = "LOG_FILE",
    help = "Create a log file"
    )
parser.add_option("-S", "--skip-check", dest="skip_check",
    action = "store_true", default=False,
    help = "Do not check package for errors/warnings. This uses `R CMD check`."
    )
parser.add_option(
    "-R", "--repo", dest="repo", 
    default = "https://api.github.com/users/zeligdev/repos",
    help = "Specify the URI of the repository"
    )

# Parse the commandline
(options, args) = parser.parse_args()


# Parse options
clone_dir = mkdtemp(prefix = "clone")
check_dir = mkdtemp(prefix = "check")
build_dir = mkdtemp(prefix = "build")

url = os.path.expanduser(options.repo)

# Ensure that -d or --dest are set
if options.dest is None:
  print "Error: \"--dest\" is not specified!"
  sys.exit()

# Continue parse options
destination = os.path.expanduser(options.dest)


# directory variables
original_wd = os.getcwd()

# Display info blurb
print "Zelig Build v0.9"
print "----------------"
print "  Destination Repository ...", options.dest
print "  Clone Directory ..........", clone_dir
print "  Rcheck Directory .........", check_dir
print "  Build Directory ..........", build_dir
print "  Log File .................", options.log
print "  Skip Check? ..............", not options.skip_check


# Logger
zelig_build = log(options.log)
zelig_build.info("Staring up")

#
if not os.path.exists(destination):
  zelig_build.info(
      "The destination repository directory (" + destination + ") must exist"
      )


# Get a response from the github api
# logger.info("Getting repository listing from zeligdev")
request = urllib2.Request(url)
response_stream = urllib2.urlopen(request)
response = response_stream.read()


# Program start
zelig_build.info("Getting list of repositories from " + url)


# Parses the repsonse into an actual data structure. Awesome!
repos = json.loads(response)


# Change working directory
os.chdir(clone_dir)



# If command
if len(args):
  pkg_list = []

  for r in repos:
    pkg = r['name']
    if pkg in args: pkg_list.append(r)

    pass
  repos = pkg_list



# Clone
for r in repos:
  pkg = r['name']
  url = r['git_url']

  pkg_dest = os.path.join(clone_dir, pkg)

  zelig_build.info("Cloning %s (%s)" % (url, pkg))
  result = subprocess.call("git clone %s %s" % (url, pkg), shell=True)

  if result == 0:
    zelig_build.info("Clone: Success")
  else:
    zelig_build.info("Clone: Fail")


# Change working directory
os.chdir(os.path.join(original_wd, check_dir))

# Check

if not options.skip_check:
  for r in repos:
    pkg = r['name']
    pkg = os.path.join("..", clone_dir, pkg)

    if not is_r_package(pkg):
      zelig_build.info(pkg + " is not an R package. Skipping")
      continue

    zelig_build.info(pkg + " is an R package")
    zelig_build.info("Checking %s")

    # Run from the system's shell
    result = subprocess.call("/usr/bin/R CMD check %s" % pkg, shell=True)

    if result == 0:
      zelig_build.info("Check: Success")
    else:
      zelig_build.info("Check: Fail")


# Change working directory
os.chdir(os.path.join(original_wd, build_dir))

# Build
for r in repos:
  pkg = r['name']
  pkg = os.path.join("..", clone_dir, pkg)

  zelig_build.info("Building %s" % pkg)

  # Run from the system's shell
  result = subprocess.call("/usr/bin/R CMD build %s" % pkg, shell=True)

  # Move into the "REPOS" file
  # print tarball, "->", pkg_repos_dest
  # os.rename(tarball, pkg_repos_dest)

  if result == 0:
    zelig_build.info("Build: Success")
  else:
    zelig_build.info("Build: Fail")


# Ensure we're in the parent directory
os.chdir(original_wd)

# Copy the recently created tarballs over to the destination directory
for src_file in os.listdir(build_dir):
  # Source file
  src_file = os.path.join(build_dir, src_file)

  # Destination file
  dest_file = os.path.join(destination, os.path.basename(src_file))

  zelig_build.info('Copying "%s" -> "%s"' % (src_file, dest_file))

  # Copy (recursively) files into the destination
  shutil.copy(src_file, dest_file)


# Move into the code repository. This is to facilitate the making of the 
# PACKAGES files
os.chdir(destination)

#
zelig_build.info("Creating 'PACKAGES' and 'PACKAGES.gz' Files in " + os.getcwd())

# Create PACKAGES and PACKAGES.gz file
r_script = '"tools:::write_PACKAGES()"'
cmd = "echo %s | /usr/bin/R --vanilla --slave" % r_script
subprocess.call(cmd, shell=True)


# Clean up temp directories
shutil.rmtree(clone_dir)
shutil.rmtree(check_dir)
shutil.rmtree(build_dir)
