#
#    ICRAR - International Centre for Radio Astronomy Research
#    (c) UWA - The University of Western Australia, 2014
#    Copyright by UWA (in the framework of the ICRAR)
#    All rights reserved
#
#    This library is free software; you can redistribute it and/or
#    modify it under the terms of the GNU Lesser General Public
#    License as published by the Free Software Foundation; either
#    version 2.1 of the License, or (at your option) any later version.
#
#    This library is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#    Lesser General Public License for more details.
#
#    You should have received a copy of the GNU Lesser General Public
#    License along with this library; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
#    MA 02111-1307  USA
#
import os
import tempfile
import unittest

from dlg.drop import AbstractDROP, FileDROP
from dlg.environmentvar_drop import EnvironmentVarDROP
from dlg.utils import getDlgDir


def create_std_env_vars(name="env_vars"):
    return EnvironmentVarDROP(
        oid="a",
        uid="a",
        nm=name,
        dir_var="/HOME/",
        int_var=3,
        bool_var=False,
        float_var=0.5,
        dict_var={"first": 1, "second": "sec"},
        list_var=[1, 2.0, "3"],
    )


def create_empty_env_vars(name="env_vars"):
    return EnvironmentVarDROP(oid="b", uid="b", nm=name)


class TestEnvironmentVarDROP(unittest.TestCase):
    def test_get(self):
        """
        Tests that environment variables are read in and fetched correctly.
        """
        env_drop = create_std_env_vars()
        self.assertEqual("/HOME/", env_drop.get("dir_var"))
        self.assertEqual(3, env_drop.get("int_var"))
        self.assertEqual(False, env_drop.get("bool_var"))
        self.assertEqual(0.5, env_drop.get("float_var"))
        self.assertEqual({"first": 1, "second": "sec"}, env_drop.get("dict_var"))
        self.assertEqual([1, 2.0, "3"], env_drop.get("list_var"))
        self.assertIsNone(env_drop.get("non_var"))
        self.assertIsNone(env_drop.get("uid"))

    def test_get_empty(self):
        """
        Tests that an empty environment drop contains no environment variables.
        """
        env_drop = create_empty_env_vars()
        self.assertEqual(dict(), env_drop._variables)

    def test_get_multiple(self):
        """
        Tests the get_multiple routine for environment variables is correct
        """
        env_drop = create_std_env_vars()
        expected_vars = [
            None,
            "/HOME/",
            3,
            False,
            0.5,
            {"first": 1, "second": "sec"},
            [1, 2.0, "3"],
            None,
        ]
        query_keys = [
            "uid",
            "dir_var",
            "int_var",
            "bool_var",
            "float_var",
            "dict_var",
            "list_var",
            "non_var",
        ]
        self.assertEqual(expected_vars, env_drop.get_multiple(query_keys))

    def test_set(self):
        """
        Should currently raise un-implemented, but here for completeness
        """
        env_drop = create_std_env_vars()
        self.assertRaises(NotImplementedError, env_drop.set, "var", "val")

    def test_drop_get_single(self):
        """
        Tests the AbstractDROP fetch routine functions correctly with a single environment drop
        """
        env_drop = create_std_env_vars()
        test_drop = AbstractDROP(uid="b", oid="b")
        test_drop.addProducer(env_drop)
        self.assertEqual(
            "/HOME/", test_drop.get_environment_variable("$env_vars.dir_var")
        )
        self.assertEqual(3, test_drop.get_environment_variable("$env_vars.int_var"))
        self.assertEqual(
            False, test_drop.get_environment_variable("$env_vars.bool_var")
        )
        self.assertEqual(0.5, test_drop.get_environment_variable("$env_vars.float_var"))
        self.assertEqual(
            {"first": 1, "second": "sec"},
            test_drop.get_environment_variable("$env_vars.dict_var"),
        )
        self.assertEqual(
            [1, 2.0, "3"], test_drop.get_environment_variable("$env_vars.list_var")
        )
        self.assertEqual(
            "$env_vars.non_var", test_drop.get_environment_variable("$env_vars.non_var")
        )
        self.assertEqual(
            "$env_vars.uid", test_drop.get_environment_variable("$env_vars.uid")
        )

    def test_drop_get_multiple(self):
        """
        Tests the AbstractDROP multiple fetch routine functions correctly with a single environment
        drop
        """
        env_name = "env_vars"
        env_drop = create_std_env_vars(name=env_name)
        test_drop = AbstractDROP(uid="b", oid="b")
        test_drop.addProducer(env_drop)
        expected_vars = [
            f"${env_name}.uid",
            "/HOME/",
            3,
            False,
            0.5,
            {"first": 1, "second": "sec"},
            [1, 2.0, "3"],
            f"${env_name}.non_var",
        ]
        query_keys = [
            "uid",
            "dir_var",
            "int_var",
            "bool_var",
            "float_var",
            "dict_var",
            "list_var",
            "non_var",
        ]
        query_keys = [
            f"${env_name}.{x}" for x in query_keys
        ]  # Build queries of the correct form
        # Add some purposefully malformed vars
        extra_keys = ["dir_var", "$non_store.non_var"]
        query_keys.extend(extra_keys)
        expected_vars.extend(extra_keys)
        self.assertEqual(expected_vars, test_drop.get_environment_variables(query_keys))

    def test_drop_get_empty(self):
        """
        Tests the case where the environment drop has no name
        """
        env_name = ""
        env_drop = create_empty_env_vars(name=env_name)
        test_drop = AbstractDROP(uid="c", oid="c")
        test_drop.addProducer(env_drop)
        self.assertEqual("", test_drop.get_environment_variable(""))
        self.assertEqual("$", test_drop.get_environment_variable("$"))

    def test_drop_get_multiEnv(self):
        """
        Tests the AbstractDROP fetch routine with multiple environment drops
        """
        env1_name = "env_vars"
        env2_name = "more_vars"
        env1_drop = create_std_env_vars(name=env1_name)
        env2_drop = EnvironmentVarDROP(
            oid="d", uid="d", nm=env2_name, dir_var="/DIFFERENT/", int_var=4
        )
        test_drop = AbstractDROP(uid="c", oid="c")
        test_drop.addProducer(env1_drop)
        test_drop.addProducer(env2_drop)
        self.assertEqual(
            "/HOME/", test_drop.get_environment_variable(f"${env1_name}.dir_var")
        )
        self.assertEqual(
            "/DIFFERENT/", test_drop.get_environment_variable(f"${env2_name}.dir_var")
        )
        self.assertEqual(3, test_drop.get_environment_variable(f"${env1_name}.int_var"))
        self.assertEqual(4, test_drop.get_environment_variable(f"${env2_name}.int_var"))
        self.assertEqual(
            f"{env1_name}.int_var",
            test_drop.get_environment_variable(f"{env1_name}.int_var"),
        )
        self.assertEqual(f".int_var", test_drop.get_environment_variable(f".int_var"))
        self.assertEqual(
            f"$third_env.int_var",
            test_drop.get_environment_variable(f"$third_env.int_var"),
        )
        self.assertEqual(
            ["/HOME/", "/DIFFERENT/", 3, 4, f"${env1_name}.non_var", "$fake.var"],
            test_drop.get_environment_variables(
                [
                    f"${env1_name}.dir_var",
                    f"${env2_name}.dir_var",
                    f"${env1_name}.int_var",
                    f"${env2_name}.int_var",
                    f"${env1_name}.non_var",
                    "$fake.var",
                ]
            ),
        )

    def test_autofill_environment_vars(self):
        """
        Tests the autofilling functionality of AbstractDROP
        """
        env_drop = create_std_env_vars(name="env_vars")
        test_drop = AbstractDROP(
            oid="a",
            uid="a",
            dir_var="$env_vars.dir_var",
            int_var="$env_vars.int_var",
            non_var=set(),
        )
        test_drop.addProducer(env_drop)
        test_drop.autofill_environment_variables()
        self.assertEqual("/HOME/", test_drop.parameters["dir_var"])
        self.assertEqual(3, test_drop.parameters["int_var"])

    def test_get_dlg_vars(self):
        test_drop = AbstractDROP(
            oid="a",
            uid="a",
            dlg_root="$DLG_ROOT",
            non_dlg_var="$DLG_NONEXISTS",
            non_var=set(),
        )
        test_drop.autofill_environment_variables()
        self.assertEqual(getDlgDir(), test_drop.parameters["dlg_root"])
        self.assertEqual(getDlgDir(), test_drop.get_environment_variable("$DLG_ROOT"))
        self.assertEqual("$DLG_NONEXISTS", test_drop.parameters["non_dlg_var"])
        self.assertEqual(
            "$DLG_NONEXISTS", test_drop.get_environment_variable("$DLG_NONEXISTS")
        )

    def test_filename_integration(self):
        with tempfile.TemporaryDirectory() as tmp_dir:
            os.environ["DLG_ROOT"] = tmp_dir
            os.environ["DLG_FILE"] = "test_file"
            test_drop = FileDROP(
                oid="a", uid="a", filepath="$DLG_FILE", dirname="$DLG_ROOT"
            )
            test_drop.write(b"1234")
            self.assertEqual(tmp_dir, test_drop.dirname)
            self.assertEqual("test_file", test_drop.filepath)
