# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2017 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.
#
# ###########################################################################*/

__authors__ = ["H. Payno"]
__license__ = "MIT"
__date__ = "24/01/2017"


from tomwer.synctools.datatransfert import ScanTransfer
from tomwer.core.scan.scanfactory import ScanFactory
from tomwer.core.utils import rebaseParFile
from tomwer.core.utils.scanutils import MockEDF
from tomwer.tests.utils import UtilsTest
from tomwer.core.scan.hdf5scan import HDF5TomoScan
from nxtomomill.converter import from_h5_to_nx
from nxtomomill.io.config.hdf5config import TomoHDF5Config
from silx.gui.utils.testutils import TestCaseQt
from glob import glob
import unittest
import os
import tempfile
import shutil
import logging
import pytest

try:
    from tomwer.synctools.rsyncmanager import RSyncManager
except ImportError:
    has_rsync = False
else:
    has_rsync = True

logging.disable(logging.INFO)


class TestEDFDataTransfert(TestCaseQt):
    """
    Test that the folder transfert process is valid
    """

    def setUp(self):
        TestCaseQt.setUp(self)
        self.sourcedir = tempfile.mkdtemp()
        self.n_file = 10
        MockEDF.fastMockAcquisition(self.sourcedir, n_radio=self.n_file)
        self.scan = ScanFactory.create_scan_object(self.sourcedir)
        assert os.path.isdir(self.sourcedir)
        assert os.path.exists(self.sourcedir)
        self.targettedir = tempfile.mkdtemp()
        assert os.path.isdir(self.targettedir)
        assert os.path.exists(self.targettedir)
        self.outputdir = os.path.join(
            self.targettedir, os.path.basename(self.sourcedir)
        )

    def tearDown(self):
        if os.path.isdir(self.sourcedir):
            shutil.rmtree(self.sourcedir)
        if os.path.isdir(self.targettedir):
            shutil.rmtree(self.targettedir)

    def testMoveFiles(self):
        """
        simple test that files are moved
        """
        folder_trans_process = ScanTransfer(
            inputs={
                "data": self.scan,
                "turn_off_print": True,
                "copying": False,
                "dest_dir": self.targettedir,
                "noRsync": True,
            }
        )
        folder_trans_process.run()
        self.assertTrue(os.path.isdir(self.outputdir))
        self.assertTrue(self.checkDataCopied())

    def testCopyFiles(self):
        """
        Simple test that file are copy and deleted
        """
        folder_trans_process = ScanTransfer(
            inputs={
                "data": self.scan,
                "turn_off_print": True,
                "copying": False,
                "dest_dir": self.targettedir,
                "noRsync": True,
                "move": False,
            }
        )
        folder_trans_process.run()
        self.assertTrue(self.checkDataCopied())

    def testMoveFilesForce(self):
        """
        Test the force option of folderTransfert
        """
        assert not os.path.isdir(self.outputdir)
        assert os.path.isdir(self.scan.path)
        folder_trans_process = ScanTransfer(
            inputs={
                "data": self.scan,
                "turn_off_print": True,
                "copying": False,
                "dest_dir": self.targettedir,
                "noRsync": True,
                "move": True,
                "force": False,
            }
        )
        folder_trans_process.run()

        MockEDF.fastMockAcquisition(self.sourcedir, n_radio=self.n_file)
        folder_trans_process = ScanTransfer(
            inputs={
                "data": self.scan,
                "turn_off_print": True,
                "copying": False,
                "dest_dir": self.targettedir,
                "noRsync": True,
                "move": True,
                "force": False,
            }
        )
        with self.assertRaises(shutil.Error):
            self.assertRaises(folder_trans_process.run())

        folder_trans_process = ScanTransfer(
            inputs={
                "data": self.scan,
                "turn_off_print": True,
                "copying": False,
                "dest_dir": self.targettedir,
                "noRsync": True,
                "move": True,
                "force": True,
            }
        )
        folder_trans_process.run()
        self.assertTrue(self.checkDataCopied())

    def testCopyFilesForce(self):
        """
        Test the force option for the copy files process
        """
        assert not os.path.isdir(self.outputdir)
        os.makedirs(self.outputdir)
        assert os.path.isdir(self.outputdir)

        folder_trans_process = ScanTransfer(
            inputs={
                "data": self.scan,
                "turn_off_print": True,
                "copying": False,
                "dest_dir": self.targettedir,
                "noRsync": True,
                "move": False,
                "force": False,
            }
        )
        folder_trans_process.run()
        self.assertTrue(self.checkDataCopied())

        MockEDF.fastMockAcquisition(self.sourcedir, n_radio=self.n_file)
        self.assertTrue(self.scan.path == self.sourcedir)

        folder_trans_process = ScanTransfer(
            inputs={
                "data": self.scan,
                "turn_off_print": True,
                "copying": False,
                "dest_dir": self.targettedir,
                "noRsync": True,
                "move": False,
                "force": False,
            }
        )

        with self.assertRaises(FileExistsError):
            self.assertRaises(folder_trans_process.run())

        folder_trans_process = ScanTransfer(
            inputs={
                "data": self.scan,
                "turn_off_print": True,
                "copying": False,
                "dest_dir": self.targettedir,
                "noRsync": True,
                "move": False,
                "force": True,
            }
        )
        folder_trans_process.run()
        self.assertTrue(self.checkDataCopied())

    def checkDataCopied(self):
        outputFiles = os.listdir(self.outputdir)
        inputFile = glob(self.sourcedir)
        # + 3 because .info and .xml are count + one tomwer_processes file
        return (
            (len(inputFile) == 0)
            and (len(outputFiles) == (self.n_file + 3))
            and (not os.path.isdir(self.sourcedir))
        )


