# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'control_window.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.

import sys

from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5.QtCore import QTimer

from config import NAME_SIMULATION, LCD_DIGITS_COUNT, TIMEOUT_DISPLAY
from Simulation import Simulation
from Controller import Controller
from threading import Thread

class Ui_MainWindow(object):

    __selected_cam: int = 1
    __is_simulation_running = False
    __timer: QTimer = None
    __controller: Controller = None
    __translate: QtCore.QCoreApplication.translate = None

    def setupUi(self, MainWindow, controller: Controller):
        
        self.__controller = controller

        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(812, 750)
        
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")

        # label: visualizacao da camera
        self.camera_label = QtWidgets.QLabel(self.centralwidget)
        self.camera_label.setGeometry(QtCore.QRect(330, 370, 271, 14))
        self.camera_label.setObjectName("label_2")

        # Camera
        self.camera_view = QtWidgets.QGraphicsView(self.centralwidget)
        self.camera_view.setGeometry(QtCore.QRect(330, 390, 300, 300))
        self.camera_view.setObjectName("graphicsView")

        # Figura guindaste
        self.crane_picture_view = QtWidgets.QGraphicsView(self.centralwidget)
        self.crane_picture_view.setGeometry(QtCore.QRect(10, 385, 300, 300))
        self.crane_picture_view.setObjectName("graphicsView_2")
        self.crane_picture_view.setStyleSheet("background:transparent;")
        self.__set_img(self.crane_picture_view, './crane.jpg', .5)

        # Status do Guindaste
        self.simulation_status_label = QtWidgets.QLabel(self.centralwidget)
        self.simulation_status_label.setGeometry(QtCore.QRect(10, 240, 131, 16))
        self.simulation_status_label.setObjectName("label_5")

        # text: status do guindaste
        self.simulation_status_display = QtWidgets.QTextBrowser(self.centralwidget)
        self.simulation_status_display.setGeometry(QtCore.QRect(10, 260, 271, 31))
        self.simulation_status_display.setObjectName("textBrowser")

        # Desligar guindaste
        self.stop_simulation_btn = QtWidgets.QPushButton(self.centralwidget)
        self.stop_simulation_btn.setGeometry(QtCore.QRect(150, 300, 131, 27))
        self.stop_simulation_btn.setObjectName("pushButton_6")

        # Ligar guindaste
        self.start_simulation_btn = QtWidgets.QPushButton(self.centralwidget)
        self.start_simulation_btn.setGeometry(QtCore.QRect(10, 300, 131, 27))
        self.start_simulation_btn.setObjectName("pushButton_7")

        # Status do Ima
        self.magnet_status_label = QtWidgets.QLabel(self.centralwidget)
        self.magnet_status_label.setGeometry(QtCore.QRect(330, 240, 91, 16))
        self.magnet_status_label.setObjectName("label_10")

        # text: status do ima
        self.magnet_status_display = QtWidgets.QTextBrowser(self.centralwidget)
        self.magnet_status_display.setGeometry(QtCore.QRect(330, 260, 271, 31))
        self.magnet_status_display.setObjectName("textBrowser_4")

        # Desligar ima
        self.magnet_turn_off_btn = QtWidgets.QPushButton(self.centralwidget)
        self.magnet_turn_off_btn.setGeometry(QtCore.QRect(470, 300, 131, 27))
        self.magnet_turn_off_btn.setObjectName("pushButton_9")

        # Ligar ima
        self.magnet_turn_on_btn = QtWidgets.QPushButton(self.centralwidget)
        self.magnet_turn_on_btn.setGeometry(QtCore.QRect(330, 300, 131, 27))
        self.magnet_turn_on_btn.setObjectName("pushButton_10")

        # Status da Carga
        self.load_status_label = QtWidgets.QLabel(self.centralwidget)
        self.load_status_label.setGeometry(QtCore.QRect(660, 10, 131, 16))
        self.load_status_label.setObjectName("label_6")

        # text: status da carga
        self.load_status_display = QtWidgets.QTextBrowser(self.centralwidget)
        self.load_status_display.setGeometry(QtCore.QRect(660, 30, 131, 31))
        self.load_status_display.setObjectName("textBrowser_2")

        # Sensor de Distancia
        self.sensor_proximity_label = QtWidgets.QLabel(self.centralwidget)
        self.sensor_proximity_label.setGeometry(QtCore.QRect(660, 80, 131, 20))
        self.sensor_proximity_label.setObjectName("label_13")

        # lcd: Sensor de Distancia
        self.sensor_proximity_lcd = QtWidgets.QLCDNumber(self.centralwidget)
        self.sensor_proximity_lcd.setGeometry(QtCore.QRect(660, 100, 131, 31))
        self.sensor_proximity_lcd.setObjectName("lcdNumber_9")

        # label: Angulo da Ferramenta
        self.hoist_angle_label = QtWidgets.QLabel(self.centralwidget)
        self.hoist_angle_label.setGeometry(QtCore.QRect(80, 110, 131, 20))
        self.hoist_angle_label.setObjectName("label_11")

        # lcd: Angulo da Ferramenta
        self.hoist_angle_lcd = QtWidgets.QLCDNumber(self.centralwidget)
        self.hoist_angle_lcd.setGeometry(QtCore.QRect(80, 130, 131, 31))
        self.hoist_angle_lcd.setObjectName("lcdNumber_7")
        self.hoist_angle_lcd.setDigitCount(LCD_DIGITS_COUNT)

        # slider: Angulo da Ferramenta
        self.hoist_angle_slider = QtWidgets.QSlider(self.centralwidget)
        self.hoist_angle_slider.setGeometry(QtCore.QRect(10, 170, 271, 29))
        self.hoist_angle_slider.setOrientation(QtCore.Qt.Horizontal)
        self.hoist_angle_slider.setObjectName("horizontalSlider_7")
        self.hoist_angle_slider.setMinimum(-10)
        self.hoist_angle_slider.setMaximum(10)
        self.hoist_angle_slider.setSliderPosition(0)
        self.hoist_angle_slider.setTickPosition(QtWidgets.QSlider.TicksAbove)
        self.hoist_angle_slider.setTickInterval(2)

        # label: Altura da Ferramenta
        self.hoist_height_label = QtWidgets.QLabel(self.centralwidget)
        self.hoist_height_label.setGeometry(QtCore.QRect(400, 110, 131, 20))
        self.hoist_height_label.setObjectName("label_8")

        # lcd: Altura da Ferramenta
        self.hoist_height_lcd = QtWidgets.QLCDNumber(self.centralwidget)
        self.hoist_height_lcd.setGeometry(QtCore.QRect(400, 130, 131, 31))
        self.hoist_height_lcd.setObjectName("lcdNumber_6")
        self.hoist_height_lcd.setDigitCount(LCD_DIGITS_COUNT)

        # Slider: Altura da Ferramenta
        self.hoist_height_slider = QtWidgets.QSlider(self.centralwidget)
        self.hoist_height_slider.setGeometry(QtCore.QRect(330, 170, 271, 29))
        self.hoist_height_slider.setOrientation(QtCore.Qt.Horizontal)
        self.hoist_height_slider.setObjectName("horizontalSlider_5")
        self.hoist_height_slider.setMinimum(-10)
        self.hoist_height_slider.setMaximum(10)
        self.hoist_height_slider.setSliderPosition(0)
        self.hoist_height_slider.setTickPosition(QtWidgets.QSlider.TicksAbove)
        self.hoist_height_slider.setTickInterval(2)

        # label: Avanco da Ferramenta
        self.crab_position_label = QtWidgets.QLabel(self.centralwidget)
        self.crab_position_label.setGeometry(QtCore.QRect(400, 10, 131, 20))
        self.crab_position_label.setObjectName("label_9")

        # lcd: Avanco da Ferramenta
        self.crab_position_lcd = QtWidgets.QLCDNumber(self.centralwidget)
        self.crab_position_lcd.setGeometry(QtCore.QRect(400, 30, 131, 31))
        self.crab_position_lcd.setObjectName("lcdNumber_5")
        self.crab_position_lcd.setDigitCount(LCD_DIGITS_COUNT)

        # Slider: Avanco da Ferramenta
        self.crab_position_slider = QtWidgets.QSlider(self.centralwidget)
        self.crab_position_slider.setGeometry(QtCore.QRect(330, 70, 271, 29))
        self.crab_position_slider.setOrientation(QtCore.Qt.Horizontal)
        self.crab_position_slider.setObjectName("horizontalSlider_6")
        self.crab_position_slider.setMinimum(-10)
        self.crab_position_slider.setMaximum(10)
        self.crab_position_slider.setSliderPosition(0)
        self.crab_position_slider.setTickPosition(QtWidgets.QSlider.TicksAbove)
        self.crab_position_slider.setTickInterval(2)

        # label: Angulo da Lanca
        self.arm_angle_label = QtWidgets.QLabel(self.centralwidget)
        self.arm_angle_label.setGeometry(QtCore.QRect(80, 10, 131, 20))
        self.arm_angle_label.setObjectName("label_12")

        # lcd: Angulo da Lanca
        self.arm_angle_lcd = QtWidgets.QLCDNumber(self.centralwidget)
        self.arm_angle_lcd.setGeometry(QtCore.QRect(80, 30, 131, 31))
        self.arm_angle_lcd.setObjectName("lcdNumber_8")
        self.arm_angle_lcd.setDigitCount(LCD_DIGITS_COUNT)

        # Slider: Angulo da Lanca
        self.arm_angle_slider = QtWidgets.QSlider(self.centralwidget)
        self.arm_angle_slider.setGeometry(QtCore.QRect(10, 70, 271, 29))
        self.arm_angle_slider.setOrientation(QtCore.Qt.Horizontal)
        self.arm_angle_slider.setObjectName("horizontalSlider_8")
        self.arm_angle_slider.setMinimum(-10)
        self.arm_angle_slider.setMaximum(10)
        self.arm_angle_slider.setSliderPosition(0)
        self.arm_angle_slider.setTickPosition(QtWidgets.QSlider.TicksAbove)
        self.arm_angle_slider.setTickInterval(2)

        # Botao: camera 1
        self.camera_btn1 = QtWidgets.QPushButton(self.centralwidget)
        self.camera_btn1.setGeometry(QtCore.QRect(640, 390, 131, 27))
        self.camera_btn1.setObjectName("pushButton_13")

        # Botao: camera 2
        self.camera_btn2 = QtWidgets.QPushButton(self.centralwidget)
        self.camera_btn2.setGeometry(QtCore.QRect(640, 440, 131, 27))
        self.camera_btn2.setObjectName("pushButton_14")

        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 812, 22))
        
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        
        self.__translate = QtCore.QCoreApplication.translate
        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

        '''
            TODO: 2021-08-26 - Pra que serve essa funcao raise() ??
        '''
        # self.graphicsView.raise_()
        # self.graphicsView2.raise_()

    def retranslateUi(self, MainWindow):
        MainWindow.setWindowTitle(self.__translate("MainWindow", NAME_SIMULATION))
        self.stop_simulation_btn.setText(self.__translate("MainWindow", "Desligar"))
        self.start_simulation_btn.setText(self.__translate("MainWindow", "Ligar"))
        self.simulation_status_label.setText(self.__translate("MainWindow", "Status do Guindaste"))
        self.load_status_label.setText(self.__translate("MainWindow", "Status da Carga"))
        self.magnet_status_label.setText(self.__translate("MainWindow", "Status do Ímã"))
        self.sensor_proximity_label.setText(self.__translate("MainWindow", "Sensor de Distância"))
        self.magnet_turn_off_btn.setText(self.__translate("MainWindow", "Desligar"))
        self.magnet_turn_on_btn.setText(self.__translate("MainWindow", "Ligar"))
        self.hoist_height_label.setText(self.__translate("MainWindow", "Altura da Ferramenta"))
        self.crab_position_label.setText(self.__translate("MainWindow", "Avanço da Ferramenta"))
        self.hoist_angle_label.setText(self.__translate("MainWindow", "Ângulo da Ferramenta"))
        self.arm_angle_label.setText(self.__translate("MainWindow", "Ângulo da Lança"))
        self.camera_btn1.setText(self.__translate("MainWindow", "Câmera 1"))
        self.camera_btn2.setText(self.__translate("MainWindow", "Câmera 2"))
        
    def initialize(self) -> None:
        self.__set_handler_callbacks()
        self.__set_timer()
        self.__update_camera_label()
        self.__update_simulation_status_display(False)
        self.__update_magnet_status_display(False)

    def __set_timer(self) -> None:
        '''
            Establishes a timer to trigger telemetry displayed values updates.
        '''

        self.__timer = QTimer()
        self.__timer.timeout.connect(self.__update_displayed_values)
        self.__timer.start(TIMEOUT_DISPLAY)

    def __update_displayed_values(self) -> None:
        '''
            Update the values shown on every lcd display / dinamic labels.
        '''

        if (not self.__is_simulation_running):
            return

        self.arm_angle_lcd.display(self.__get_display_number(controller.get_arm_angle()))
        self.crab_position_lcd.display(self.__get_display_number(controller.get_crab_position()))
        self.hoist_angle_lcd.display(self.__get_display_number(controller.get_hoist_angle()))
        self.hoist_height_lcd.display(self.__get_display_number(controller.get_hoist_height()))

        is_load_attached = self.__controller.is_load_attached()
        self.__update_load_status_display(is_load_attached)
        self.__update_simulation_status_display(self.__is_simulation_running)
        self.__update_magnet_status_display(self.__controller.is_magnet_active())

        prox_sensor_distance = controller.get_load_distance()
        prox_display_value = self.__get_display_number(prox_sensor_distance) if not is_load_attached else '--'
        self.sensor_proximity_lcd.display(prox_display_value)

        if (self.__selected_cam):
            self.__set_img(self.camera_view, self.__controller.get_cam_img(self.__selected_cam), 1.1)

    def __set_handler_callbacks(self) -> None:
        '''
            Establishes callback functions to be executed on user interaction with the ui.
        '''
        
        self.arm_angle_slider.valueChanged.connect(self.__set_arm_vel)
        self.crab_position_slider.valueChanged.connect(self.__set_crab_vel)
        self.hoist_angle_slider.valueChanged.connect(self.__set_hoist_angular_vel)
        self.hoist_height_slider.valueChanged.connect(self.__set_hoist_vertical_vel)
        
        self.start_simulation_btn.clicked.connect(self.__start_simulation)
        self.stop_simulation_btn.clicked.connect(self.__stop_simulation)
        self.magnet_turn_on_btn.clicked.connect(self.__turn_magnet_on)
        self.magnet_turn_off_btn.clicked.connect(self.__turn_magnet_off)
        self.camera_btn1.clicked.connect(self.__select_camera1)
        self.camera_btn2.clicked.connect(self.__select_camera2)

    def __start_simulation(self) -> None:
        self.__controller.start_simulation()
        self.__update_simulation_status_display(True)
        self.__is_simulation_running = True

    def __stop_simulation(self) -> None:
        self.__controller.stop_simulation()
        self.__update_simulation_status_display(False)
        self.__is_simulation_running = False

    def __turn_magnet_on(self) -> None:
        self.__controller.turn_magnet_on()
        self.__update_magnet_status_display(True)

    def __turn_magnet_off(self) -> None:
        self.__controller.turn_magnet_off()
        self.__update_magnet_status_display(False)

    def __set_arm_vel(self, level: int) -> None:
        self.__controller.set_arm_vel(level)
        
    def __set_crab_vel(self, level: int) -> None:
        self.__controller.set_crab_vel(level)

    def __set_hoist_vertical_vel(self, level: int) -> None:
        self.__controller.set_hoist_vertical_vel(level)

    def __set_hoist_angular_vel(self, level: int) -> None:
        self.__controller.set_hoist_angular_vel(level)

    def __select_camera1(self) -> None:
        self.__selected_cam = 1
        self.__update_camera_label()

    def __select_camera2(self) -> None:
        self.__selected_cam = 2
        self.__update_camera_label()

    def __update_camera_label(self) -> None:
        txt: str

        if (self.__selected_cam == 1):
            txt = 'Imagens da Câmera 01' 
        elif (self.__selected_cam == 2):
            txt = 'Imagens da Câmera 01'
        else:
            txt = 'Visualização da Câmera'

        self.camera_label.setText(self.__translate("MainWindow", txt))

    def __update_simulation_status_display(self, is_on: bool) -> None:
        self.simulation_status_display.setPlainText('Simulação em andamento' if is_on else 'Simulação parada')

    def __update_magnet_status_display(self, is_on: bool) -> None:
        self.magnet_status_display.setPlainText('Ligado' if is_on else 'Desligado')

    def __update_load_status_display(self, is_attached: bool) -> None:
        self.load_status_display.setPlainText('Acoplada' if is_attached else 'Não acoplada')

    def __get_display_number(self, value: float) -> str:
        '''
            Unifies numeric formatting for all number values exhibition.
        '''

        return str(round(value, 2))

    def __set_img(self, view: QtWidgets.QGraphicsView, img, scale: float = 1) -> None:
        scene_item = QtWidgets.QGraphicsPixmapItem(QtGui.QPixmap(img))
        scene_item.setScale(scale)
        scene = QtWidgets.QGraphicsScene(self.centralwidget)
        scene.addItem(scene_item)
        view.setScene(scene)

