# Use kernel/python/linedb/pro/runtests.py to run these tests.

import os
from linedb import line
from linedb import jpl
from linedb import cdms
from linedb import db

def cmp_lines(l1, l2):
   assert l1.species == l2.species
   assert l1.frequency == l2.frequency
   assert l1.einstein_coefficient - l2.einstein_coefficient < 1e-9
   assert l1.upper_level.energy - l2.upper_level.energy < 1e-10
   assert l1.upper_level.statistical_weight == l2.upper_level.statistical_weight
   assert l1.upper_level.quantum_numbers == l2.upper_level.quantum_numbers
   assert l1.lower_level.energy - l2.lower_level.energy < 1e-10
   assert l1.lower_level.statistical_weight == l2.lower_level.statistical_weight
   assert l1.lower_level.quantum_numbers == l2.lower_level.quantum_numbers

def jplLine():
   l = line.line()
   l.species = 'c-C3H'
   l.frequency = 85222.6112
   l.einstein_coefficient = 1.39170629757e-07
   l.upper_level = line.level()
#   l.upper_level.energy = 10.3468203195 # cm-1
   l.upper_level.energy = 14.88674806241731
   l.upper_level.statistical_weight = 9
   l.upper_level.quantum_numbers = '3 1 2 4 4'
   l.lower_level = line.level()
#   l.lower_level.energy = 7.5041 # cm-1
   l.lower_level.energy = 10.796712679416093
   l.lower_level.statistical_weight = 9
   l.lower_level.quantum_numbers = '3 1 3 3 3'
   l.dbsource = 'jpl'
   l.origin = 'jpl'
   return l

class JplTester():
   default = jpl.default

   def test_part_function(self):
      t, q = self.default.part_function('c-C3H', 'jpl', 'jpl')
      assert t == [300., 225., 150., 75., 37.5, 18.75, 9.375]
      assert q == [10356.189770449864, 6813.9665928283175, 3739.3834364315157, 1345.8603540559484, 497.0499105762351, 192.17637629219328, 81.30176988564409]

   def test_search(self):
      l = jplLine()
      got = self.default.search(85220, 85230, 'c-C3H', 'All', 'All', -1, -1)
      for g in got:
         cmp_lines(g, l)
         assert g.dbsource == l.dbsource
         assert g.origin == l.origin

def cdmsLine():
   l = line.line()
   l.species = 'CC-13-H'
   l.frequency = 85229.3354
   l.einstein_coefficient = 1.42429516497e-06
   l.upper_level = line.level()
#   l.upper_level.energy = 2.8488446147 # cm-1
   l.upper_level.energy = 4.09884686680851
   l.upper_level.statistical_weight = 6
   l.upper_level.quantum_numbers = '1 2 2 3'
   l.lower_level = line.level()
#   l.lower_level.energy = 0.0059 # cm-1
   l.lower_level.energy = 0.008488773444990732
   l.lower_level.statistical_weight = 6
   l.lower_level.quantum_numbers = '0 1 1 2'
   l.dbsource = 'cdms'
   l.origin = 'cdms'
   return l

class CdmsTester():
   default = cdms.default

   def test_part_function(self):
      t, q = self.default.part_function('CC-13-H', 'cdms', 'cdms')
      assert t == [300.0, 225.0, 150.0, 75.0, 37.5, 18.75, 9.375]
      assert q == [1176.521857667227, 882.8765871653017, 589.3862502405809, 295.93749953433894, 149.31381777706937, 76.01512254993878, 39.418491471429675]

   def test_search(self):
      l = cdmsLine()
      got = self.default.search(85220, 85230, 'CC-13-H', 'All', 'All', -1, -1)
      for g in got:
         cmp_lines(g, l)
         assert g.dbsource == l.dbsource
         assert g.origin == l.origin

