import asyncio
import qasync
import warnings

from PySide6 import QtCore
from PySide6.QtCore import QTimer, Slot
from PySide6.QtWidgets import QMainWindow, QLabel, QApplication
from astroquery.exceptions import NoResultsWarning
from astropy.coordinates import SkyCoord, AltAz, EarthLocation, Angle
import astropy.units as u
from astropy.time import Time

from pybrotlib import Transport, BROT
from .pointing.pointing import Pointing
from .qpointingmodel import QPointingModel
from .qdatalogger import QDataLogger
from .qt.mainwindow_ui import Ui_MainWindow
from .qmessagelogger import QMessageLogger
from .qtelescopecontrol import QTelescopeControl
from .stellarium import Stellarium

warnings.filterwarnings("error", category=NoResultsWarning)


class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(
        self,
        transport: Transport,
        location: tuple[float, float, float],
        telescope_name: str,
        pointing_model: str,
        **kwargs,
    ):
        QMainWindow.__init__(self, **kwargs)
        self.setupUi(self)

        self.transport = transport
        self.brot = BROT(self.transport, telescope_name)
        self.stellarium = Stellarium(self.brot.telescope)

        # correct location
        self.location = EarthLocation(lat=location[0] * u.deg, lon=location[1] * u.deg, height=location[2] * u.m)
        self.telescope_name = telescope_name
        self.pointing_model = Pointing.get_model(pointing_model)

        # set telemetry for status sidebar
        self.status.brot = self.brot
        self.status.telemetry = transport.telemetry
        self.plotPosition.location = self.location

        self.tabMessageLogger = QMessageLogger()
        self.transport.logMessageReceived.connect(self.tabMessageLogger.log)

        self.tabTelescopeControl = QTelescopeControl(transport, self.brot, self.tabMessageLogger, self.location)
        self.tabPointingModel = QPointingModel(transport.telemetry, self.brot, self.location, self.pointing_model)
        self.tabDataLogger = QDataLogger()

        self.tabWidget.addTab(self.tabTelescopeControl, "Telescope Control")
        self.tabWidget.addTab(self.tabPointingModel, "Pointing Model")
        self.tabWidget.addTab(self.tabDataLogger, "Data Logger")
        self.tabWidget.addTab(self.tabMessageLogger, "Message Logger")

        # status widgets
        self.label_server = QLabel(f"Server: {transport}")
        self.statusbar.addPermanentWidget(self.label_server)
        self.label_connected = QLabel("not connected")
        self.statusbar.addPermanentWidget(self.label_connected)
        self.label_time = QLabel()
        self.statusbar.addPermanentWidget(self.label_time)

        # connections
        self.tabTelescopeControl.new_target_coordinates.connect(self.new_target_coordinates)

        QTimer.singleShot(100, self._update_values_loop)
        QTimer.singleShot(100, self.run)

        self.log("CRITICAL", "GUI has been started, now listening...")

    @qasync.asyncSlot()
    async def run(self):
        await self.stellarium.start()
        asyncio.create_task(self.transport.run())

    def log(self, level: str, message: str):
        self.statusbar.showMessage(f"[{level}] {message}", 5000)
        self.tabMessageLogger.log(level, message)

    @qasync.asyncSlot()
    async def _update_values_loop(self) -> None:
        while True:
            await self._update_values()
            await asyncio.sleep(0.5)

    @qasync.asyncSlot()
    async def _update_values(self) -> None:
        """Updates the values received from the Telescope."""

        # set connection status and make label green :)
        if self.transport.connected:
            self.label_connected.setText("connected")
            self.label_connected.setStyleSheet("background-color:  #00aa00")
        else:
            self.label_connected.setText("not connected")
            self.label_connected.setStyleSheet("background-color:  #aa0000")

        # time
        self.label_time.setText(Time.now().isot.split("T")[1].split(".")[0])

        # update telemetry
        self.tabDataLogger.update_from_dict(self.transport.data)

        # plot
        if (
            self.transport.telemetry.POSITION.HORIZONTAL.ALT != 0
            and self.transport.telemetry.POSITION.HORIZONTAL.AZ != 0
        ):
            altaz = AltAz(
                alt=self.transport.telemetry.POSITION.HORIZONTAL.ALT * u.deg,
                az=self.transport.telemetry.POSITION.HORIZONTAL.AZ * u.deg,
                location=self.location,
                obstime=Time.now(),
            )
            self.plotPosition.set_current_position(altaz)

    @Slot()
    def new_target_coordinates(self, coords: SkyCoord) -> None:
        self.plotPosition.set_target_position(coords)