if __name__ != "__main__":
    sys.exit()

simulation: Simulation = None
app: QtWidgets.QApplication = None
thread_updater: Thread = None
ui: Ui_MainWindow = None

def finish_simulation() -> None:
    if simulation:
        simulation.disconnect()
    print('\n-- THE END --\n')

try:

    # Captura IP customizado (se houver)
    ip = sys.argv[1] if (len(sys.argv) > 1) else None
    if (len(sys.argv) > 1):
        ip = sys.argv[1]

    # Inicia simulacao
    simulation = Simulation()
    is_connected, client_id, conn_error = simulation.connect(ip) if ip else simulation.connect()
    if (not is_connected):
        raise ConnectionError(conn_error)

    # Iniciar controlador & tela
    app = QtWidgets.QApplication(sys.argv)
    # app.aboutToQuit.connect(finish_simulation)
    
    MainWindow = QtWidgets.QMainWindow()
    controller = Controller(simulation)
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow, controller)
    ui.initialize()
    
    # Exibe tela
    MainWindow.show()
    sys.exit(app.exec_())
            

except ConnectionError as conn_error:
    print('\nLost connection to simulation: ', conn_error)
    raise conn_error

except KeyboardInterrupt:
    print('\nKeyboardInterrupt...')
    pass

except Exception as error:
    print('\nFalha inesperada: ', error)
    raise error

finally:
    finish_simulation()