#!/usr/bin/env python
#####################################
# Imports
#####################################
# Python native imports
import sys
from PyQt5 import QtWidgets, QtCore, QtGui, uic
import signal
import rospy
import logging
import qdarkstyle
import PIL.Image
from PIL.ImageQt import ImageQt

# Custom Imports
import Framework.StartupSystems.ROSMasterChecker as ROSMasterChecker
import Framework.LoggingSystems.Logger as Logger
import Framework.VideoSystems.RoverVideoCoordinator as RoverVideoCoordinator
import Framework.MapSystems.RoverMapCoordinator as RoverMapCoordinator

#####################################
# Global Variables
#####################################
UI_FILE_LEFT = "Resources/Ui/left_screen.ui"
UI_FILE_RIGHT = "Resources/Ui/right_screen.ui"

#####################################
# Class Organization
#####################################
# Class Name:
#   "init"
#   "run (if there)" - personal pref
#   "private methods"
#   "public methods, minus slots"
#   "slot methods"
#   "static methods"
#   "run (if there)" - personal pref


#####################################
# ApplicationWindow Class Definition
#####################################
class ApplicationWindow(QtWidgets.QMainWindow):
    exit_requested_signal = QtCore.pyqtSignal()

    kill_threads_signal = QtCore.pyqtSignal()

    def __init__(self, parent=None, ui_file_path=None):
        super(ApplicationWindow, self).__init__(parent)

        uic.loadUi(ui_file_path, self)

        QtWidgets.QShortcut(QtGui.QKeySequence("Ctrl+Q"), self, self.exit_requested_signal.emit)


#####################################
# GroundStation Class Definition
#####################################
class GroundStation(QtCore.QObject):
    LEFT_SCREEN_ID = 1
    RIGHT_SCREEN_ID = 0

    exit_requested_signal = QtCore.pyqtSignal()

    start_threads_signal = QtCore.pyqtSignal()
    connect_signals_and_slots_signal = QtCore.pyqtSignal()
    kill_threads_signal = QtCore.pyqtSignal()

    def __init__(self, parent=None,):
        # noinspection PyArgumentList
        super(GroundStation, self).__init__(parent)

        # ##### Setup the Logger Immediately #####
        self.logger_setup_class = Logger.Logger(console_output=True)  # Doesn't need to be shared

        # ########## Get the Pick And Plate instance of the logger ##########
        self.logger = logging.getLogger("groundstation")

        self.shared_objects = {
            "screens": {},
            "regular_classes": {},
            "threaded_classes": {}
        }

        # ###### Instantiate Left And Right Screens ######
        self.shared_objects["screens"]["left_screen"] = \
            self.create_application_window(UI_FILE_LEFT, "Rover Ground Station Left Screen",
                                           self.LEFT_SCREEN_ID)  # type: ApplicationWindow

        self.shared_objects["screens"]["right_screen"] = \
            self.create_application_window(UI_FILE_RIGHT, "Rover Ground Station Right Screen",
                                           self.RIGHT_SCREEN_ID)  # type: ApplicationWindow

        # ###### Initialize the Ground Station Node ######
        rospy.init_node("ground_station")

        # ##### Instantiate Regular Classes ######

        # ##### Instantiate Threaded Classes ######
        self.__add_thread("Video Coordinator", RoverVideoCoordinator.RoverVideoCoordinator(self.shared_objects))
        self.__add_thread("Map Coordinator", RoverMapCoordinator.RoverMapCoordinator(self.shared_objects))
        self.connect_signals_and_slots_signal.emit()
        self.__connect_signals_to_slots()
        self.start_threads_signal.emit()
        
        compass_image = PIL.Image.open("Resources/Images/compass.png").resize((300, 300))  # PIL.Image     
        self.shared_objects["right_screen"].compass_label.setPixmap(QtGui.QPixmap.fromImage(ImageQt(compass_image))) 

    def ___ros_master_running(self):
        checker = ROSMasterChecker.ROSMasterChecker()

        if not checker.master_present(5):
            self.logger.debug("ROS Master Not Found!!!! Exiting!!!")
            QtGui.QGuiApplication.exit()
            return False
        return True

    def __add_thread(self, thread_name, instance):
        self.shared_objects["threaded_classes"][thread_name] = instance
        instance.setup_signals(self.start_threads_signal, self.connect_signals_and_slots_signal,
                               self.kill_threads_signal)

    def __connect_signals_to_slots(self):
        self.shared_objects["screens"]["left_screen"].exit_requested_signal.connect(self.on_exit_requested__slot)
        self.shared_objects["screens"]["right_screen"].exit_requested_signal.connect(self.on_exit_requested__slot)

    def on_exit_requested__slot(self):
        self.kill_threads_signal.emit()

        # Wait for Threads
        for thread in self.shared_objects["threaded_classes"]:
            self.shared_objects["threaded_classes"][thread].wait()

        QtGui.QGuiApplication.exit()

    @staticmethod
    def create_application_window(ui_file_path, title, display_screen):
        system_desktop = QtWidgets.QDesktopWidget()  # This gets us access to the desktop geometry

        app_window = ApplicationWindow(parent=None, ui_file_path=ui_file_path)  # Make a window in this application
        app_window.setWindowTitle(title)  # Sets the window title

        app_window.setWindowFlags(app_window.windowFlags() |  # Sets the windows flags to:
                                  QtCore.Qt.FramelessWindowHint |  # remove the border and frame on the application,
                                  QtCore.Qt.WindowStaysOnTopHint |  # and makes the window stay on top of all others
                                  QtCore.Qt.X11BypassWindowManagerHint)  # This is needed to show fullscreen in gnome

        app_window.setGeometry(
            system_desktop.screenGeometry(display_screen))  # Sets the window to be on the first screen

        app_window.showFullScreen()  # Shows the window in full screen mode

        return app_window


#####################################
# Main Definition
#####################################
if __name__ == "__main__":
    signal.signal(signal.SIGINT, signal.SIG_DFL)  # This allows the keyboard interrupt kill to work properly

    # ########## Start the QApplication Framework ##########
    application = QtWidgets.QApplication(sys.argv)  # Create the ase qt gui application
    application.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())

    # ########## Set Organization Details for QSettings ##########
    QtCore.QCoreApplication.setOrganizationName("OSURC")
    QtCore.QCoreApplication.setOrganizationDomain("http://osurobotics.club/")
    QtCore.QCoreApplication.setApplicationName("groundstation")

    # ########## Check ROS Master Status ##########
    master_checker = ROSMasterChecker.ROSMasterChecker()

    if not master_checker.master_present(5):
        message_box = QtWidgets.QMessageBox()
        message_box.setWindowTitle("Rover Ground Station")
        message_box.setText("Connection to ROS Master Failed!!!\n" +
                            "Ensure ROS master is running or check for network issues.")
        message_box.exec_()
        exit()

    # ########## Start Ground Station If Ready ##########
    ground_station = GroundStation()
    application.exec_()  # Execute launching of the application
