# coding: utf-8
# /*##########################################################################
# Copyright (C) 2016-2018 European Synchrotron Radiation Facility
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# ############################################################################*/
"""Tests for SpecFile to HDF5 converter"""

from numpy import array_equal
import os
import sys
import tempfile
import unittest

try:
    import h5py
except ImportError:
    h5py_missing = True
else:
    h5py_missing = False
    from ..spech5 import SpecH5, SpecH5Group
    from ..convert import convert, write_to_h5

__authors__ = ["P. Knobel"]
__license__ = "MIT"
__date__ = "12/02/2018"


sftext = """#F /tmp/sf.dat
#E 1455180875
#D Thu Feb 11 09:54:35 2016
#C imaging  User = opid17
#O0 Pslit HGap  MRTSlit UP  MRTSlit DOWN
#O1 Sslit1 VOff  Sslit1 HOff  Sslit1 VGap
#o0 pshg mrtu mrtd
#o2 ss1vo ss1ho ss1vg

#J0 Seconds  IA  ion.mono  Current
#J1 xbpmc2  idgap1  Inorm

#S 1  ascan  ss1vo -4.55687 -0.556875  40 0.2
#D Thu Feb 11 09:55:20 2016
#T 0.2  (Seconds)
#P0 180.005 -0.66875 0.87125
#P1 14.74255 16.197579 12.238283
#N 4
#L MRTSlit UP  second column  3rd_col
-1.23 5.89  8
8.478100E+01  5 1.56
3.14 2.73 -3.14
1.2 2.3 3.4

#S 1 aaaaaa
#D Thu Feb 11 10:00:32 2016
#@MCADEV 1
#@MCA %16C
#@CHANN 3 0 2 1
#@CALIB 1 2 3
#N 3
#L uno  duo
1 2
@A 0 1 2
@A 10 9 8
3 4
@A 3.1 4 5
@A 7 6 5
5 6
@A 6 7.7 8
@A 4 3 2
"""


@unittest.skipIf(h5py_missing, "Could not import h5py")
class TestConvertSpecHDF5(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        fd, cls.spec_fname = tempfile.mkstemp(text=False)
        if sys.version_info < (3, ):
            os.write(fd, sftext)
        else:
            os.write(fd, bytes(sftext, 'ascii'))
        os.close(fd)

        fd, cls.h5_fname = tempfile.mkstemp(text=False)
        # Close and delete (we just need the name)
        os.close(fd)
        os.unlink(cls.h5_fname)

    @classmethod
    def tearDownClass(cls):
        os.unlink(cls.spec_fname)

    def setUp(self):
        convert(self.spec_fname, self.h5_fname)

        self.sfh5 = SpecH5(self.spec_fname)
        self.h5f = h5py.File(self.h5_fname, "a")

    def tearDown(self):
        self.h5f.close()
        self.sfh5.close()
        os.unlink(self.h5_fname)

    def testAppendToHDF5(self):
        write_to_h5(self.sfh5, self.h5f, h5path="/foo/bar/spam")
        self.assertTrue(
            array_equal(self.h5f["/1.2/measurement/mca_1/data"],
                        self.h5f["/foo/bar/spam/1.2/measurement/mca_1/data"])
        )

    def testWriteSpecH5Group(self):
        """Test passing a SpecH5Group as parameter, instead of a Spec filename
        or a SpecH5."""
        g = self.sfh5["1.1/instrument"]
        self.assertIsInstance(g, SpecH5Group)        # let's be paranoid
        write_to_h5(g, self.h5f, h5path="my instruments")

        self.assertAlmostEqual(self.h5f["my instruments/positioners/Sslit1 HOff"][tuple()],
                               16.197579, places=4)

    def testTitle(self):
        """Test the value of a dataset"""
        title12 = self.h5f["/1.2/title"].value
        self.assertEqual(title12,
                         u"aaaaaa")

    def testAttrs(self):
        # Test root group (file) attributes
        self.assertEqual(self.h5f.attrs["NX_class"],
                         u"NXroot")
        # Test dataset attributes
        ds = self.h5f["/1.2/instrument/mca_1/data"]
        self.assertTrue("interpretation" in ds.attrs)
        self.assertEqual(list(ds.attrs.values()),
                         [u"spectrum"])
        # Test group attributes
        grp = self.h5f["1.1"]
        self.assertEqual(grp.attrs["NX_class"],
                         u"NXentry")
        self.assertEqual(len(list(grp.attrs.keys())),
                         1)

    def testHdf5HasSameMembers(self):
        spec_member_list = []

        def append_spec_members(name):
            spec_member_list.append(name)
        self.sfh5.visit(append_spec_members)

        hdf5_member_list = []

        def append_hdf5_members(name):
            hdf5_member_list.append(name)
        self.h5f.visit(append_hdf5_members)

        # 1. For some reason, h5py visit method doesn't include the leading
        # "/" character when it passes the member name to the function,
        # even though an explicit the .name attribute of a member will
        # have a leading "/"
        spec_member_list = [m.lstrip("/") for m in spec_member_list]

        self.assertEqual(set(hdf5_member_list),
                         set(spec_member_list))

    def testLinks(self):
        self.assertTrue(
            array_equal(self.sfh5["/1.2/measurement/mca_0/data"],
                        self.h5f["/1.2/measurement/mca_0/data"])
        )
        self.assertTrue(
            array_equal(self.h5f["/1.2/instrument/mca_1/channels"],
                        self.h5f["/1.2/measurement/mca_1/info/channels"])
        )


def suite():
    test_suite = unittest.TestSuite()
    test_suite.addTest(
        unittest.defaultTestLoader.loadTestsFromTestCase(TestConvertSpecHDF5))
    return test_suite


if __name__ == '__main__':
    unittest.main(defaultTest="suite")
