"""
Unit tests for our pgsphere interface.
"""

#c Copyright 2008-2024, 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 astropy import coordinates
from astropy import units as u

from gavo.helpers import testhelpers

from gavo import utils
from gavo.utils import DEG
from gavo.utils import pgsphere

import tresc


# this is http://alasky.u-strasbg.fr/footprints/cats/vizier/I/206?product=MOC&nside=64
_SAMPLE_FITS_MOC = utils.getDirtyBytes("""
eJztl2tMW2Ucxh810WhijNH4QY05iRq2qEOI2eYFI20PUGxPO9rKZbqtQGFFKKNQKAiJTjeNt206
3eYE4m2aZYOp87YpxGxeolHHFplm4gSdGjM1Xpbsg7dfwUQyKQRcjInnSX4f+p7zPu//9ianAbfX
7zENI8eYQEEj0yiOR5uaIjGjvNXw+pxGTbg5bOT63cYs/5y8SDwWbUhEZo/b4nAH/e6SNH7zJ1qc
XFZuiTtgpPG7fPp+Bf6SVBoT+80l3wIz1+OPJg1vorYp6otXRuKGs745Eg9XR4y68DJjVl04adSn
1kfTNkuCpuVKW79pa9TMli1btmzZsmXL1n9Wqc+/gNtn5RgZDrcVzHV4zIxxj//6nqwY/xGZXv/q
93P2DP2y0vhdMUO/7An9sudlTd/P7/SFrODx+7+QP5nfDOIL5rlNjytwHP18Rd7RfmRkFY4tZUy+
Ywq/YKnfHPNjDk3PMX6ZBrMcqU1NdWqoQ5Z7gRFL1JXzL2li+RyBxRZOafqbaVhju+urjGUp58Yp
4vMVucwit5VPfNbo6cfG543Eq6OxaoNb6TKNcKzSsMxA0HSN3qq/y+nDMVAawM/559I/qp/9/82W
LVu2bNmaWlJuBwzAIPwmOR6B/TAE30rO+bAe+mEXsO76QDJdcAdsga+kvCfhRSm/EX6XCrzwPnwv
uQUnAD7ul6VC3ilcAc/ATnhNuv5CuAzmQjWsgSdgk+Q5BRZIXt717pGs86AQFkE3DEv+TGnBfVIR
eQROhbNhHXwIn0vB06UQMYTYFyLn0CvwnnTDlbAcboeH4Cep+CQgxuJ7pRLyKr0HOqUy8ijDu2wO
cF7ZVXBYWpgNxHzjbGnRPGnx+dKSr6Xy76QK8qngWcXbUuVZEIYH4CUgtsoRKXIBmFJVBuBdRR+q
24C6LvVDMfwgRa8B6h7leQ0x1bwj1Z4LO6RYr1S/AX6UGsivgXfi10mNUdgI9KbxVdgLv0hN1C5x
GjADCeJPXA0LgbonHgN6k3gW3gTmI0GszXfBC4BXC/taOK/lGylJLZKclaRASTySz0utF8GlwLNW
4m7NAfrQRg/azgBybcuDh+Fx6ZZrYbfUTq/byam9DlYDzzroXQe97eB3x7vws3RrjbT8JqAuK1qk
lazfH5NWOeCotPp1aQ3xPJgAZnftiXCOtH67tIFzNxLLoyGpk3p3srdrJTBvXcxPNz3rrgBmq5s+
dRPXJuJ7ulXazH3YTF+39Ehb6dHW26QeatBDTXsZ9F7mddsb0nPksJ0a7Vgi7WSe+si5j/vS96vU
T136iau/S9pFb3bfLb3FndpzRBpYCsztAHM3QP33OqV95L8vDvR2EN9Bct9fKX3E748vAeb0AHfr
AOd+Qq5D9HyImf70YlgL1PAg+R3kLn7GO8PlgMcwMz28TRo5GejTSBAiwF0YuROIa+Qp6YtVcEg6
dLN0+EzpyJfSUWK3ZcuWLVu2/p/6A9x9I/E=""")


