#!/usr/bin/env python3

# Follow the instructions in https://wiki.ubuntu.com/SimpleSbuild

import datetime
import tempfile
import time
import json
import os
import glob
import git
import shutil
import pysftp
from git import Repo
import subprocess
from email.utils import formatdate
import re
import sys
import platform
import argparse

parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('--jobs', type=int, default=0, help='Number of parallel jobs')
args = parser.parse_args()
if args.jobs > 0:
  default_sbuild_flags=["--jobs=%d" % args.jobs]
else:
  default_sbuild_flags=[]

archs = ['x86_64']

def readable_sha(sha):
  if len(sha) in [7,40]:
    return sha
  elif len(sha)==20:
    s=""
    for i in range(0,20):
      s+=("%0.2x" % int(sha[i]))
    return s
  else:
    raise(Exception("Can only handle 20 or 40-len sha's, got %s" % sha))

def makeRepo(path, url, bare=False):
  if os.path.exists(path):
    repo = Repo(path)
    origin = repo.remotes.origin
    if not (url == origin.url):
      repo.delete_remote("origin")
      origin = repo.create_remote("origin", url=url)
  else:
    repo = Repo.init(path, bare=bare)
    origin = repo.create_remote("origin", url=url)
    assert(url == origin.url)
    origin.fetch()
    repo.create_head('master', origin.refs.master).set_tracking_branch(origin.refs.master)
  master_head = None
  for fetch_info in origin.fetch():
    if str(fetch_info.ref) == "origin/master":
      print("Updated %s %s to %s" % (url, fetch_info.ref, fetch_info.commit))
      master_head = fetch_info.commit
  if not bare:
    repo.head.reset(commit=master_head, index=True, working_tree=True)
  assert(master_head)
  print("Repo %s has hash %s" % (repo,master_head))
  return (repo, master_head)

def describe(g, commit):
  try:
    res = g.execute(["git", "describe", "--abbrev=7", commit]).replace('-', '~', 1)
    assert(res[0]=='v')
    return res[1:]
  except:
    print("Failed to describe",g.working_dir,commit)
    raise

def updateVMs(projects):
  lvm = projects["LVM-VG"]
  dists = projects["RpmDists"]
  for chroot in dists:
    chroots = subprocess.check_output(['schroot', '-l']).decode("utf-8").split("\n")
    if ("source:%s"%chroot) not in chroots:
      print("You need to create the LVM chroot for %s" % chroot)
      sys.exit(1)
    for cmd in [
      ["yum", "upgrade", "-y"],
    ]:
      sys.stdout.flush()
      if not (0==subprocess.call(["schroot","--directory=/tmp","-c","source:%s" % chroot,"-u","root","--"] + cmd) or
        0==subprocess.call(["schroot","--directory=/tmp","-c","source:%s" % chroot,"-u","root","--"] + cmd)):
        raise Exception("Failed to run command %s in chroot %s" % (cmd,chroot))

def hasBinaryPackage(sftp, projects, dist, arch, project, version, releaseNum):
  path="%s/%s/%s-%s-%s.%s.%s.rpm" % (projects["RPMS"], dist, project.lower(), version, releaseNum, ("."+dist) if dist else "", arch)
  return sftp.exists(path)

def sourcePackagePath(projects, project, version, ext):
  return "%s/SRPM/%s-%s%s" % (projects["RPMS"], project.lower(), version, ext)
def hasSourcePackage(sftp, projects, project, version, releaseNum):
  premote = sourcePackagePath(projects, project, version, "-%s.src.rpm" % releaseNum)
  b = sftp.exists(premote)
  return b
  #if not b:
  #  plocal = sourcePackagePath({"AptBool":projects["SourceDir"]}, project, version)
  #  if os.path.exists(plocal):
  #    for ext in
  #    sftp.put(plocal, premote)
  #    print("Uploaded file %s" % premote)
  #    return True
  #return b

projects = json.load(open("projects.json","r"))

baseRepo = os.path.expanduser(projects["BaseRepo"])
buildScriptsDir = "BuildScripts"
(buildRepo, buildHead) = makeRepo(buildScriptsDir, os.path.join(baseRepo, projects["BuildScripts"]), bare=False)

if not os.path.exists("sources"):
  os.mkdir("sources")
updateVMs(projects)

keyDebFileRegex = re.compile("^(.*/)?([^_]*)_[^_]*_([a-z0-9]*)[.]deb$")
def keyDebFile(f):
  try:
    m = keyDebFileRegex.search(f)
    return m.group(2)+":"+m.group(3)
  except:
    return f

def reconnect():
  build = os.path.expanduser('~/.ssh/id_rsa_sftp_build')
  return pysftp.Connection('build.openmodelica.org', username='build', private_key=build)
