"""
Tests for time series serialisation, discovery, and the like.
"""

#c Copyright 2008-2025, the GAVO project <gavo@ari.uni-heidelberg.de>
#c
#c This program is free software, covered by the GNU GPL.  See the
#c COPYING file in the source distribution.


from gavo.helpers import testhelpers

from gavo import api


class _FullPhoto0TS(testhelpers.TestResource):
	"""A time series using the timeseries#photo0 mixin with all pars filled
	out and a few extra columns.
	"""
	def make(self, deps):
		rd = api.parseFromString(api.RD, """<resource schema="test">
				<table id="instance">
					<mixin
							timescale="UTC"
							refposition="TOPOCENTER"
							time0="2400000.5"

							refframe="ICRS"
							pos_epoch="J2020.56"
							longitude="@ra"
							latitude="@dec"

							independent_axes="[@obs_time @obs_time_reduced]"
							dependent_axes="[@phot @mag @xray]"

							filterIdentifier="@ssa_bandpass"
							zeroPointFlux="2000"
							magnitudeSystem="Vega"
							effectiveWavelength="5e-7"

							time_description="Detection time"
							phot_description="Reduced V-band flux"
							phot_unit="Jy"
							phot_ucd="phot.flux;em.opt.V"
						>//timeseries#phot-0</mixin>

					<dm>
						(phot:PhotCal) {
							filterIdentifier: "Johnson/V"
							magnitudeSystem: Vega
							zeroPointFlux: 2000
							effectiveWavelength: 5e-7
							value: @mag
						}
					</dm>

					<dm>
						(phot:PhotCal) {
							filterIdentifier: "XMM/Hardrock"
							effectiveWavelength: 1e-10
							value: @xray
						}
					</dm>

					<dm>
						(ivoa:Measurement) {
							value: @phot
							statError: @phot_error
						}
					</dm>

					<dm>
						(votable:Coords) {
							time: (votable:TimeCoordinate) {
								frame:
									(votable:TimeFrame) {
										timescale: TCB
										refPosition: BARYCENTER
										time0: 0 }
								location: @obs_time_reduced
							}
							space:
								(votable:SphericalCoordinate) {
									frame: (votable:SpaceFrame) {
										orientation: ICRS
										epoch: J2020.56 }
									longitude: @ra
									latitude: @dec
								}
						}
					</dm>

					<param name="ra" ucd="pos.eq.ra">299.59031591</param>
					<param name="dec" ucd="pos.eq.dec">35.20160625</param>
					<param name="ssa_bandpass" type="text">Johnson V</param>

					<column name="obs_time_reduced" ucd="time.epoch"/>
					<column name="mag" ucd="phot.mag;em.opt.V" unit="mag"/>
					<column name="xray" ucd="phot.flux;em.xray.hard" unit="Jy"/>
					<column name="phot_error" ucd="stat.error;phot.flux;em.opt.V"
						unit="Jy"/>
				</table>
			</resource>""")
		table = api.TableForDef(rd.getById("instance"),
			rows=[dict(obs_time=56321, obs_time_reduced=2456320.996,
				phot=125, phot_error=0.23, mag=6.5, xray=10.3)])
		return testhelpers.getXMLTree(
			api.getFormatted("vodml", table), debug=False)


