import asyncio
import logging
import tempfile
from multiprocessing import Process, Queue
from pathlib import Path

from bluesky import RunEngine
from bluesky.callbacks import LiveTable
from bluesky.callbacks.zmq import Publisher
from bluesky.plan_stubs import sleep
from bluesky.plans import scan
from bluesky.preprocessors import SupplementalData
from event_model import RunRouter
from ophyd.sim import det, motor, motor1, motor2
from suitcase.jsonl import Serializer

log = logging.getLogger(__name__)


def run_proxy(queue):
    """
    Run Proxy on random, free ports and communicate the port numbers back.
    """
    from bluesky.callbacks.zmq import Proxy

    proxy = Proxy()
    queue.put((proxy.in_port, proxy.out_port))
    proxy.start()


def run_publisher(in_port, data_path, quiet=False):
    """
    Acquire data in an infinite loop and publish it.
    """
    publisher = Publisher(f"localhost:{in_port}")
    RE = RunEngine(loop=asyncio.new_event_loop())
    sd = SupplementalData()
    RE.preprocessors.append(sd)
    sd.baseline.extend([motor1, motor2])
    RE.subscribe(publisher)

    def factory(name, doc):
        serializer = Serializer(data_path / "abc", flush=True)
        return [serializer], []

    rr = RunRouter([factory])
    RE.subscribe(rr)
    if not quiet:
        RE.subscribe(LiveTable(["motor", "det"]))

    motor.delay = 0.2
    det.kind = "hinted"

    def infinite_plan():
        while True:
            for i in range(1, 5):
                yield from sleep(2)
                yield from scan([det], motor, -1, 1, 5 * i)

    # Just as a convenience, avoid collission with scan_ids of runs in Catalog.
    RE.md["scan_id"] = 100
    try:
        RE(infinite_plan())
    finally:
        RE.halt()


def stream_example_data(data_path, quiet=False):
    data_path = Path(data_path)
    log.info(
        f"Writing example data into directory {data_path!s}. It will be deleted when this process is stopped."
    )

    queue = Queue()
    proxy_process = Process(target=run_proxy, args=(queue,))
    proxy_process.start()
    in_port, out_port = queue.get()
    log.info(f"Connect a consumer to localhost:{out_port}")

    publisher_process = Process(target=run_publisher, args=(in_port, data_path, quiet))
    publisher_process.start()
    log.info("Demo acquisition has started.")

    return f"localhost:{out_port}", proxy_process, publisher_process


if __name__ == "__main__":
    import logging
    import sys

    quiet = (len(sys.argv) > 1) and (sys.argv[1] in ("--quiet", "-q"))

    handler = logging.StreamHandler()
    handler.setLevel("INFO")
    log.setLevel("DEBUG")
    log.addHandler(handler)
    with tempfile.TemporaryDirectory() as directory:
        stream_example_data(directory, quiet)
    # Delete example data at exit.
