# -*- coding: utf-8 -*-

"""This test unit checks control of dataset timestamps with track_times.

"""

from __future__ import print_function
from __future__ import absolute_import
import hashlib
import sys
import time

import tables
from tables import (
    StringAtom, Int16Atom, StringCol, IntCol, Int16Col,
)
from tables.tests import common
from tables.tests.common import unittest
from tables.tests.common import PyTablesTestCase as TestCase
from six.moves import range

HEXDIGEST = '2aafb84ab739bb4ae61d2939dc010bfd'


class Record(tables.IsDescription):
    var1 = StringCol(itemsize=4)  # 4-character String
    var2 = IntCol()               # integer
    var3 = Int16Col()             # short integer


class TrackTimesMixin(object):
    def _add_datasets(self, group, j, track_times):
        # Create a table
        table = self.h5file.create_table(group, 'table{}'.format(j),
                                         Record,
                                         title=self.title,
                                         filters=None,
                                         track_times=track_times)
        # Get the record object associated with the new table
        d = table.row
        # Fill the table
        for i in range(self.nrows):
            d['var1'] = '%04d' % (self.nrows - i)
            d['var2'] = i
            d['var3'] = i * 2
            d.append()      # This injects the Record values
        # Flush the buffer for this table
        table.flush()

        # Create a couple of arrays in each group
        var1List = [x['var1'] for x in table.iterrows()]
        var3List = [x['var3'] for x in table.iterrows()]

        self.h5file.create_array(group, 'array{}'.format(j),
                                 var1List, "col {}".format(j),
                                 track_times=track_times)

        # Create CArrays as well
        self.h5file.create_carray(group, name='carray{}'.format(j),
                                  obj=var3List,
                                  title="col {}".format(j + 2),
                                  track_times=track_times)

        # Create EArrays as well
        ea = self.h5file.create_earray(group, 'earray{}'.format(j),
                                       StringAtom(itemsize=4), (0,),
                                       "col {}".format(j + 4),
                                       track_times=track_times)
        # And fill them with some values
        ea.append(var1List)

        # Finally VLArrays too
        vla = self.h5file.create_vlarray(group, 'vlarray{}'.format(j),
                                         Int16Atom(),
                                         "col {}".format(j + 6),
                                         track_times=track_times)
        # And fill them with some values
        vla.append(var3List)


class TimestampTestCase(TrackTimesMixin, common.TempFileMixin, TestCase):
    title = "A title"
    nrows = 10

    def setUp(self):
        super(TimestampTestCase, self).setUp()
        self.populateFile()

    def populateFile(self):
        group = self.h5file.root
        for j in range(4):
            track_times = bool(j % 2)
            self._add_datasets(group, j, track_times)

    def test00_checkTimestamps(self):
        """Checking retrieval of timestamps"""

        for pattern in ('/table{}',
                        '/array{}',
                        '/carray{}',
                        '/earray{}',
                        '/vlarray{}'):
            # Verify that:
            # - if track_times was False, ctime is 0
            # - if track_times was True, ctime is not 0,
            #   and has either stayed the same or incremented
            tracked_ctimes = []
            for j in range(4):
                track_times = bool(j % 2)
                node = pattern.format(j)
                obj = self.h5file.get_node(node)
                # Test property retrieval
                self.assertEqual(obj.track_times, track_times)
                timestamps = obj._get_obj_timestamps()
                self.assertEqual(timestamps.atime, 0)
                self.assertEqual(timestamps.mtime, 0)
                self.assertEqual(timestamps.btime, 0)
                if not track_times:
                    self.assertEqual(timestamps.ctime, 0)
                else:
                    self.assertNotEqual(timestamps.ctime, 0)
                    tracked_ctimes.append(timestamps.ctime)
            self.assertTrue(tracked_ctimes[1] >= tracked_ctimes[0])


class BitForBitTestCase(TrackTimesMixin, common.TempFileMixin, TestCase):
    title = "A title"
    nrows = 10

    def repopulateFile(self, track_times):
        self.h5file.close()
        self.h5file = tables.open_file(self.h5fname, mode="w")
        group = self.h5file.root
        self._add_datasets(group, 1, track_times)
        self.h5file.close()

    def test00_checkReproducibility(self):
        """Checking bit-for-bit reproducibility with no track_times"""

        self.repopulateFile(track_times=False)
        hexdigest_wo_track_1 = self._get_digest(self.h5fname)
        self.repopulateFile(track_times=True)
        hexdigest_w_track_1 = self._get_digest(self.h5fname)
        time.sleep(1)
        self.repopulateFile(track_times=True)
        hexdigest_w_track_2 = self._get_digest(self.h5fname)
        self.repopulateFile(track_times=False)
        hexdigest_wo_track_2 = self._get_digest(self.h5fname)
        self.assertEqual(HEXDIGEST, hexdigest_wo_track_1)
        self.assertEqual(hexdigest_wo_track_1, hexdigest_wo_track_2)
        self.assertNotEqual(hexdigest_wo_track_1, hexdigest_w_track_1)
        self.assertNotEqual(hexdigest_w_track_1, hexdigest_w_track_2)

    def _get_digest(self, filename):
        md5 = hashlib.md5()
        fd = open(filename, 'rb')

        for data in fd:
            md5.update(data)

        fd.close()

        hexdigest = md5.hexdigest()

        return hexdigest


def suite():
    theSuite = unittest.TestSuite()
    niter = 1
    # common.heavy = 1 # Uncomment this only for testing purposes!

    for i in range(niter):
        theSuite.addTest(unittest.makeSuite(TimestampTestCase))
        theSuite.addTest(unittest.makeSuite(BitForBitTestCase))

    return theSuite


if __name__ == '__main__':
    common.parse_argv(sys.argv)
    common.print_versions()
    unittest.main(defaultTest='suite')
