# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2018.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.


"""Qobj tests."""

import copy

from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
from qiskit.compiler import assemble
from qiskit.qobj import (
    QasmQobj,
    PulseQobj,
    QobjHeader,
    PulseQobjInstruction,
    PulseQobjExperiment,
    PulseQobjConfig,
    QobjMeasurementOption,
    PulseLibraryItem,
    QasmQobjInstruction,
    QasmQobjExperiment,
    QasmQobjConfig,
    QasmExperimentCalibrations,
    GateCalibration,
)

from test import QiskitTestCase  # pylint: disable=wrong-import-order


class TestQASMQobj(QiskitTestCase):
    """Tests for QasmQobj."""

    def setUp(self):
        super().setUp()
        with self.assertWarns(DeprecationWarning):
            self.valid_qobj = QasmQobj(
                qobj_id="12345",
                header=QobjHeader(),
                config=QasmQobjConfig(shots=1024, memory_slots=2),
                experiments=[
                    QasmQobjExperiment(
                        instructions=[
                            QasmQobjInstruction(name="u1", qubits=[1], params=[0.4]),
                            QasmQobjInstruction(name="u2", qubits=[1], params=[0.4, 0.2]),
                        ]
                    )
                ],
            )

        self.valid_dict = {
            "qobj_id": "12345",
            "type": "QASM",
            "schema_version": "1.2.0",
            "header": {},
            "config": {"memory_slots": 2, "shots": 1024},
            "experiments": [
                {
                    "instructions": [
                        {"name": "u1", "params": [0.4], "qubits": [1]},
                        {"name": "u2", "params": [0.4, 0.2], "qubits": [1]},
                    ]
                }
            ],
        }
        with self.assertWarns(DeprecationWarning):
            self.bad_qobj = copy.deepcopy(self.valid_qobj)
        self.bad_qobj.experiments = []

    def test_from_dict_per_class(self):
        """Test Qobj and its subclass representations given a dictionary."""
        with self.assertWarns(DeprecationWarning):
            test_parameters = {
                QasmQobj: (self.valid_qobj, self.valid_dict),
                QasmQobjConfig: (
                    QasmQobjConfig(shots=1, memory_slots=2),
                    {"shots": 1, "memory_slots": 2},
                ),
                QasmQobjExperiment: (
                    QasmQobjExperiment(
                        instructions=[QasmQobjInstruction(name="u1", qubits=[1], params=[0.4])]
                    ),
                    {"instructions": [{"name": "u1", "qubits": [1], "params": [0.4]}]},
                ),
                QasmQobjInstruction: (
                    QasmQobjInstruction(name="u1", qubits=[1], params=[0.4]),
                    {"name": "u1", "qubits": [1], "params": [0.4]},
                ),
            }

        for qobj_class, (qobj_item, expected_dict) in test_parameters.items():
            with self.subTest(msg=str(qobj_class)):
                with self.assertWarns(DeprecationWarning):
                    qobj = qobj_class.from_dict(expected_dict)
                self.assertEqual(qobj_item, qobj)

    def test_snapshot_instruction_to_dict(self):
        """Test snapshot instruction to dict."""
        with self.assertWarns(DeprecationWarning):
            valid_qobj = QasmQobj(
                qobj_id="12345",
                header=QobjHeader(),
                config=QasmQobjConfig(shots=1024, memory_slots=2),
                experiments=[
                    QasmQobjExperiment(
                        instructions=[
                            QasmQobjInstruction(name="u1", qubits=[1], params=[0.4]),
                            QasmQobjInstruction(name="u2", qubits=[1], params=[0.4, 0.2]),
                            QasmQobjInstruction(
                                name="snapshot",
                                qubits=[1],
                                snapshot_type="statevector",
                                label="my_snap",
                            ),
                        ]
                    )
                ],
            )
        res = valid_qobj.to_dict()
        expected_dict = {
            "qobj_id": "12345",
            "type": "QASM",
            "schema_version": "1.3.0",
            "header": {},
            "config": {"memory_slots": 2, "shots": 1024},
            "experiments": [
                {
                    "instructions": [
                        {"name": "u1", "params": [0.4], "qubits": [1]},
                        {"name": "u2", "params": [0.4, 0.2], "qubits": [1]},
                        {
                            "name": "snapshot",
                            "qubits": [1],
                            "snapshot_type": "statevector",
                            "label": "my_snap",
                        },
                    ],
                    "config": {},
                    "header": {},
                }
            ],
        }
        self.assertEqual(expected_dict, res)

    def test_snapshot_instruction_from_dict(self):
        """Test snapshot instruction from dict."""
        with self.assertWarns(DeprecationWarning):
            expected_qobj = QasmQobj(
                qobj_id="12345",
                header=QobjHeader(),
                config=QasmQobjConfig(shots=1024, memory_slots=2),
                experiments=[
                    QasmQobjExperiment(
                        instructions=[
                            QasmQobjInstruction(name="u1", qubits=[1], params=[0.4]),
                            QasmQobjInstruction(name="u2", qubits=[1], params=[0.4, 0.2]),
                            QasmQobjInstruction(
                                name="snapshot",
                                qubits=[1],
                                snapshot_type="statevector",
                                label="my_snap",
                            ),
                        ]
                    )
                ],
            )
        qobj_dict = {
            "qobj_id": "12345",
            "type": "QASM",
            "schema_version": "1.2.0",
            "header": {},
            "config": {"memory_slots": 2, "shots": 1024},
            "experiments": [
                {
                    "instructions": [
                        {"name": "u1", "params": [0.4], "qubits": [1]},
                        {"name": "u2", "params": [0.4, 0.2], "qubits": [1]},
                        {
                            "name": "snapshot",
                            "qubits": [1],
                            "snapshot_type": "statevector",
                            "label": "my_snap",
                        },
                    ]
                }
            ],
        }
        with self.assertWarns(DeprecationWarning):
            qobj = QasmQobj.from_dict(qobj_dict)
        self.assertEqual(expected_qobj, qobj)

    def test_change_qobj_after_compile(self):
        """Test modifying Qobj parameters after compile."""
        qr = QuantumRegister(3)
        cr = ClassicalRegister(3)
        qc1 = QuantumCircuit(qr, cr)
        qc2 = QuantumCircuit(qr, cr)
        qc1.h(qr[0])
        qc1.cx(qr[0], qr[1])
        qc1.cx(qr[0], qr[2])
        qc2.h(qr)
        qc1.measure(qr, cr)
        qc2.measure(qr, cr)
        circuits = [qc1, qc2]
        with self.assertWarns(DeprecationWarning):
            qobj1 = assemble(circuits, shots=1024, seed=88)
        qobj1.experiments[0].config.shots = 50
        qobj1.experiments[1].config.shots = 1
        self.assertTrue(qobj1.experiments[0].config.shots == 50)
        self.assertTrue(qobj1.experiments[1].config.shots == 1)
        self.assertTrue(qobj1.config.shots == 1024)

    def test_gate_calibrations_to_dict(self):
        """Test gate calibrations to dict."""
        with self.assertWarns(DeprecationWarning):
            pulse_library = [PulseLibraryItem(name="test", samples=[1j, 1j])]
        with self.assertWarns(DeprecationWarning):
            valid_qobj = QasmQobj(
                qobj_id="12345",
                header=QobjHeader(),
                config=QasmQobjConfig(shots=1024, memory_slots=2, pulse_library=pulse_library),
                experiments=[
                    QasmQobjExperiment(
                        instructions=[QasmQobjInstruction(name="u1", qubits=[1], params=[0.4])],
                        config=QasmQobjConfig(
                            calibrations=QasmExperimentCalibrations(
                                gates=[
                                    GateCalibration(
                                        name="u1", qubits=[1], params=[0.4], instructions=[]
                                    )
                                ]
                            )
                        ),
                    )
                ],
            )
        res = valid_qobj.to_dict()
        expected_dict = {
            "qobj_id": "12345",
            "type": "QASM",
            "schema_version": "1.3.0",
            "header": {},
            "config": {
                "memory_slots": 2,
                "shots": 1024,
                "pulse_library": [{"name": "test", "samples": [1j, 1j]}],
            },
            "experiments": [
                {
                    "instructions": [{"name": "u1", "params": [0.4], "qubits": [1]}],
                    "config": {
                        "calibrations": {
                            "gates": [
                                {"name": "u1", "qubits": [1], "params": [0.4], "instructions": []}
                            ]
                        }
                    },
                    "header": {},
                }
            ],
        }
        self.assertEqual(expected_dict, res)


