#!/usr/bin/env python
# Joins decomposed eb files generated using "Parallel In Time" into a single file
# Example: bit.ly/3OQg71F

# Standard modules
import argparse
from argparse import RawTextHelpFormatter
import os
import glob

# Third-party modules
import numpy as np
import h5py


# Kaipy modules
import kaipy.kaiH5 as kh5
import kaipy.kdefs as kd

tEps = 1.0e-3 #Small time

#Create new file w/ same root vars/attributes as old
def createfile(fIn,fOut,doLink=False):
	print('Creating new output file:',fOut)
	iH5 = h5py.File(fIn,'r')
	oH5 = h5py.File(fOut,'w')
#Start by scraping all variables from root
	#Copy root attributes
	print("Copying root attributes ...")
	for k in iH5.attrs.keys():
		aStr = str(k)
		oH5.attrs.create(k,iH5.attrs[aStr])
		print("\t%s"%(aStr))
	#Copy root groups
	print("Copying root variables ...")

	for Q in iH5.keys():
		sQ = str(Q)
		#Skip cache, we add it later
		if kd.grpTimeCache in sQ:
			continue
		#Don't include stuff that starts with "Step"
		if "Step" not in sQ:
			if doLink:
				oH5[sQ] = h5py.ExternalLink(fIn, sQ)
			else:
				oH5.create_dataset(sQ,data=iH5[sQ])
			print("\t%s"%(sQ))
	iH5.close()

	return oH5

def create_command_line_parser():
	"""Create the command-line argument parser.
	Create the parser for command-line arguments.
	Returns:
		argparse.ArgumentParser: Command-line argument parser for this script.
	"""
	# Defaults
	dIn = os.getcwd()

	runid = "msphere"
	typeid = "deltab"
	MainS = """Joins blocks created by calcdb.x (or similar CHIMP routines) into single file
	
	runid : Run ID

	"""

	parser = argparse.ArgumentParser(description=MainS, formatter_class=RawTextHelpFormatter)
	parser.add_argument('-runid',type=str,metavar="runid",default=runid,help="Input run ID (default: %(default)s)")
	parser.add_argument('-typeid',type=str,metavar="typeid",default=typeid,help="Input type ID (default: %(default)s)")
	parser.add_argument('--link',action='store_true',help="Create links to existing files rather than copy data (default: %(default)s)")

	return parser


def main():
	parser = create_command_line_parser()
	#Finalize parsing
	args = parser.parse_args()
	runid = args.runid
	typeid = args.typeid
	doLink = args.link

	globStr = '%s.????.%s.h5'%(runid,typeid)
	dbIns = glob.glob(globStr)
	dbIns.sort()

	if doLink:
		fOut = "%s.%s.link.h5"%(runid,typeid)
	else:
		fOut = "%s.%s.h5"%(runid,typeid)

	N = len(dbIns)
	print("Found %d files, writing output to %s"%(N,fOut))
	if (N == 0):
		print("No files found, exiting")
		exit()
	#Create file w/ attributes and root variables as first file
	oH5 = createfile(dbIns[0],fOut, doLink)

	# Store and concat timeAttributeCache to add at the very end
	timeCacheVars = {}
	with h5py.File(dbIns[0], 'r') as tacF:
		for k in tacF[kd.grpTimeCache].keys():
			timeCacheVars[k] = np.array([], dtype=tacF[kd.grpTimeCache][k].dtype)

	s0 = 0 #Current step
	nowTime = 0.0
	oldTime = -np.inf

	#Now loop over files
	for n in range(N):
		fIn = dbIns[n]
		
		Ns,sIDs = kh5.cntSteps(fIn)
		nS = sIDs.min()
		nE = sIDs.max()
		dN = nE-nS+1
		print("Reading steps %d to %d from %s"%(nS,nE,fIn))
		print("\tWriting to %d to %d"%(s0,s0+dN-1))
		iH5 = h5py.File(fIn,'r')

		# Grow timeAttributeCache
		for k in iH5[kd.grpTimeCache].keys():
			data = iH5[kd.grpTimeCache][k][:]
			if k == 'step':
				data += s0  # Cache for merged h5 file needs to remap original steps to their position in merged file
			timeCacheVars[k] = np.append(timeCacheVars[k], data, axis=0)

		#Loop over steps in the input file
		for s in range(nS,nE+1):
			#Input
			igStr = "Step#%d"%(s)
			ogStr = "Step#%d"%(s0)

			#Check if this is too close to last value
			nowTime = kh5.tStep(fIn,s)
			#print(nowTime,oldTime)
			if ( np.abs(nowTime-oldTime)<=tEps):
				print("\tSkipping step %d"%(s))
				continue
			else:
				#Good value, update old time
				oldTime = nowTime

			if doLink:
				oH5[ogStr] = h5py.ExternalLink(fIn, igStr)
			else:
				oH5.create_group(ogStr)
				print("Copying %s to %s"%(igStr,ogStr))

				#Group atts
				for k in iH5[igStr].attrs.keys():

					aStr = str(k)
					oH5[ogStr].attrs.create(k,iH5[igStr].attrs[aStr])
					#print(aStr)
				#Group vars
				for Q in iH5[igStr].keys():
					sQ = str(Q)
					oH5[ogStr].create_dataset(sQ,data=iH5[igStr][sQ])
			#Update s0
			s0 = s0 + 1

		iH5.close()

	# Write timeAttributeCache to output file
	print("Writing " + kd.grpTimeCache)
	tag = oH5.create_group(kd.grpTimeCache)
	for k in timeCacheVars:
		tag.create_dataset(k, data=timeCacheVars[k], dtype=timeCacheVars[k].dtype)
	
	#Done
	oH5.close()


if __name__ == "__main__":
	main()