class AsPolyTest(testhelpers.VerboseTest):
	def _assertCircleBecomesPolygon(self, alpha, delta, radius):
		alpha, delta, radius = alpha*DEG, delta*DEG, radius*DEG
		c = pgsphere.SCircle(pgsphere.SPoint(alpha, delta), radius)
		poly = c.asPoly()
		for pt in poly.points:
			self.assertAlmostEqual(radius,
				coordinates.angular_separation(
					pt.x*u.rad, pt.y*u.rad, alpha*u.rad, delta*u.rad).to(u.rad).value)
	
	def testCircle1AsPoly(self):
		self._assertCircleBecomesPolygon(0, 90, 3)

	def testCircle2AsPoly(self):
		self._assertCircleBecomesPolygon(0, 0, 8)

	def testCircle3AsPoly(self):
		self._assertCircleBecomesPolygon(80, -10, 20)

	def testCircle4AsPoly(self):
		self._assertCircleBecomesPolygon(120, -80, 1)

	def testCircle5AsPoly(self):
		self._assertCircleBecomesPolygon(220, -45, 1)

	def testCircle6AsPoly(self):
		self._assertCircleBecomesPolygon(320, 45, 90)

	def testPolyAsPoly(self):
		p = pgsphere.SPoly([pgsphere.SPoint(*p) for p in
			((0.5, 0.4), (1, -0.2), (1.5, 0))])
		self.assertTrue(p.asPoly() is p)
	
	def testNormSboxAsPoly(self):
		b = pgsphere.SBox(pgsphere.SPoint(0.2, -0.5), pgsphere.SPoint(2, 0.1))
		self.assertEqual(b.asPoly(),
			pgsphere.SPoly([pgsphere.SPoint(*p) for p in
			((0.2, -0.5), (0.2, 0.1), (2, 0.1), (2, -0.5))]))

	def testInvSboxAsPoly(self):
		b = pgsphere.SBox(pgsphere.SPoint(2, 0.1), pgsphere.SPoint(-0.1, -0.5))
		self.assertEqual(b.asPoly(),
			pgsphere.SPoly([pgsphere.SPoint(*p) for p in
			((-0.1, -0.5), (-0.1, 0.1), (2, 0.1), (2, -0.5))]))


class FromDALITest(testhelpers.VerboseTest):
	def testShortPolyDALIConstructor(self):
		self.assertRaisesWithMsg(ValueError,
			"Need an even-numbered number (>=6) of floats in a DALI polygon representation, got 4 floats.",
			pgsphere.SPoly.fromDALI,
			([1, 1, 2, 2],))

	def testOddPolyDALIConstructor(self):
		self.assertRaisesWithMsg(ValueError,
			"Need an even-numbered number (>=6) of floats in a DALI polygon representation, got 7 floats.",
			pgsphere.SPoly.fromDALI,
			([-1.5372606267401516, 85.26415832196923, -1.5372606267401516, 85.25833260227361, -1.5401725504711685, 85.25541973647518, -1.5416285063290134],))

	def testCoveringCircle(self):
		points = [pgsphere.SPoint.fromDegrees(*v) for v in [
			(2, 3), (355, -9), (0, 0)]]
		c = pgsphere.SCircle.fromPointSet(points)
		self.assertEqual(c.asSTCS("X"),
			"Circle X 359.0157337607 -1.9990620034 6.2881158141")


