# the core computing for times/q
# This used to wrap a piece of FORTRAN/C.
# However, the C code contained a subtle bug, and rather than using
# this clumsy wrapping of sofa, I'm going for ERFA through astopy directly.

import datetime
import io
import os
import subprocess

import numpy
import erfa

from gavo import api
from gavo import base
from gavo import utils
from gavo.svcs import vizierexprs


def expandDates(startDate, endDate, interval):
	delta = datetime.timedelta(seconds=interval)
	cur, res = startDate, []
	while cur<endDate:
		res.append(cur)
		cur = cur+delta
	return res


def computeDates(args):
	"""returns a list of records for the input table from the user-provided
	arguments.
	"""
	interval = args["interval"] or 3600
	if args["ut1"] is None:
		yield datetime.datetime.utcnow()
		return

	try:
		expr = vizierexprs.parseDateExpr(args["ut1"])
		if expr.operator in set([',', '=']):
			for c in expr.children:
				yield c

		elif expr.operator=='..':
			for c in expandDates(expr.children[0],
					expr.children[1], interval):
				yield c

		elif expr.operator=="+/-":
			d0, wiggle = expr.children[0], datetime.timedelta(
				expr.expr.children[1])
			for c in expandDates(d0-wiggle, d0+wiggle):
				yield c

		else:
			raise base.ValidationError("This sort of date expression"
				" does not make sense for this service", colName="ut1")
	except base.ParseException as msg:
		raise base.ValidationError(
			"Invalid date expression (at %s)."%msg.loc,
			colName="ut1")


class Core(api.Core):
	inputTableXML = """
		<inputTable>
			<inputKey name="ut1" type="vexpr-date" multiplicity="single"
				tablehead="UT1"
				description="Date and time (UT1)" ucd="time.epoch;meta.main"/>
			<inputKey name="interval" type="integer" multiplicity="single"
				tablehead="Interval"
				unit="s" ucd="time.interval"
				description="Interval between two sets of computed values"
			>3600</inputKey>
		</inputTable>
	"""

	def initialize(self):
		self.outputTable = api.OutputTableDef.fromTableDef(
			self.rd.getById("times"), None)

	def run(self, service, inputTable, queryMeta):
		try:
			dates = list(computeDates(inputTable.args))
		except ValueError as msg:
			raise api.ValidationError(str(msg), "ut1")
	
		# compute TAI-UT, prepare our date arrays, and compute the TTs for
		# our (UT1) input dates; splitting dates into JD of midnight and
		# hours since then doesn't buy us much here, except we need
		# the day part for our ERA correction.
		deltaTs = (numpy.array([
			erfa.dat(d.year, d.month, d.day,
				(d.hour*3600+d.minute*60+d.second)/86400.) for d in dates]
				)+32.184)/86400
		jds = numpy.array([api.dateTimeToJdn(d.date()) for d in dates])
		hours = numpy.array([(d.hour*3600+d.minute*60+d.second)/86400.
			for d in dates])
		tts = erfa.ut1tt(jds, hours, deltaTs)

		# Now compute the times we're interested in
		gasts = erfa.gst06a(jds, hours, *tts)*180/numpy.pi
		gmsts = erfa.gmst06(jds, hours, *tts)*180/numpy.pi
		eraDay = erfa.era00(jds, numpy.zeros(jds.shape))*180/numpy.pi
		# See Astronomical Almanac 2012, B8
		eras = numpy.fmod(eraDay+hours*24*15.0410672, 360)

		# and finally make our result table
		labels = ["ut1", "gmst", "gast", "era"]
		return api.TableForDef(self.outputTable, rows=[
			dict(zip(labels, r))
			for r in zip(dates, gmsts, gasts, eras)])


def _test():
	from gavo import api
	from gavo.formats import texttable #noflake: for registration
	import sys

	from gavo import base
	base.DEBUG= True

	rd = api.getRD("apfs/times")
	svc = rd.getById("q")
	api.formatData("tsv",
		svc.run("form",
			{"ut1": "2008-10-04 .. 2008-10-05"},
			api.QueryMeta()),
		sys.stdout.buffer)


if __name__=="__main__":
	_test()
