#!/usr/bin/env python3
import os
import re
import subprocess

def find_recipes(root):
	for path, dirs, files in os.walk(root):
		for i in dirs:
			if i.startswith("."):
				dirs.remove(i)

		for f in files:
			if os.path.splitext(f)[1] in [".bb", ".inc"]:
				yield os.path.join(path, f)

def recipe_file_version(path):
	filename = os.path.basename(path).split(".")[0]
	if "_" not in filename:
		return None
	return filename.split("_")[1]

def recipe_replace_variable(path, variable, value):
	with open(path, "r") as f:
		content = f.read()
	newcontent = re.sub("^(" + variable + "\\s*=\\s*\")(.*?)(\")", "\\g<1>" + value + "\\g<3>", content, flags = re.MULTILINE)
	with open(path, "w") as f:
		f.write(newcontent)

def recipe_gitinfo(path):
	if not os.path.exists(path):
		return None, None, None

	with open(path, "r") as f:
		content = f.read()
		srcuri, srcrev, srcbranch = None, None, None
		m = re.search("^SRC_URI\\s*=\\s*\"(git://|gitsm://)(.*?)\"", content, re.MULTILINE)
		if m:
			uriparts = m.group(2).split(";")
			srcuri = m.group(1) + uriparts[0]
			if "protocol=https" in m.group(2):
				srcuri = "https://" + uriparts[0]
			for p in uriparts[1:]:
				if "nobranch=" in p:
					srcbranch = None
				elif "branch=" in p:
					srcbranch = p.split("=")[1]

		if srcuri is None:
			# check includes for src uri
			for i in re.finditer(r"^require\s+?([^\s]+?)$", content, re.MULTILINE):
				relpath = i.group(1)
				abspath = os.path.join(os.path.dirname(path), relpath)
				isrcuri, _, isrcbranch = recipe_gitinfo(abspath)
				if isrcuri is not None:
					srcuri = isrcuri
					srcbranch = isrcbranch
					break

		m = re.search("^SRCREV\\s*=\\s*\"(.*?)\"", content, re.MULTILINE)
		if m:
			srcrev = m.group(1)
		return srcuri, srcrev, srcbranch

def recipe_pvinfo(path):
	with open(path, "r") as f:
		content = f.read()
		m = re.search("^PV\\s*=\\s*\"(\d+\.\d+(\.\d+)?)\+(.*?)\"", content, re.MULTILINE)
		if m:
			return m.group(1), tuple(int(i) for i in m.group(1).split(".")), m.group(3)
		return None, None, None

def remote_refs(remote):
	r = subprocess.run(["git", "ls-remote", "--heads", "--tags", remote], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
	if r.returncode != 0:
		raise Exception("Failed to query remote {0}".format(remote))

	for i in r.stdout.decode().splitlines():
		rid, ref, *_ = i.split("\t")
		yield rid, ref

def remote_version_tags(refs):
	for i, ref in refs:
		if ref.startswith("refs/tags/"):
			tag = ref[len("refs/tags/"):]
			m = re.search("^(.*?-)?v?(\d+\.\d+(\.\d+)?)$", tag)
			if m:
				yield (i, ref, tag, m.group(2), tuple(int(i) for i in m.group(2).split(".")))

layerroot = os.path.abspath(os.path.dirname(__file__))
for path in find_recipes(layerroot):
	recipepath = os.path.relpath(path, layerroot)
	fileversion = recipe_file_version(path)
	remote, current, currentref = recipe_gitinfo(path)
	pvversion, _, pvsuffix = recipe_pvinfo(path)

	if remote is not None and current is not None and current != "${AUTOREV}":
		refs = list(remote_refs(remote))

		if pvversion is not None and pvsuffix is not None and pvsuffix == "git${SRCPV}":
			# only attempt to update clean git versions without additional suffixes
			version = next(iter(reversed(sorted(remote_version_tags(refs), key = lambda k : k[4]))), None)
			if version is not None:
				if pvversion is not None and pvversion != version[3]:
					newvalue = version[3] + "+" + pvsuffix
					print(recipepath, "updated version to ->", newvalue, "(from {})".format(pvversion))
					# recipe_replace_variable(path, "PV", newvalue)

		# do not update to the current refer for versioned recipes
		if fileversion is not None and fileversion not in ["git", "litex"]:
			continue

		for i, ref in refs:
			if currentref is not None and ref != "refs/heads/{0}".format(currentref):
				continue
			if currentref is None and ref != "refs/heads/master":
				continue

			if current != i:
				print(recipepath, "updated to ->", i)
				recipe_replace_variable(path, "SRCREV", i)
			break

