# Copyright (C) 2022 - 2026 ANSYS, Inc. and/or its affiliates.
# SPDX-License-Identifier: MIT
#
#
# 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.


# THIS MODULE CONTAINS THE CLASSES FOR TEST HARNESS AND TEST MODULE
# THIS IS NOT YET FULLY IMPLEMENTED

from typing import Union, List, Optional, Callable, cast
import ansys.scadeone.core.swan.common as common
from ansys.scadeone.core.swan.variable import VarDecl
from ansys.scadeone.core.swan.typedecl import Uint64Type, BoolType
from ansys.scadeone.core.swan.modules import Module, UseDirective, GlobalDeclaration
from ansys.scadeone.core.swan.scopes import Scope
from ansys.scadeone.core.swan.diagram import DiagramObject
from ansys.scadeone.core.svc.swan_creator.module_creator import TestModuleCreator
from ansys.scadeone.core.svc.swan_creator.harness_creator import TestHarnessDiagramCreator
from ansys.scadeone.core.swan.operators import OperatorDefinition
from ansys.scadeone.core.swan.instances import OperatorExpression


class DataSource(OperatorExpression):  # numpydoc ignore=PR01
    """Class representing a source in the harness."""

    def __init__(self, id: common.Identifier) -> None:
        super().__init__()
        self._id = id

    @property
    def id(self) -> common.Identifier:
        """DataSource key as identifier."""
        return self._id


class Oracle(OperatorExpression):  # numpydoc ignore=PR01
    """Class representing an oracle in the harness."""

    def __init__(self, id: common.Identifier) -> None:
        super().__init__()
        self._id = id

    @property
    def id(self) -> common.Identifier:
        """Oracle key as identifier."""
        return self._id


class SetSensorEquation(common.Equation):  # numpydoc ignore=PR01
    """Class representing a set sensor equation in the harness."""

    def __init__(self, sensor: common.PathIdentifier, value: common.Expression) -> None:
        super().__init__()
        self._sensor = sensor
        self._value = value

    @property
    def sensor(self) -> common.PathIdentifier:
        """Sensor identifier."""
        return self._sensor

    @property
    def value(self) -> common.Expression:
        """Value to set."""
        return self._value


class SetSensorBlock(DiagramObject):  # numpydoc ignore=PR01
    """Class representing a set sensor block in the harness."""

    def __init__(
        self,
        sensor: Union[common.PathIdentifier, common.ProtectedItem],
        lunum: Optional[common.Lunum] = None,
        luid: Optional[common.Luid] = None,
        locals: Optional[List[DiagramObject]] = None,
        pragmas: Optional[List[common.Pragma]] = None,
    ) -> None:
        super().__init__(lunum, luid, locals, pragmas)
        self._sensor = sensor

    @property
    def sensor(self) -> Union[common.PathIdentifier, common.ProtectedItem]:
        """Sensor identifier."""
        return self._sensor

    @property
    def is_protected(self) -> bool:
        """True when sensor is syntactically incorrect and protected."""
        return isinstance(self.sensor, common.ProtectedItem)


class TestHarness(
    common.Declaration, common.ModuleItem, TestHarnessDiagramCreator
):  # numpydoc ignore=PR01
    """Test harness definition."""

    def __init__(
        self,
        id: common.Identifier,
        body: Optional[Union[Scope, common.Equation, Callable]],
        pragmas: Optional[List[common.Pragma]] = None,
    ) -> None:
        common.Declaration.__init__(self, id, pragmas)
        self._body = body
        self._inputs = [VarDecl(common.Identifier("_current_cycle"), type=Uint64Type())]
        self._outputs = [VarDecl(common.Identifier("_stop_condition"), type=BoolType())]

    @property
    def body(self) -> Optional[Union[Scope, common.Equation]]:
        """Harness body: a scope, an equation, or None."""
        if isinstance(self._body, Callable):
            body = self._body(self)
            self._body = body
            self.set_owner(self, self._body)
        return self._body


class TestModule(Module, TestModuleCreator):  # numpydoc ignore=PR01
    """Test module definition."""

    __test__ = False  # Disable pytest discovery for this class

    def __init__(
        self,
        name: common.PathIdentifier,
        use_directives: Optional[List[UseDirective]] = None,
        declarations: Optional[List[GlobalDeclaration]] = None,
        pragmas: Optional[List[common.Pragma]] = None,
    ) -> None:
        Module.__init__(self, name, use_directives, declarations, pragmas)

    @property
    def extension(self) -> str:
        """Returns module extension, with '.' included."""
        return ".swant"

    @property
    def operator_definitions(self) -> List[OperatorDefinition]:
        """Return a list of operator definitions."""
        return [
            cast(OperatorDefinition, decl)
            for decl in self.filter_declarations(lambda x: isinstance(x, OperatorDefinition))
            if decl
        ]

    @property
    def test_harnesses(self) -> List[TestHarness]:
        """Return a list of test harness."""
        return [
            cast(TestHarness, decl)
            for decl in self.filter_declarations(lambda x: isinstance(x, TestHarness))
            if decl
        ]
