"""
Tests for value mapping
"""

#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.


import datetime
import types

from gavo.helpers import testhelpers

from gavo import base
from gavo import formal
from gavo import rscdef
from gavo.base import valuemappers
from gavo.protocols import products #noflake: for registration
from gavo.utils import pgsphere
from gavo.web import htmltable


class QuotingTest(testhelpers.VerboseTest,
		metaclass=testhelpers.SamplesBasedAutoTest):
	def _runTest(self, sample):
		name, doesNeed = sample
		self.assertEqual(valuemappers.needsQuoting(name), doesNeed)
	
	samples = [
		("RA(J2000)", True),
		("ABS", True),
		("AbS", True),
		("r", False),
		("_r", True),
		("someThingNormal", False),
		("foo.bar", True),
	]


class AnnotationTest(testhelpers.VerboseTest):
	def testBasic(self):
		col = valuemappers.AnnotatedColumn(base.parseFromString(rscdef.Column,
			'<column name="abc" type="integer" displayHint="sf=2" required="True"/>'))
		self.assertEqual(col.original.name, "abc")
		self.assertEqual(col["name"], "abc")
		self.assertTrue(col["displayHint"] is col.original.displayHint)
		self.assertEqual(col["datatype"], "int")
		self.assertEqual(col["arraysize"], None)
		self.assertTrue(col["id"] is None)

	def testSetting(self):
		col = valuemappers.AnnotatedColumn(base.parseFromString(rscdef.Column,
			'<column name="abc" type="integer" displayHint="sf=2"/>'))
		col["name"] = "changed"
		self.assertEqual(col.original.name, "abc")
		self.assertEqual(col["name"], "changed")

	def testWrapping(self):
		col = valuemappers.AnnotatedColumn(testhelpers.getTestRD(
			).getById("pgs_siaptable").getColumnByName("dateObs"))
		self.assertEqual(col["ucd"], "time;obs.exposure")


class MapperBasicTest(testhelpers.VerboseTest):
	def testFactorySequence(self):
		m1 = lambda cp: lambda val: "First"
		m2 = lambda cp: lambda val: "Second"
		mf = valuemappers.ValueMapperFactoryRegistry()
		mf.registerFactory(m1)
		self.assertEqual(mf.getMapper({})(0), "First",
			"Registring mappers doesn't work")
		mf.registerFactory(m2)
		self.assertEqual(mf.getMapper({})(0), "Second",
			"Factories registred later are not tried first")


class _MapsToMixin(testhelpers.VerboseTest):
	def assertMapsTo(self, colDef, inValue, expectedValue,
			expectedAttributes=[]):
		column = base.parseFromString(rscdef.Column,
			"<column %s</column>"%colDef)
		annCol = valuemappers.AnnotatedColumn(column)
		res = valuemappers.defaultMFRegistry.getMapper(annCol)(inValue)
		if isinstance(expectedValue, float):
			self.assertAlmostEqual(expectedValue, res, places=3)
		else:
			self.assertEqual(expectedValue, res)
		
		for key, value in expectedAttributes:
			self.assertEqual(annCol[key], value)


class _EnumeratedMapperTest(_MapsToMixin,
		metaclass=testhelpers.SamplesBasedAutoTest):
	def _runTest(self, sample):
		self.assertMapsTo(*sample)

	samples = []


class StandardMapperTest(_EnumeratedMapperTest):
	samples = [
# 0
		('name="d" type="date">',
			datetime.date(2003, 5, 4), "2003-05-04",
			[("xtype", "timestamp")]),
		('name="d" type="date" unit="yr">',
			datetime.date(2003, 5, 4), 2003.33607118,
			[("datatype", "double")]),
		('name="d" type="date" unit="d">',
			datetime.date(2003, 5, 4), 2452763.5),
		('name="d" type="timestamp" unit="d">',
			datetime.datetime(2003, 5, 4, 20, 23), 2452764.34931),
		('name="d" type="date" unit="d" ucd="VOX:Image_MJDateObs">',
			datetime.date(2003, 5, 4), 52763.0),
# 5
		('name="d" type="date" unit="yr">',
			None, None),
		('name="d" type="integer"><values nullLiteral="-1"/>',
			None, None),
		('name= "b" type= "spoint">', pgsphere.SPoint.fromDegrees(2, 12),
			(2., 12.)),
		('name="d" unit="d" type="timestamp">',
			datetime.datetime(2005, 6, 4, 23, 12, 21),
			2453526.4669097224),
		('name="d" unit="d" type="timestamp">',
			datetime.datetime(1952, 1, 3, 3, 59, 1),
			2434014.6659837961),
# 10
		('name="d" unit="d" type="timestamp">',
			datetime.datetime(2000, 12, 31, 12, 00, 00),
			2451910.0),
		('name="d" unit="d" type="timestamp">',
			datetime.datetime(2000, 12, 31, 11, 59, 59),
			2451909.999988426),
		('name="d" unit="d" xtype="mjd">',
			54320.2,
			54320.2),
		('name="d" type="timestamp" xtype="timestamp">',
			datetime.datetime(2000, 12, 31, 11, 59, 59),
			"2000-12-31T11:59:59"),
		('name="c" type="scircle" xtype="adql:REGION">',
			pgsphere.SCircle.fromDALI([10, 15, 3]),
			"Circle UNKNOWNFrame 10. 15. 3."),
# 15
		('name="b" type= "spoint" xtype="adql:POINT">',
			pgsphere.SPoint.fromDegrees(2, 12),
			"Position UNKNOWNFrame 2. 12."),
]