@pytest.mark.skipif(RSyncManager().has_rsync() is False, reason="Rsync is missing")
class TestHDFDataTransfert(TestCaseQt):
    """Make sure we can transfer data from bliss acquisition"""

    def setUp(self):
        super().setUp()
        self.input_dir = tempfile.mkdtemp()
        self.output_dir = tempfile.mkdtemp()
        shutil.copytree(
            UtilsTest.getBlissDataset(folderID="sample"),
            os.path.join(self.input_dir, "sample"),
        )

        self._proposal_file = os.path.join(
            self.input_dir, "sample", "ihpayno_sample.h5"
        )
        assert os.path.exists(self._proposal_file)
        self._sample_file = os.path.join(
            self.input_dir, "sample", "sample_29042021", "sample_29042021.h5"
        )
        assert os.path.exists(self._sample_file)
        self._sample_file_entry = "/1.1"

        output_file_path = os.path.join(
            self.input_dir, "sample", "sample_29042021", "nx_file.nx"
        )
        # convert it to nx
        configuration = TomoHDF5Config()
        configuration.input_file = self._sample_file
        configuration.output_file = output_file_path
        files_entries = (self._sample_file_entry,)
        configuration.entries = files_entries
        configuration.single_file = False
        configuration.overwrite = False
        configuration.request_input = False
        configuration.file_extension = ".nx"
        results = from_h5_to_nx(configuration)

        assert len(results) == 1
        output_file_path, entry = results[0]
        assert os.path.exists(output_file_path)
        assert os.path.isfile(output_file_path)
        self.scan = HDF5TomoScan(scan=output_file_path, entry=entry)

        # register information regarding origin of the file
        from tomwer.core.process.control.datalistener import DataListener

        source_scans = DataListener._get_scan_sources(
            self._sample_file, self._sample_file_entry
        )
        DataListener._register_process(
            process_file=self.scan.process_file,
            process=DataListener,
            entry=self.scan.entry,
            results={"output_file": self.scan.master_file, "entry": self.scan.entry},
            configuration={
                "sample_file": self._sample_file,
                "entry": self._sample_file_entry,
                "source_scans": source_scans,
                "file_proposal": self._proposal_file,
            },
            process_index=self.scan.pop_process_index(),
            overwrite=True,
        )

    def tearDown(self):
        for dir_ in (self.input_dir, self.output_dir):
            shutil.rmtree(dir_)

    def testDataTransfert(self):
        process = ScanTransfer(
            inputs={
                "data": self.scan,
                "dest_dir": os.path.join(self.output_dir, "sample"),
                "block": True,
            }
        )
        process.run()
        output_scan = os.path.join(
            self.output_dir, "sample", "sample_29042021", "scan0002"
        )
        input_scan = os.path.join(
            self.input_dir, "sample", "sample_29042021", "scan0002"
        )
        output_proposal_file = os.path.join(
            self.output_dir, "sample", "ihpayno_sample.h5"
        )
        output_sample_file = os.path.join(
            self.output_dir, "sample", "sample_29042021", "sample_29042021.h5"
        )
        self.assertTrue(os.path.exists(output_scan))
        self.assertEqual(len(os.listdir(output_scan)), len(os.listdir(input_scan)))
        self.assertTrue(os.path.exists(output_proposal_file))
        self.assertTrue(os.path.exists(output_sample_file))
        other_scan_dir = os.path.join(self.output_dir, "sample", "scan0004")
        self.assertFalse(os.path.exists(other_scan_dir))


