from pathlib import Path

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.slider import Slider
from kivy.uix.label import Label
from kivy.uix.image import Image as KivyImage
from kivy.graphics.texture import Texture
from kivy.core.window import Window
import numpy as np
from PIL import Image
import cv2
import io

class CameraTransformApp(App):
    def build(self):
        Window.size = (900, 700)

        # Главный контейнер
        main_layout = BoxLayout(orientation='vertical', padding=10, spacing=10)

        # Контейнер для изображения
        self.image_widget = KivyImage(size_hint=(1, 0.7))
        main_layout.add_widget(self.image_widget)

        # Контейнер для ползунков
        controls_layout = BoxLayout(orientation='vertical', size_hint=(1, 0.3), spacing=5)

        # Yaw (рыскание) - вращение вокруг вертикальной оси
        yaw_layout = BoxLayout(orientation='horizontal', spacing=10, size_hint=(1, None), height=40)
        yaw_layout.add_widget(Label(text='Рыскание (Yaw):', size_hint=(0.2, 1)))
        self.yaw_slider = Slider(min=-45, max=45, value=0, size_hint=(0.6, 1))
        self.yaw_slider.bind(value=self.on_slider_change)
        self.yaw_label = Label(text='0°', size_hint=(0.2, 1))
        yaw_layout.add_widget(self.yaw_slider)
        yaw_layout.add_widget(self.yaw_label)
        controls_layout.add_widget(yaw_layout)

        # Pitch (тангаж) - наклон вперед/назад
        pitch_layout = BoxLayout(orientation='horizontal', spacing=10, size_hint=(1, None), height=40)
        pitch_layout.add_widget(Label(text='Тангаж (Pitch):', size_hint=(0.2, 1)))
        self.pitch_slider = Slider(min=-45, max=45, value=0, size_hint=(0.6, 1))
        self.pitch_slider.bind(value=self.on_slider_change)
        self.pitch_label = Label(text='0°', size_hint=(0.2, 1))
        pitch_layout.add_widget(self.pitch_slider)
        pitch_layout.add_widget(self.pitch_label)
        controls_layout.add_widget(pitch_layout)

        # Roll (крен) - наклон влево/вправо
        roll_layout = BoxLayout(orientation='horizontal', spacing=10, size_hint=(1, None), height=40)
        roll_layout.add_widget(Label(text='Крен (Roll):', size_hint=(0.2, 1)))
        self.roll_slider = Slider(min=-45, max=45, value=0, size_hint=(0.6, 1))
        self.roll_slider.bind(value=self.on_slider_change)
        self.roll_label = Label(text='0°', size_hint=(0.2, 1))
        roll_layout.add_widget(self.roll_slider)
        roll_layout.add_widget(self.roll_label)
        controls_layout.add_widget(roll_layout)

        main_layout.add_widget(controls_layout)

        # Создаем тестовое изображение с сеткой
        self.original_image = self.create_test_image()
        self.update_image()

        return main_layout

    def create_test_image(self):
        """Создает тестовое изображение с сеткой и текстом"""
        img = cv2.imread(Path('images') / 'photo_610.png')

        return img

    def get_rotation_matrix(self, yaw, pitch, roll):
        """Вычисляет матрицу поворота 3D"""
        # Конвертируем в радианы
        yaw = np.radians(yaw)
        pitch = np.radians(pitch)
        roll = np.radians(roll)

        # Матрица поворота вокруг Z (yaw)
        Rz = np.array([
            [np.cos(yaw), -np.sin(yaw), 0],
            [np.sin(yaw), np.cos(yaw), 0],
            [0, 0, 1]
        ])

        # Матрица поворота вокруг Y (pitch)
        Ry = np.array([
            [np.cos(pitch), 0, -np.sin(pitch)],
            [0, 1, 0],
            [np.sin(pitch), 0, np.cos(pitch)]
        ])

        # Матрица поворота вокруг X (roll)
        Rx = np.array([
            [1, 0, 0],
            [0, np.cos(roll), -np.sin(roll)],
            [0, np.sin(roll), np.cos(roll)]
        ])

        # Комбинируем повороты: R = Rz * Ry * Rx
        R = Rx @ Ry @ Rz
        return R

    def apply_perspective_transform(self, img, yaw, pitch, roll):
        """Применяет перспективное преобразование к изображению"""
        h, w = img.shape[:2]

        # Параметры камеры
        focal_length = w
        distance = w

        # Матрица камеры
        camera_matrix = np.array([
            [focal_length / 2, 0, w / 2],
            [0, focal_length / 2, h / 2],
            [0, 0, 1]
        ])

        # Получаем матрицу поворота
        R = self.get_rotation_matrix(yaw, pitch, roll)

        T = np.array([
            [1, 0, 0.2],
            [0, 1, 0.1],
            [0, 0, 2],
        ])

        # Создаем матрицу трансформации
        H =  camera_matrix @ R @ T @ np.linalg.inv(camera_matrix)

        # Применяем преобразование
        try:
            result = cv2.warpPerspective(img, H, (w, h), 
                                        borderMode=cv2.BORDER_CONSTANT,
                                        borderValue=(50, 50, 50))
            return result
        except:
            return img

    def update_image(self):
        """Обновляет изображение с текущими параметрами"""
        yaw = self.yaw_slider.value
        pitch = self.pitch_slider.value
        roll = self.roll_slider.value

        # Применяем трансформацию
        transformed = self.apply_perspective_transform(
            self.original_image, yaw, pitch, roll
        )

        # Конвертируем в текстуру Kivy
        buf = cv2.flip(transformed, 0).tobytes()
        texture = Texture.create(size=(transformed.shape[1], transformed.shape[0]), 
                                colorfmt='bgr')
        texture.blit_buffer(buf, colorfmt='bgr', bufferfmt='ubyte')
        self.image_widget.texture = texture

    def on_slider_change(self, instance, value):
        """Обработчик изменения ползунков"""
        self.yaw_label.text = f'{int(self.yaw_slider.value)}°'
        self.pitch_label.text = f'{int(self.pitch_slider.value)}°'
        self.roll_label.text = f'{int(self.roll_slider.value)}°'
        self.update_image()

if __name__ == '__main__':
    CameraTransformApp().run()