class _MappingTestBase(testhelpers.VerboseTest,
		metaclass=testhelpers.SamplesBasedAutoTest):
	mfRegistry = None

	def _getMapped(self, colDef, inValue):
		column = base.parseFromString(rscdef.Column,
			"<column %s</column>"%colDef)
		annCol = valuemappers.AnnotatedColumn(column)
		return annCol, self.mfRegistry.getMapper(annCol)(inValue)

	def _runTest(self, args):
		colDef, inValue, expected, propDict = args
		annCol, res = self._getMapped(colDef, inValue)

		if isinstance(res, types.GeneratorType):
			# that's stan being returned from the HTML mapper
			rendered = formal.flattenSync(res).decode("utf-8")
			self.assertEqual(rendered, expected)
		else:
			self.assertEqual(res, expected)

		for key, value in propDict.items():
			self.assertEqual(annCol[key], value)


class HTMLMapperTest(_MappingTestBase):
	mfRegistry = htmltable._htmlMFRegistry
	
	samples = [
		('name="d" unit="d" type="double precision" displayHint="type=humanDate">',
			2451909.999988426,
			"2000-12-31T11:59:59Z",
			{"xtype": "timestamp", "arraysize": "*", "unit": ""}),
		('name="d" unit="d" ucd="vox:MJDTrash" displayHint="type=humanDate">',
			51909.499988,
			"2000-12-31T11:59:59Z",
			{"xtype": "timestamp", "arraysize": "*", "unit": ""}),
		('name="d" unit="d" xtype="mjd" displayHint="type=humanDate">',
			51909.499988,
			"2000-12-31T11:59:59Z",
			{"xtype": "timestamp", "arraysize": "*", "unit": ""}),
		('name="d" unit="yr" displayHint="type=humanDate">',
			1994.25,
			"1994-04-02T07:30:00Z",
			{"xtype": "timestamp", "arraysize": "*", "unit": ""}),
		('name="d" unit="s" displayHint="type=humanDate">',
			1e9,
			"2001-09-09T01:46:40Z",
			{"xtype": "timestamp", "arraysize": "*", "unit": ""}),
# 05
		('name="d" type="timestamp" displayHint="type=humanDate">',
			datetime.datetime(2001, 9, 9, 1, 46, 40),
			"2001-09-09 01:46:40",
			{"xtype": "timestamp", "arraysize": "*", "unit": ""}),
		('name="d" type="date" displayHint="type=humanDate">',
			datetime.date(2001, 9, 9),
			"2001-09-09 00:00:00",
			{"xtype": "timestamp", "arraysize": "*", "unit": ""}),
		('name="d" type="date" displayHint="type=humanDay">',
			datetime.date(2001, 9, 9),
			"2001-09-09",
			{"xtype": "timestamp", "arraysize": "*", "unit": ""}),
		('name="d" type="double precision" displayHint="type=dms,sf=3">',
			245.3002,
			"+245 18 00.720",
			{"unit": "d:m:s"}),
		('name="a" type="real[2]" unit="deg/pix"'
			' displayHint="displayUnit=arcsec/pix,sf=4">',
			[0.002, 0.004],
			"[7.2000, 14.4000]",
			{"unit": "arcsec/pix"}),
# 10
		('name="src" type="text" displayHint="type=bibcode">',
			"2014ApJ...786L...5A, 2015A&C....10...88D",
			'<a href="https://ui.adsabs.harvard.edu/abs/2014ApJ...786L...5A/abstract">2014ApJ...786L...5A</a> <a href="https://ui.adsabs.harvard.edu/abs/2015A%26C....10...88D/abstract">2015A&amp;C....10...88D</a> ',
			{}),
		('name="src" type="text" ucd="meta.bib.bibcode">',
			"2014ApJ...786L...5A, 2015A&C....10...88D",
			'<a href="https://ui.adsabs.harvard.edu/abs/2014ApJ...786L...5A/abstract">2014ApJ...786L...5A</a> <a href="https://ui.adsabs.harvard.edu/abs/2015A%26C....10...88D/abstract">2015A&amp;C....10...88D</a> ',
			{}),
		('name="nu" displayHint="spectralUnit=m,sf=1" unit="MHz">',
			70,
			"4.3",
			{"unit": "m"}),
		('name="a" type="double precision" unit="km/s"'
			' displayHint="displayUnit=m/s">',
			None,
			"N/A",
			{"unit": "m/s"}),
	]

	def testBadSpectralUnitMessage(self):
		self.assertRaisesWithMsg(
			base.IncompatibleUnits,
			"km/s is not a spectral unit understood here",
			self._getMapped,
			('name="nu" displayHint="spectralUnit=J" unit="km/s">', 0))

	def testDMMJD(self):
		td = base.parseFromString(rscdef.TableDef,
			"""<table id="t"><dm>
				(votable:Coords) { time: {
					frame: {time0: "MJD-origin"}
					location: @mytime}}</dm>
				<column name="mytime" unit="d" displayHint="type=humanDate"/>
			</table>""")
		annCol = valuemappers.AnnotatedColumn(td.getColumnByName("mytime"))
		self.assertEqual(
			self.mfRegistry.getMapper(annCol)(54300),
			"2007-07-19T00:00:00Z")

	def testWithAnchor(self):
		rendered = formal.flattenSync(
			self._getMapped(
				'name="l" type="text" displayHint="type=product">'
					'<property key="anchorText">Image</property>', "a/b/c")[1])

		self.assertEqual(rendered, b'<a href="http://localhost:8080/getproduct'
			b'/a/b/c" onmouseover="insertPreview(this, null)"'
			b' class="productlink">Image</a>')