class TestPulseQobj(QiskitTestCase):
    """Tests for PulseQobj."""

    def setUp(self):
        super().setUp()
        with self.assertWarns(DeprecationWarning):
            self.valid_qobj = PulseQobj(
                qobj_id="12345",
                header=QobjHeader(),
                config=PulseQobjConfig(
                    shots=1024,
                    memory_slots=2,
                    meas_level=1,
                    memory_slot_size=8192,
                    meas_return="avg",
                    pulse_library=[
                        PulseLibraryItem(
                            name="pulse0", samples=[0.0 + 0.0j, 0.5 + 0.0j, 0.0 + 0.0j]
                        )
                    ],
                    qubit_lo_freq=[4.9],
                    meas_lo_freq=[6.9],
                    rep_time=1000,
                ),
                experiments=[
                    PulseQobjExperiment(
                        instructions=[
                            PulseQobjInstruction(name="pulse0", t0=0, ch="d0"),
                            PulseQobjInstruction(name="fc", t0=5, ch="d0", phase=1.57),
                            PulseQobjInstruction(name="fc", t0=5, ch="d0", phase=0.0),
                            PulseQobjInstruction(name="fc", t0=5, ch="d0", phase="P1"),
                            PulseQobjInstruction(name="setp", t0=10, ch="d0", phase=3.14),
                            PulseQobjInstruction(name="setf", t0=10, ch="d0", frequency=8.0),
                            PulseQobjInstruction(name="shiftf", t0=10, ch="d0", frequency=4.0),
                            PulseQobjInstruction(
                                name="acquire",
                                t0=15,
                                duration=5,
                                qubits=[0],
                                memory_slot=[0],
                                kernels=[
                                    QobjMeasurementOption(
                                        name="boxcar", params={"start_window": 0, "stop_window": 5}
                                    )
                                ],
                            ),
                        ]
                    )
                ],
            )
        self.valid_dict = {
            "qobj_id": "12345",
            "type": "PULSE",
            "schema_version": "1.2.0",
            "header": {},
            "config": {
                "memory_slots": 2,
                "shots": 1024,
                "meas_level": 1,
                "memory_slot_size": 8192,
                "meas_return": "avg",
                "pulse_library": [{"name": "pulse0", "samples": [0, 0.5, 0]}],
                "qubit_lo_freq": [4.9],
                "meas_lo_freq": [6.9],
                "rep_time": 1000,
            },
            "experiments": [
                {
                    "instructions": [
                        {"name": "pulse0", "t0": 0, "ch": "d0"},
                        {"name": "fc", "t0": 5, "ch": "d0", "phase": 1.57},
                        {"name": "fc", "t0": 5, "ch": "d0", "phase": 0},
                        {"name": "fc", "t0": 5, "ch": "d0", "phase": "P1"},
                        {"name": "setp", "t0": 10, "ch": "d0", "phase": 3.14},
                        {"name": "setf", "t0": 10, "ch": "d0", "frequency": 8.0},
                        {"name": "shiftf", "t0": 10, "ch": "d0", "frequency": 4.0},
                        {
                            "name": "acquire",
                            "t0": 15,
                            "duration": 5,
                            "qubits": [0],
                            "memory_slot": [0],
                            "kernels": [
                                {"name": "boxcar", "params": {"start_window": 0, "stop_window": 5}}
                            ],
                        },
                    ]
                }
            ],
        }

    def test_from_dict_per_class(self):
        """Test converting to Qobj and its subclass representations given a dictionary."""
        with self.assertWarns(DeprecationWarning):
            test_parameters = {
                PulseQobj: (self.valid_qobj, self.valid_dict),
                PulseQobjConfig: (
                    PulseQobjConfig(
                        meas_level=1,
                        memory_slot_size=8192,
                        meas_return="avg",
                        pulse_library=[PulseLibraryItem(name="pulse0", samples=[0.1 + 0.0j])],
                        qubit_lo_freq=[4.9],
                        meas_lo_freq=[6.9],
                        rep_time=1000,
                    ),
                    {
                        "meas_level": 1,
                        "memory_slot_size": 8192,
                        "meas_return": "avg",
                        "pulse_library": [{"name": "pulse0", "samples": [0.1 + 0j]}],
                        "qubit_lo_freq": [4.9],
                        "meas_lo_freq": [6.9],
                        "rep_time": 1000,
                    },
                ),
                PulseLibraryItem: (
                    PulseLibraryItem(name="pulse0", samples=[0.1 + 0.0j]),
                    {"name": "pulse0", "samples": [0.1 + 0j]},
                ),
                PulseQobjExperiment: (
                    PulseQobjExperiment(
                        instructions=[PulseQobjInstruction(name="pulse0", t0=0, ch="d0")]
                    ),
                    {"instructions": [{"name": "pulse0", "t0": 0, "ch": "d0"}]},
                ),
                PulseQobjInstruction: (
                    PulseQobjInstruction(name="pulse0", t0=0, ch="d0"),
                    {"name": "pulse0", "t0": 0, "ch": "d0"},
                ),
            }

        for qobj_class, (qobj_item, expected_dict) in test_parameters.items():
            with self.subTest(msg=str(qobj_class)):
                with self.assertWarns(DeprecationWarning):
                    qobj = qobj_class.from_dict(expected_dict)
                self.assertEqual(qobj_item, qobj)

    def test_to_dict_per_class(self):
        """Test converting from Qobj and its subclass representations given a dictionary."""
        with self.assertWarns(DeprecationWarning):
            test_parameters = {
                PulseQobj: (self.valid_qobj, self.valid_dict),
                PulseQobjConfig: (
                    PulseQobjConfig(
                        meas_level=1,
                        memory_slot_size=8192,
                        meas_return="avg",
                        pulse_library=[PulseLibraryItem(name="pulse0", samples=[0.1 + 0.0j])],
                        qubit_lo_freq=[4.9],
                        meas_lo_freq=[6.9],
                        rep_time=1000,
                    ),
                    {
                        "meas_level": 1,
                        "memory_slot_size": 8192,
                        "meas_return": "avg",
                        "pulse_library": [{"name": "pulse0", "samples": [0.1 + 0j]}],
                        "qubit_lo_freq": [4.9],
                        "meas_lo_freq": [6.9],
                        "rep_time": 1000,
                    },
                ),
                PulseLibraryItem: (
                    PulseLibraryItem(name="pulse0", samples=[0.1 + 0.0j]),
                    {"name": "pulse0", "samples": [0.1 + 0j]},
                ),
                PulseQobjExperiment: (
                    PulseQobjExperiment(
                        instructions=[PulseQobjInstruction(name="pulse0", t0=0, ch="d0")]
                    ),
                    {"instructions": [{"name": "pulse0", "t0": 0, "ch": "d0"}]},
                ),
                PulseQobjInstruction: (
                    PulseQobjInstruction(name="pulse0", t0=0, ch="d0"),
                    {"name": "pulse0", "t0": 0, "ch": "d0"},
                ),
            }

        for qobj_class, (qobj_item, expected_dict) in test_parameters.items():
            with self.subTest(msg=str(qobj_class)):
                self.assertEqual(qobj_item.to_dict(), expected_dict)


def _nop():
    pass
