from owrx.reporting.reporter import Reporter
from owrx.version import openwebrx_version
from owrx.config import Config
from owrx.locator import Locator
from owrx.metrics import Metrics, CounterMetric
from queue import Queue, Full
from urllib import request, parse
import threading
import logging
from datetime import datetime, timezone

logger = logging.getLogger(__name__)


PoisonPill = object()


class Worker(threading.Thread):
    def __init__(self, queue: Queue):
        self.queue = queue
        self.doRun = True

        super().__init__(daemon=True)

    def run(self):
        while self.doRun:
            try:
                spot = self.queue.get()
                if spot is PoisonPill:
                    self.doRun = False
                else:
                    self.uploadSpot(spot)
                    self.queue.task_done()
            except Exception:
                logger.exception("Exception while uploading WSPRNet spot")

    def _getMode(self, spot):
        interval = round(spot["interval"] / 60)
        # FST4W modes are mapped not to conflict with WSPR modes 2 and 15:
        if spot["mode"] != "WSPR" and interval in [2, 15]:
            return interval + 1
        return interval

    def uploadSpot(self, spot):
        config = Config.get()
        # function=wspr&date=210114&time=1732&sig=-15&dt=0.5&drift=0&tqrg=7.040019&tcall=DF2UU&tgrid=JN48&dbm=37&version=2.3.0-rc3&rcall=DD5JFK&rgrid=JN58SC&rqrg=7.040047&mode=2
        # {'timestamp': 1610655960000, 'db': -23.0, 'dt': 0.3, 'freq': 7040048, 'drift': -1, 'msg': 'LA3JJ JO59 37', 'callsign': 'LA3JJ', 'locator': 'JO59', 'mode': 'WSPR'}
        date = datetime.fromtimestamp(spot["timestamp"] / 1000, tz=timezone.utc)
        data = parse.urlencode(
            {
                "function": "wspr",
                "date": date.strftime("%y%m%d"),
                "time": date.strftime("%H%M"),
                "sig": spot["db"],
                "dt": spot["dt"],
                # FST4W does not have drift
                "drift": spot["drift"] if "drift" in spot else 0,
                "tqrg": spot["freq"] / 1e6,
                "tcall": spot["callsign"],
                "tgrid": spot["locator"],
                "dbm": spot["dbm"],
                "version": openwebrx_version,
                "rcall": config["wsprnet_callsign"],
                "rgrid": Locator.fromCoordinates(config["receiver_gps"]),
                "mode": self._getMode(spot),
            }
        ).encode()
        request.urlopen("http://wsprnet.org/post/", data, timeout=60)


class WsprnetReporter(Reporter):
    def __init__(self):
        # max 100 entries
        self.queue = Queue(100)
        # single worker
        Worker(self.queue).start()

        # metrics
        metrics = Metrics.getSharedInstance()
        self.spotCounter = CounterMetric()
        metrics.addMetric("wsprnet.spots", self.spotCounter)

    def stop(self):
        while not self.queue.empty():
            self.queue.get(timeout=1)
            self.queue.task_done()
        self.queue.put(PoisonPill)

    def spot(self, spot):
        try:
            self.queue.put(spot, block=False)
            self.spotCounter.inc()
        except Full:
            logger.warning("WSPRNet Queue overflow, one spot lost")

    def getSupportedModes(self):
        return ["WSPR", "FST4W"]