class VOTableMapperTest(_MappingTestBase):
	mfRegistry = valuemappers.defaultMFRegistry

	samples = [
		('name="pos" unit="deg" type="spoint">',
			pgsphere.SPoint.fromDegrees(90, 45),
			(90.0, 45.0),
			{"xtype": "point", "datatype": "double", "arraysize": '2'}),
		('name="dint" type="timestamp[2]">',
			[datetime.datetime(2008, 0o4, 0o1), datetime.datetime(2008, 0o5, 0o1)],
			[datetime.datetime(2008, 0o4, 0o1), datetime.datetime(2008, 0o5, 0o1)],
			{"xtype": "timestamp-interval", "datatype": "char", "arraysize": '19x2'}),
		('name="dint" type="timestamp[3]">',
			[datetime.datetime(2008, 0o4, 0o1),
				datetime.datetime(2008, 0o5, 0o1),
				datetime.datetime(2008, 0o6, 0o7),],
			[datetime.datetime(2008, 0o4, 0o1),
				datetime.datetime(2008, 0o5, 0o1),
				datetime.datetime(2008, 0o6, 0o7),],
			{"xtype": "timestamp", "datatype": "char", "arraysize": '19x3'}),
		('name="poly" type="spoly">',
			pgsphere.SPoly.fromDALI([1, 2, 2, 3, 3, 2, 1, 2]),
			[1.0, 2.0, 2.0, 3.0, 3.0, 2.0, 1.0, 2.0],
			{"xtype": "polygon", "datatype": "double", "arraysize": "*"}),
	]


class ProductMapperTest(_MapsToMixin,
		metaclass=testhelpers.SamplesBasedAutoTest):
	def _runTest(self, sample):
		colDef, prodName, encoded = sample
		colDef = colDef+' type="text">'
		prodLink = "http://localhost:8080/getproduct/"+encoded
		self.assertMapsTo(colDef, prodName, prodLink)
	
	samples = [
		('name="accref" displayHint="type=product"', "gobba", "gobba"),
		('name="uuxj" utype="ssa:Access.Reference"',
			"gobba", "gobba"),
		('name="uuxj" displayHint="type=product"',
			"gobba", "gobba"),
		('name="uuxj" ucd="VOX:Image_AccessReference"',
			"gobba", "gobba"),
		('name="accref" displayHint="type=product"',
			"wierdo+name/goes somewhere&is/bad",
			"wierdo%2Bname/goes%20somewhere%26is/bad"),
		]


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