class FullPhoto0Test(testhelpers.VerboseTest):
	resources = [("tree", _FullPhoto0TS())]

	def testDataproductType(self):
		dpPar = self.tree.uniqueXpath(
			"RESOURCE/TABLE/PARAM[@name='dataproduct_type']")
		self.assertEqual(dpPar.get("value"), "timeseries")
		self.assertEqual(dpPar.get("ucd"), "meta.code.class")
		self.assertEqual(dpPar.get("utype"), "obscore:ObsDataset.dataProductType")

	def testDataproductSubtype(self):
		dpPar = self.tree.uniqueXpath(
			"RESOURCE/TABLE/PARAM[@name='dataproduct_subtype']")
		self.assertEqual(dpPar.get("value"), "lightcurve")
		self.assertEqual(dpPar.get("ucd"), "meta.code.class")
		self.assertEqual(
			dpPar.get("utype"), "obscore:ObsDataset.dataProductSubtype")

	def testCoosys(self):
		coosysID = self.tree.uniqueXpath("//PARAM[@name='ra']").get("ref")
		cs = self.tree.getByID(coosysID)
		self.assertEqual(cs.get("epoch"), "J2020.56")
		self.assertEqual(cs.get("system"), "ICRS")

	def testTimesys1(self):	
		ts = self.tree.getByID(
			self.tree.uniqueXpath("//FIELD[@name='obs_time']").get("ref"))
		self.assertEqual(ts.get("refposition"), "TOPOCENTER")
		self.assertEqual(ts.get("timeorigin"), "2400000.5")
		self.assertEqual(ts.get("timescale"), "UTC")

	def testTimesys2(self):	
		ts = self.tree.getByID(
			self.tree.uniqueXpath("//FIELD[@name='obs_time_reduced']").get("ref"))
		self.assertEqual(ts.get("refposition"), "BARYCENTER")
		self.assertEqual(ts.get("timeorigin"), "0")
		self.assertEqual(ts.get("timescale"), "TCB")

	def testCubeIndepAxes(self):
		self.assertEqual(
			self.tree.xpath("//INSTANCE[@dmtype='ndcube:Cube']/"
				"ATTRIBUTE[@dmrole='independent_axes']/COLLECTION/ATTRIBUTE/@ref"),
			["obs_time", "obs_time_reduced"])

	def testCubeDepAxes(self):
		self.assertEqual(
			self.tree.xpath("//INSTANCE[@dmtype='ndcube:Cube']/"
				"ATTRIBUTE[@dmrole='dependent_axes']/COLLECTION/ATTRIBUTE/@ref"),
			["phot", "mag", "xray"])

	def testDefaultPhotcal(self):
		pg = self.tree.getByID(
			self.tree.getByID("phot").get("ref"))
		self.assertEqual("2000",
			pg.xpath("PARAM[@name='zeroPointFlux']")[0].get("value"))
		self.assertEqual("Vega",
			pg.xpath("PARAM[@name='magnitudeSystem']")[0].get("value"))
		self.assertEqual("5e-7",
			pg.xpath("PARAM[@name='effectiveWavelength']")[0].get("value"))
	
	def testParametersReferencing(self):
		pg = self.tree.getByID(
			self.tree.getByID("phot").get("ref"))
		paramId = pg.xpath("PARAMref[@utype='photDM:PhotometryFilter.identifier']"
			)[0].get("ref")
		self.assertEqual(self.tree.getByID(paramId).get("value"), "Johnson V")

	def testXrayPhotcal(self):
		pg = self.tree.getByID(
			self.tree.getByID("xray").get("ref"))
		self.assertEqual("XMM/Hardrock",
			pg.xpath("PARAM[@name='filterIdentifier']")[0].get("value"))
		self.assertEqual("1e-10",
			pg.xpath("PARAM[@name='effectiveWavelength']")[0].get("value"))
		self.assertEqual([],
			pg.xpath("PARAM[@name='zeroPointFlux']"))
		self.assertEqual([],
			pg.xpath("PARAM[@name='magnitudeSystem']"))

	def testPhotMetadata(self):
		f = self.tree.uniqueXpath("//FIELD[@name='phot']")
		self.assertEqual(f.get("unit"), "Jy")
		self.assertEqual(f.get("ucd"), "phot.flux;em.opt.V")
		self.assertEqual(
			f.xpath("DESCRIPTION")[0].text,
			"Reduced V-band flux")
	
	def testTimeMetadata(self):
		f = self.tree.uniqueXpath("//FIELD[@name='obs_time']")
		self.assertEqual(f.get("unit"), "d")
		self.assertEqual(f.get("ucd"), "time.epoch")
		self.assertEqual(
			f.xpath("DESCRIPTION")[0].text,
			"Detection time")

	def testForwardReferences(self):
		self.assertEqual(
			self.tree.xpath("//*[@utype='adhoc:location']/@ref"),
			["phot", "mag", "xray"])


class _MinimalPhoto0TS(testhelpers.TestResource):
	"""A time series using the timeseries#photo0 mixin with only the
	mandatory pars filled out and obs_time overridden.
	"""
	def make(self, deps):
		rd = api.parseFromString(api.RD, """<resource schema="test">
				<table id="instance">
					<mixin
							timescale="UTC"
							refposition="TOPOCENTER"
							time0="2440587.5"

							filterIdentifier='"Johnson/V"'
							effectiveWavelength="5e-7"

							phot_unit="Jy"
							phot_ucd="phot.flux;em.opt.V"
						>//timeseries#phot-0</mixin>
					<column original="obs_time" unit="s"/>
				</table>
			</resource>""")
		table = api.TableForDef(rd.getById("instance"),
			rows=[dict(obs_time=1.45e9, phot=125,)])
		return testhelpers.getXMLTree(
			api.getFormatted("vodml", table), debug=False)


class MinimalPhot0Test(testhelpers.VerboseTest):
	resources = [("tree", _MinimalPhoto0TS())]

	def testDataproductType(self):
		dpPar = self.tree.uniqueXpath(
			"RESOURCE/TABLE/PARAM[@name='dataproduct_type']")
		self.assertEqual(dpPar.get("value"), "timeseries")
		self.assertEqual(dpPar.get("ucd"), "meta.code.class")
		self.assertEqual(dpPar.get("utype"), "obscore:ObsDataset.dataProductType")

	def testTimeMetaOverridden(self):
		f = self.tree.uniqueXpath("//FIELD[@name='obs_time']")
		self.assertEqual(f.get("unit"), "s")
	
	def testNoParamJunk(self):
		self.assertEqual(
			self.tree.xpath("//PARAM[@name='effectiveWavelength']/@value"),
			['5e-7'])

		self.assertEqual(
			self.tree.xpath("//PARAM[@name='zeroPointFlux']"),
			[])

if __name__=="__main__":
	testhelpers.main(FullPhoto0Test)
