# General

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec

# Q-CTRL Open Controls
from qctrlopencontrols import new_quadratic_sequence, new_ramsey_sequence

# Q-CTRL Qiskit Adapter
from qctrlqiskit import convert_dds_to_qiskit_quantum_circuit

# Q-CTRL Visualizer
from qctrlvisualizer import plot_sequences

# Qiskit
##To define a backend (simulated or real), transpile and assemble a circuit
from qiskit import BasicAer
from qiskit.compiler import transpile, assemble

##To plot the outcome
from qiskit.tools.visualization import plot_histogram

## To handle account information,find a suitable device and monitor a job
from qiskit import IBMQ
from qiskit.providers.ibmq import least_busy
from qiskit.tools.monitor import job_monitor

## Quadratic sequence, total duration: 20us
quadratic_sequence = new_quadratic_sequence(
    duration=20e-6,
    inner_offset_count=2,
    outer_offset_count=2,
    pre_post_rotation=True,
    name="Quadratic sequence",
)

# Ramsey sequence, total duration: 20us
ramsey_sequence = new_ramsey_sequence(
    duration=20e-6, pre_post_rotation=True, name="Ramsey sequence"
)

print(quadratic_sequence)
print(ramsey_sequence)

## Prepare the Qiskit related parameters
"""
target_qubits : list
    A list of integers specifying the target qubits within the set of qubit registers
"""
target_qubits = [0]

"""
gate_time : float
    Time delay (in seconds) introduced by identity gate
"""
gate_time = 0.4e-6

"""
add_measurement : bool
    Indicates if the circuit requires a measurement step.
    Required for 'qasm_simulator' and real device backends
"""
add_measurement = True

"""
circuit_name : str
    An optional string as a name to the circuit
"""
circuit_name = "quadratic-sequence-circuit"

## convert the quadratic sequence to QuantumCircuit

quadratic_quantum_circuit = convert_dds_to_qiskit_quantum_circuit(
    dynamic_decoupling_sequence=quadratic_sequence,
    target_qubits=target_qubits,
    gate_time=gate_time,
    add_measurement=add_measurement,
    circuit_name=circuit_name,
)

## convert the ramsey sequence to QuantumCircuit
circuit_name = "ramsey-sequence-circuit"
ramsey_quantum_circuit = convert_dds_to_qiskit_quantum_circuit(
    dynamic_decoupling_sequence=ramsey_sequence,
    target_qubits=target_qubits,
    gate_time=gate_time,
    add_measurement=add_measurement,
    circuit_name=circuit_name,
)

formatted_plot_data = quadratic_sequence.export()
figure = plt.figure()
plt.suptitle("Quadratic Sequence")
plot_sequences(figure, formatted_plot_data)

formatted_plot_data = ramsey_sequence.export()
figure = plt.figure()
plt.suptitle("Ramsey Sequence")
plot_sequences(figure, formatted_plot_data)

##Drawing the Quadratic Circuit
quadratic_quantum_circuit.draw()

ramsey_quantum_circuit.draw()

print(ramsey_quantum_circuit.count_ops()["id"])

## Prepares the simulator backend
"""
backend : str
    One of 'unitary_simulator', 'statevector_simulator' or 'qasm_simulator';
    defaults to 'qasm_simulator'
"""
backend = "qasm_simulator"
backend_simulator = BasicAer.get_backend(backend)

"""
number_of_shots : int
    Number of repeats the experiment has to be carried out;
    defaults to 1
"""
number_of_shots = 1024

## Run the quadratic sequence circuit, Get the result (counts of state |1> and |0>), plot the histogram

# Transpile and assemble the circuits so that the identity gates are preserved
transpiled_quadratic_circuit = transpile(
    quadratic_quantum_circuit, backend=backend_simulator, optimization_level=0
)
assembled_quadratic_circuit = assemble(
    transpiled_quadratic_circuit, shots=number_of_shots
)

# run the circuit
job = backend_simulator.run(assembled_quadratic_circuit)
result = job.result()
counts = result.get_counts(quadratic_quantum_circuit)
plot_histogram(counts)

## Run the ramsey sequence circuit, Get the result (counts of state |1> and |0>), plot the histogram

# Transpile and assemble the circuits so that the identity gates are preserved
transpiled_ramsey_circuit = transpile(
    ramsey_quantum_circuit, backend=backend_simulator, optimization_level=0
)
assembled_ramsey_circuit = assemble(transpiled_ramsey_circuit, shots=number_of_shots)

job = backend_simulator.run(assembled_ramsey_circuit)
result = job.result()
counts = result.get_counts(ramsey_quantum_circuit)
plot_histogram(counts)

# NBVAL_SKIP
"""
account_token : str
    Token to enable IBM Q device access
"""
account_token = "insert-your-ibmq-token-here"
IBMQ.enable_account(account_token)
provider = IBMQ.get_provider(hub="ibm-q", group="open")
backends = provider.backends()
backends = [backend for backend in backends if backend.name() != "ibmq_qasm_simulator"]

backend = least_busy(backends)
print("Least busy device is {}".format(backend.name()))

# NBVAL_SKIP

job = backend.run(assembled_quadratic_circuit)
job_monitor(job)
result = job.result()
counts = result.get_counts(quadratic_quantum_circuit)
plot_histogram(counts)

# NBVAL_SKIP

job = backend.run(assembled_ramsey_circuit)
job_monitor(job)
result = job.result()
counts = result.get_counts(ramsey_quantum_circuit)
plot_histogram(counts)

# NBVAL_SKIP
IBMQ.disable_account()