# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
#     http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.

import math
from typing import Any, Dict, Optional

from oqpy import FrameVar as OQFrame
from oqpy.base import OQPyExpression

from braket.pulse.port import Port


class Frame:
    """
    Frame tracks the frame of reference, when interacting with the qubits, throughout the execution
    of a program. See https://openqasm.com/language/openpulse.html#frames for more details.
    """

    def __init__(
        self,
        frame_id: str,
        port: Port,
        frequency: float,
        phase: float = 0,
        is_predefined: bool = False,
        properties: Optional[Dict[str, Any]] = None,
    ):
        """
        Args:
            frame_id (str): str identifying a unique frame.
            port (Port): port that this frame is attached to.
            frequency (float): frequency to which this frame should be initialized.
            phase (float): phase to which this frame should be initialized. Defaults to 0.
            is_predefined (bool): bool indicating whether this is a predefined frame on
                the device. Defaults to False.
            properties (Optional[Dict[str, Any]]): Dict containing properties of this frame.
                Defaults to None.
        """
        self._frame_id = frame_id
        self.port = port
        self.frequency = frequency
        self.phase = phase
        self.is_predefined = is_predefined
        self.properties = properties

    @property
    def id(self) -> str:
        """Returns a str indicating the frame id."""
        return self._frame_id

    def __eq__(self, other) -> bool:
        return (
            (
                (self.id == other.id)
                and (self.port == other.port)
                and math.isclose(self.frequency, other.frequency)
                and math.isclose(self.phase, other.phase)
            )
            if isinstance(other, Frame)
            else False
        )

    def _to_oqpy_expression(self) -> OQPyExpression:
        return OQFrame(
            port=self.port._to_oqpy_expression(),
            frequency=self.frequency,
            phase=self.phase,
            name=self.id,
            needs_declaration=not self.is_predefined,
        )