with reconnect() as sftp:
  # print(sftp.listdir(projects["RPMS"]))
  for project in sorted(projects["Projects"].keys(), key=lambda k: (projects["Projects"][k]["BuildPriority"],k)):
    p=projects["Projects"][project]
    sourceDir = projects["SourceDir"]
    baseRepo = os.path.expanduser(projects["BaseRepo"])
    buildScriptsDir = "BuildScripts"
    buildScriptsDirAbs = os.path.abspath(buildScriptsDir)
    makeRepo(project, os.path.join(baseRepo, project), bare=False)
    for (stability,flags) in p["ReleasePackages"].items(): # TODO: Add this? ["release", "stable", "nightly"]:
      binaryPackageName = project.lower() + '-' + stability
      patches = flags["PATCHES"]
      patchcmd = flags["PATCHCMD"]
      releaseNum = int(flags["RELEASENUM"])
      priority = int(flags["PRIORITY"])
      configureflags = flags["CONFIGUREFLAGS"]
      documentationversion = flags["DOCUMENTATIONVERSION"]
      version = flags["VERSION"]
      if version.startswith("follow:"):
        version=p["extra-branches"][version[7:]]["revision"]
      repo_dir = project
      g = git.cmd.Git(repo_dir)
      # print("Source %s %s? %s" % (project,version,hasSourcePackage(sftp, projects, project, version)))
      hash = version
      if version is None:
        print("Warning, got version none for %s" % str(p))
      version = version if len(version)!=40 else describe(g, hash)
      rpm_version = version.replace("-","~")
      for dist in projects["RpmDists"]:
        ignores = (p.get("Ignore") or [])
        if [dist,version] in ignores:
          print("Skipping %s %s" % (dist,version))
          continue
        for arch in archs:
          if arch=="armhf":
            if dist in noarm:
              continue
          if [arch,version] in ignores:
            print("Skipping %s %s %s" % (dist,arch,version))
            continue
          if not hasBinaryPackage(sftp, projects, dist, arch, project, rpm_version, releaseNum):
            print("Building %s %s %s %s %s" % (project,dist,arch,rpm_version,releaseNum))
            if os.path.exists("tmpbuilddir"):
              shutil.rmtree("tmpbuilddir")
            os.mkdir("tmpbuilddir")
            SPECSDIR = os.path.expanduser('~/rpmbuild/SPECS')
            RPMSDIR = os.path.expanduser('~/rpmbuild/RPMS/%s' % arch)
            SRPMSDIR = os.path.expanduser('~/rpmbuild/SRPMS')
            SOURCESDIR = os.path.expanduser('~/rpmbuild/SOURCES')
            BUILDDIR = os.path.expanduser('~/rpmbuild/BUILD')
            BUILDROOTDIR = os.path.expanduser('~/rpmbuild/BUILDROOT')
            if not os.path.exists(SOURCESDIR):
              os.makedirs(SOURCESDIR)
            if not os.path.exists(SPECSDIR):
              os.makedirs(SPECSDIR)
            if os.path.exists(BUILDDIR):
              shutil.rmtree(BUILDDIR)
            if os.path.exists(BUILDROOTDIR):
              shutil.rmtree(BUILDROOTDIR)
            specFile = "%s/%s.spec" % (SPECSDIR, binaryPackageName)
            with open(buildScriptsDirAbs+"/rpm/SPECS/openmodelica.spec.tpl") as fin:
              content=fin.read().replace("RPMVERSION",rpm_version).replace("DEBVERSION",version).replace("RELEASENUM",str(releaseNum)).replace("NAME",binaryPackageName).replace("DATE", datetime.datetime.now().strftime("%a %b %d %Y")).replace("PATCHES",patches).replace("PATCHCMDS",patchcmd).replace("BRANCH",stability).replace("PRIORITY",str(priority)).replace("CONFIGUREFLAGS",configureflags).replace("DOCUMENTATIONVERSION",documentationversion)
              with open(specFile,"w") as fout:
                fout.write(content)
            for f in glob.glob(buildScriptsDirAbs+"/rpm/PATCHES/*"):
              shutil.copy(f, SOURCESDIR)
            cmd=["bash", "-c", "cd tmpbuilddir && sudo -- schroot -c chroot:%s -- sh -c \"su hudson -c 'echo spectool && spectool -g -R %s' && echo yum-config-manager && yum-config-manager --enable cr && echo build-dep && %s -y %s && su hudson -c -- 'echo rpmbuild && rpmbuild --define \\\"dist .%s\\\" -ba %s'\"" % (dist, specFile, "dnf builddep" if dist.startswith("fc") else "yum-builddep", specFile, dist, specFile)]

            generatedFiles = ["%s-%s-%s.%s.%s" % (binaryPackageName, rpm_version, releaseNum, dist, ext) for ext in ["%s.rpm" % arch, "src.rpm"]]
            print("Looking for " + str(generatedFiles))

            if not all(os.path.exists((SRPMSDIR if f.endswith(".src.rpm") else RPMSDIR) + "/" + f) for f in generatedFiles):
              print("%s: Running command: %s" % (datetime.datetime.now().isoformat(), cmd))
              sys.stdout.flush()
              start_time = time.time()
              sftp.close()
              assert(0==subprocess.call(cmd))
              print("Elapsed time %.2f minutes" % ((time.time() - start_time)/60))
              # Sometimes, we timeout when building for a long time...
              sftp = reconnect()
            else:
              print("We already have the files for the build...")
            for f in generatedFiles:
              remotedir = "%s/%s" % (projects["RPMS"], dist)
              remotename = "%s/%s" % (remotedir, f)
              if not sftp.exists(remotename):
                sftp.makedirs(remotedir)
                sftp.put((SRPMSDIR if f.endswith(".src.rpm") else RPMSDIR) + "/" + f, remotename)