class LocalDbTester():
   foodb = db.Local('./foo.db', 'foo', True)
   bardb = db.Local(os.path.abspath('./bar.db'), 'bar', True)
   nameToDB = {
      'foo': foodb,
      'bar': bardb
   }

   def insert_from_remote(self):
      lines = [jplLine(), cdmsLine()]
      dbin = db.mlinedb([jpl.default, cdms.default])
      self.foodb.insertLines(dbin, lines, False)

   def test_search_all(self):
      lines = [jplLine(), cdmsLine()]
      got = self.foodb.search(-1, -1, 'All', 'All', 'All', -1, -1)
      assert len(got) == 2
      for i,l in enumerate(lines):
         cmp_lines(got[i], l)
         assert got[i].dbsource == self.foodb.name
         assert got[i].origin == l.origin

   searchByParam = {
      'species': [('CC-13-H', 'All', 'All', -1, -1),('c-C3H', 'All', 'All', -1, -1)],
      'origin': [('All', 'cdms', 'All', -1, -1),('All', 'jpl', 'All', -1, -1)],
      'source': [('All', 'All', 'cdms', -1, -1),('All', 'All', 'jpl', -1, -1)]
      }

   def test_search_param(self):
      lines = [cdmsLine(), jplLine()]
      for params in list(self.searchByParam.values()):
         for i,l in enumerate(lines):
            got = self.foodb.search(-1, -1, *(params[i]))
            assert len(got) == 1
            cmp_lines(got[0], l)
            assert got[0].dbsource == self.foodb.name
            assert got[0].origin == l.origin

   def insert_from_local(self):
      dbin = db.mlinedb([self.foodb])
      lines = dbin.search(-1, -1, 'All', 'All', 'All', -1, -1)
      assert len(lines) == 2
      self.bardb.insertLines(dbin, lines, False)

   partfuncParams = {
      'foo': {
         ('CC-13-H', 'cdms', 'cdms'): ([300.0, 225.0, 150.0, 75.0, 37.5, 18.75, 9.375], [1176.521857667227, 882.8765871653017, 589.3862502405809, 295.93749953433894, 149.31381777706937, 76.01512254993878, 39.418491471429675]),
         ('c-C3H', 'jpl', 'jpl'): ([300., 225., 150., 75., 37.5, 18.75, 9.375], [10356.189770449864, 6813.9665928283175, 3739.3834364315157, 1345.8603540559484, 497.0499105762351, 192.17637629219328, 81.30176988564409])
         },
      'bar': {
         ('CC-13-H', 'cdms', 'foo'): ([300.0, 225.0, 150.0, 75.0, 37.5, 18.75, 9.375], [1176.521857667227, 882.8765871653017, 589.3862502405809, 295.93749953433894, 149.31381777706937, 76.01512254993878, 39.418491471429675]),
         ('c-C3H', 'jpl', 'foo'): ([300., 225., 150., 75., 37.5, 18.75, 9.375], [10356.189770449864, 6813.9665928283175, 3739.3834364315157, 1345.8603540559484, 497.0499105762351, 192.17637629219328, 81.30176988564409])
         }
      }

   def test_part_function(self):
      for name, data in list(self.partfuncParams.items()):
         thedb = self.nameToDB[name]
         for p,r in list(data.items()):
            t, q = thedb.part_function(*p)
            assert list(t) == r[0]
            assert list(q) == r[1]

   def test_search_dbsource(self):
      params = [('All', 'All', 'foo', -1, -1),('All', 'All', 'jpl', -1, -1)]
      lines = self.bardb.search(-1, -1, 'All', 'All', 'All', -1, -1)
      got = self.bardb.search(-1, -1, *(params[0]))
      assert len(got) == 2
      for i,l in enumerate(lines):
         cmp_lines(got[i], l)
         assert got[i].dbsource == self.bardb.name
         assert got[i].origin == l.origin
      got = self.bardb.search(-1, -1, *(params[1]))
      assert len(got) == 0

   def test_remove(self):
      torm = self.foodb.search(-1, -1, 'All', 'jpl', 'All', -1, -1)
      assert len(torm) == 1
      self.foodb.removeLines(torm)
      left = self.foodb.search(-1, -1, 'All', 'All', 'All', -1, -1)
      assert len(left) == 1
      torm = self.bardb.search(-1, -1, 'All', 'cdms', 'All', -1, -1)
      assert len(torm) == 1
      self.bardb.removeLines(torm)
      left = self.bardb.search(-1, -1, 'All', 'All', 'All', -1, -1)
      assert len(left) == 1

   def cleanup(self):
      os.remove('./foo.db')
      os.remove('./bar.db')

def main():
   print("Testing jpl")
   jpltester = JplTester()
   jpltester.test_part_function()
   jpltester.test_search()

   print("Testing cdms")
   cdmstester = CdmsTester()
   cdmstester.test_part_function()
   cdmstester.test_search()

   # the order matters
   print("Testing with local dbs")
   localdbtester = LocalDbTester()
   localdbtester.insert_from_remote()
   localdbtester.test_search_all()
   localdbtester.test_search_param()
   localdbtester.insert_from_local()
   localdbtester.test_search_dbsource()
   localdbtester.test_part_function()
   localdbtester.test_remove()
   localdbtester.cleanup()
   print("All tests passed")

if __name__ == "__main__":
   main()
