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

"""
VR360_ExtractPerspectiveView.py
***************************************************************************
*                                                                         *
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU General Public License as published by  *
*   the Free Software Foundation; either version 2 of the License, or     *
*   (at your option) any later version.                                   *
*                                                                         *
***************************************************************************
"""
__author__ = 'Leandro França'
__date__ = '2026-01-27'
__copyright__ = '(C) 2026, Leandro França'

from qgis.core import (QgsApplication,
                       QgsProcessingParameterFile,
                       QgsProcessingParameterNumber,
                       QgsProcessingException,
                       QgsProcessingParameterFileDestination,
                       QgsProcessingAlgorithm)

from lftools.geocapt.imgs import Imgs
from lftools.translations.translate import translate
from qgis.PyQt.QtGui import QIcon
import os
import numpy as np


class ExtractPerspectiveView(QgsProcessingAlgorithm):

    LOC = QgsApplication.locale()[:2]

    def tr(self, *string):
        return translate(string, self.LOC)

    def createInstance(self):
        return ExtractPerspectiveView()

    def name(self):
        return 'ExtractPerspectiveView'.lower()

    def displayName(self):
        return self.tr('Extract Perspective View', 'Extrair vista perspectiva')

    def group(self):
        return self.tr('VR 360°', 'RV 360°')

    def groupId(self):
        return 'vr360'

    def tags(self):
        return 'GeoOne,virtual,reality,realidade,esphere,esférica,360°,graus,photography,photos,fotos,fotografia,gopro,insta360,VR,RV,perspective,perspectiva,crop,recorte,FOV'.split(',')

    def icon(self):
        return QIcon(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'images/rv_360.png'))

    txt_en = 'Extracts a perspective (pinhole) image from a 360° equirectangular image, simulating the framing of a conventional camera oriented toward a specified viewing direction.'
    txt_pt = 'Extrai uma imagem em projeção perspectiva (pinhole) a partir de uma imagem 360° no formato equiretangular, simulando o enquadramento de uma câmera convencional orientada para uma direção específica no espaço.'
    figure = 'images/tutorial/vr360_extract_perspective.jpg'

    def shortHelpString(self):
        social_BW = Imgs().social_BW
        footer = '''<div align="center">
                      <img src="'''+ os.path.join(os.path.dirname(os.path.dirname(__file__)), self.figure) +'''">
                      </div>
                      <div align="left">
                      <p>
                      <b><a href="'''+ self.tr('https://portal.geoone.com.br/m/lessons/mapeamento360?classId=6047') + '''" target="_blank">'''+ self.tr('Click here to watch a full class on this tool',
                                    'Clique aqui para assistir uma aula completa sobre esta ferramenta') +'''</a></b>
                      </p>
                      <p>
                      <b><a href="'''+ self.tr('https://geoone.com.br/pvmapeamento360/') + '''" target="_blank">'''+ self.tr('Enroll in the 360° VR course in QGIS.',
                                    'Inscreva-se no curso de RV 360° no QGIS') +'</a><br><br>'+ self.tr('Author: Leandro Franca', 'Autor: Leandro França')+'''</b>
                      </p>'''+ social_BW + '''
                    </div>'''
        return self.tr(self.txt_en, self.txt_pt) + footer

    INPUT = 'INPUT'
    LAT = 'LAT'
    LON = 'LON'
    FOV = 'FOV'
    W = 'W'
    H = 'H'
    ROLL = 'ROLL'
    OUTPUT = 'OUTPUT'

    def initAlgorithm(self, config=None):

        self.addParameter(
            QgsProcessingParameterFile(
                self.INPUT,
                self.tr('Equirectangular image (360°)', 'Imagem equiretangular (360°)'),
                behavior=QgsProcessingParameterFile.File,
                fileFilter='Image (*.jpeg *.jpg *.JPG)'
            )
        )

        self.addParameter(
            QgsProcessingParameterNumber(
                self.LAT,
                self.tr('Perspective center – latitude', 'Centro da perspectiva - latitude'),
                type=QgsProcessingParameterNumber.Type.Double,
                minValue=-90.0,
                maxValue=90.0,
                defaultValue=0.0
            )
        )

        self.addParameter(
            QgsProcessingParameterNumber(
                self.LON,
                self.tr('Perspective center – longitude', 'Centro da perspectiva - longitude'),
                type=QgsProcessingParameterNumber.Type.Double,
                minValue=-180.0,
                maxValue=180.0,
                defaultValue=0.0
            )
        )

        self.addParameter(
            QgsProcessingParameterNumber(
                self.FOV,
                self.tr('Horizontal field of view (°)', 'Campo de visão horizontal (°)'),
                type=QgsProcessingParameterNumber.Type.Double,
                minValue=30.0,
                maxValue=110.0,
                defaultValue=90.0
            )
        )

        self.addParameter(
            QgsProcessingParameterNumber(
                self.W,
                self.tr('Output image width (pixels)', 'Largura da imagem de saída (pixels)'),
                type=QgsProcessingParameterNumber.Type.Integer,
                minValue=256,
                maxValue=8192,
                defaultValue=1920
            )
        )

        self.addParameter(
            QgsProcessingParameterNumber(
                self.H,
                self.tr('Output image height (pixels)', 'Altura da imagem de saída (pixels)'),
                type=QgsProcessingParameterNumber.Type.Integer,
                minValue=256,
                maxValue=8192,
                defaultValue=1080
            )
        )

        self.addParameter(
            QgsProcessingParameterNumber(
                self.ROLL,
                self.tr('Camera roll (°)', 'Rotação da câmera (roll) (°)'),
                type=QgsProcessingParameterNumber.Type.Double,
                minValue=-180.0,
                maxValue=180.0,
                defaultValue=0.0
            )
        )

        # OUTPUT
        self.addParameter(
            QgsProcessingParameterFileDestination(
                self.OUTPUT,
                self.tr('Perspective view image', 'Imagem de vista perspectiva'),
                fileFilter='Image (*.jpg)'
            )
        )


    def processAlgorithm(self, parameters, context, feedback):

        from lftools.dependencies import ensure_pillow
        from lftools.geocapt.vr360 import extract_perspective_from_equirect
        Image = ensure_pillow(feedback)
        if Image is None:
            raise QgsProcessingException(
                "The Pillow library (PIL) is required for this tool and could not be loaded automatically."
            )   
        
        # INPUT (imagem equiretangular)
        entrada = self.parameterAsFile(parameters, self.INPUT, context)
        if not entrada:
            raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT))

        # Parâmetros numéricos
        center_lat = self.parameterAsDouble(parameters, self.LAT, context)
        center_lon = self.parameterAsDouble(parameters, self.LON, context)
        fov = self.parameterAsDouble(parameters, self.FOV, context)
        w = self.parameterAsInt(parameters, self.W, context)
        h = self.parameterAsInt(parameters, self.H, context)
        roll = self.parameterAsDouble(parameters, self.ROLL, context)

        # OUTPUT (arquivo de saída)
        saida = self.parameterAsFileOutput(parameters, self.OUTPUT, context)
        if not saida:
            raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT))

        # Checagem rápida equiretangular ~2:1
        img_pil = Image.open(entrada)
        exif_bytes = img_pil.info.get("exif", None)
        W_eq, H_eq = img_pil.size
        ratio = W_eq / H_eq
        if not (1.94 <= ratio <= 2.06):
            raise QgsProcessingException(
                self.tr(
                    f'Input image does not appear to be equirectangular (expected ~2:1, found {ratio:.3f}).',
                    f'A imagem de entrada não parece ser equiretangular (esperado ~2:1, encontrado {ratio:.3f}).'
                )
            )

        feedback.pushInfo(self.tr('Reading input image...', 'Lendo imagem de entrada...'))

        # Carrega como RGB
        img_eq = np.array(img_pil.convert("RGB"))

        feedback.pushInfo(self.tr('Extracting perspective view...', 'Extraindo vista perspectiva...'))
        feedback.setProgress(30)

        # Gera a vista perspectiva
        view = extract_perspective_from_equirect(
            img_eq,
            center_lat=center_lat,
            center_lon=center_lon,
            fov_h=fov,
            w=w,
            h=h,
            roll=roll,
            interp="bilinear"
        )

        feedback.setProgress(90)
        feedback.pushInfo(self.tr('Saving output image...', 'Salvando imagem de saída...'))

        out = Image.fromarray(view)
        if exif_bytes:
            out.save(saida, exif=exif_bytes)
        else:
            out.save(saida)

        feedback.setProgress(100)
        feedback.pushInfo(self.tr('Operation completed successfully!', 'Operação finalizada com sucesso!'))
        feedback.pushInfo(self.tr('Leandro Franca - Cartographic Engineer', 'Leandro França - Eng Cart'))

        return {self.OUTPUT: saida}