class MOCDryTest(testhelpers.VerboseTest):
	def testBadMOCRandom(self):
		self.assertRaisesWithMsg(ValueError,
			"No order separator visible in MOC literal 'Habe nun Philosophie"
				"/Juristerei/Medic...'",
			pgsphere.SMoc.fromASCII,
			("Habe nun Philosophie/Juristerei/Medicin und leider ach Theologie",))

	def testBadMOCJunkAtStart(self):
		self.assertRaisesWithMsg(ValueError,
			"MOC literal 'Habe 1/3,4 3/45,9' does not start with order spec",
			pgsphere.SMoc.fromASCII,
			("Habe 1/3,4 3/45,9",))
	
	def testJunkInCenter(self):
		self.assertRaisesWithMsg(ValueError,
			"MOC literal syntax error at char 4",
			pgsphere.SMoc.fromASCII,
			("1/3, Habe, 4 3/45,9",))

	def testMOCConstruction(self):
		m = pgsphere.SMoc.fromASCII("1/1\n3 4 2/4 25 12-14 21")
		self.assertEqual(m.moc[2], frozenset([4, 12, 13, 14, 21, 25]))

	def testFITSConstructor(self):
		m = pgsphere.SMoc.fromFITS(_SAMPLE_FITS_MOC)
		self.assertTrue(14400 in m.moc[6])

	def testToASCII(self):
		m = pgsphere.SMoc.fromASCII("1/1,3,4 2/4,25,12-14,21")
		self.assertEqual(m.asASCII(),
			"1/1 3-4 2/4 12-14 21 25")

	def testMocFromPoly(self):
		m = pgsphere.SPoly.fromDALI([0, 0, 45, 0, 0, 90]).asSMoc(order=4)
		self.assertEqual(m.asASCII(),
			'1/2 2/2 14 71 77 3/2 14 50 62 279 283 305 317'
			' 4/2 14 50 62 194 206 242 254 1111 1115 1127 1131 1217 1229 1265 1277'
			' 4/')
	
	def testMocFromCircle(self):
		m = pgsphere.SCircle.fromDALI([-50, 33, 2]).asPoly().asSMoc()
		self.assertEqual(m.asASCII(),
			'5/3309 6/13233-13235 13240-13241 13243-13246 13280 13282 6/')

	def testMocFromCircleInclusive(self):
		m = pgsphere.SCircle.fromDALI([-50, 33, 2]).asPoly().asSMoc(inclusive=True)
		self.assertEqual(m.asASCII(),
			'4/827 6/13211 13214-13215 13258 13280 13282 13288 13290 14609 14612 6/')

	def testCircleSMoc(self):
		c = pgsphere.SCircle.fromDALI([250, -79, 0.25])
		self.assertEqual(
			c.asSMoc(order=7).asSTCS("X"),
			"MOC 7/164157 7/")
	
	def testMaxOrderParsed(self):
		m = pgsphere.SMoc.fromASCII("3/5,6 7/")
		self.assertEqual(m.maxOrder, 7)
	
	def testMaxOrderSerialialised(self):
		m = pgsphere.SMoc.fromASCII("7/ 0/1-2")
		self.assertEqual(m.asASCII(), "0/1-2 7/")
	
	def testMaxOrderWithRegrid(self):
		m = pgsphere.SMoc.fromASCII("4/55 9/40031-43210")
		m2 = m.asSMoc(3)
		self.assertEqual(m2.maxOrder, 3)

	def testMaxOrderLower(self):
		m = pgsphere.SMoc.fromASCII("0/2-3 4/")
		m2 = m.asSMoc(7)
		self.assertEqual(m2.maxOrder, 4)
	

class InDBTest(testhelpers.VerboseTest):
	resources = [("conn", tresc.dbConnection)]

	def makeTable(self, testedType, testValue):
		self.conn.execute("CREATE TABLE pgstest (col %s)"%testedType)
		self.conn.execute("INSERT INTO pgstest (col) VALUES (%(val)s)",
			{"val": testValue})

	def assertTripsRound(self, testedType, testValue):
		try:
			self.makeTable(testedType, testValue)
			res = list(self.conn.query("SELECT * from pgstest"))
			self.assertEqual(res[0][0], testValue)
		finally:
			self.conn.rollback()

	def testSPoints(self):
		self.assertTripsRound("spoint", pgsphere.SPoint(2,0.5))

	def testSCircle(self):
		self.assertTripsRound("scircle",
			pgsphere.SCircle(pgsphere.SPoint(2,0.5), 0.25))

	def testSPoly(self):
		self.assertTripsRound("spoly",
			pgsphere.SPoly([pgsphere.SPoint(2,0.5),
				pgsphere.SPoint(2.5,-0.5),
				pgsphere.SPoint(1.5,0),]))

	def testSBox(self):
		self.assertTripsRound("sbox",
			pgsphere.SBox(pgsphere.SPoint(2.5,-0.5),
				pgsphere.SPoint(2.0,0.5)))

	def testSMocRoundTrip(self):
		self.assertTripsRound("smoc",
			pgsphere.SMoc.fromASCII('29/2-5,20-29,123,444,17-21,33-39,332-339,0-1'))


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