class TestPreTransfert(unittest.TestCase):
    """Test the pretransfert actions"""

    def setUp(self):
        unittest.TestCase.setUp(self)
        folderDataset = UtilsTest.getEDFDataset("scan_3_")
        self.tmpdir = tempfile.mkdtemp()
        self.outputfolder = tempfile.mkdtemp()
        scan_path = os.path.join(self.tmpdir, "scan_3_")
        shutil.copytree(src=folderDataset, dst=scan_path)
        self.scan = ScanFactory.create_scan_object(scan_path=scan_path)

    def tearDown(self):
        unittest.TestCase.tearDown(self)
        for _dir in (self.tmpdir, self.outputfolder):
            if os.path.exists(_dir):
                shutil.rmtree(_dir)

    def testParFile(self):
        """Make sure the scan_3_.par is correctly updated when moved"""
        parDict = self.getParDict(os.path.join(self.scan.path, "scan_3_.par"))
        assert (
            parDict["file_prefix"]
            == "/data/visitor/mi1226/id19/nemoz/henri/scan_3_/scan_3_"
        )
        assert (
            parDict["ff_prefix"]
            == "/data/visitor/mi1226/id19/nemoz/henri/scan_3_/refHST"
        )
        rebaseParFile(
            os.path.join(self.scan.path, "scan_3_.par"),
            oldfolder="/data/visitor/mi1226/id19/nemoz/henri/scan_3_",
            newfolder=self.scan.path,
        )
        parDict = self.getParDict(os.path.join(self.scan.path, "scan_3_.par"))
        self.assertTrue(
            parDict["file_prefix"] == os.path.join(self.scan.path, "scan_3_")
        )
        self.assertTrue(parDict["ff_prefix"] == os.path.join(self.scan.path, "refHST"))
        folderTrans = ScanTransfer(
            inputs={
                "turn_off_print": True,
                "copying": False,
                "dest_dir": self.outputfolder,
                "data": self.scan,
                "move": False,
                "force": True,
                "noRsync": True,
            }
        )

        folderTrans.run()
        parDict = self.getParDict(
            os.path.join(self.outputfolder, "scan_3_", "scan_3_.par")
        )
        self.assertTrue(
            parDict["file_prefix"]
            == os.path.join(self.outputfolder, "scan_3_", "scan_3_")
        )
        self.assertTrue(
            parDict["ff_prefix"] == os.path.join(self.outputfolder, "scan_3_", "refHST")
        )

    @staticmethod
    def getParDict(_file):
        assert os.path.isfile(_file)
        ddict = {}
        f = open(_file, "r")
        lines = f.readlines()
        for line in lines:
            if "=" not in line:
                continue
            line_str = line.rstrip().replace(" ", "")
            line_str = line_str.split("#")[0]
            key, value = line_str.split("=")
            ddict[key.lower()] = value
        return ddict
