#! /usr/bin/env python
# -*- coding: utf-8 -*-

"""
Module that contains widget implementation for taking snapshots
"""

from __future__ import print_function, division, absolute_import

import os

from Qt.QtCore import *
from Qt.QtWidgets import *
from Qt.QtGui import *

import tpDcc
from tpDcc.libs.python import path as path_utils
from tpDcc.libs.qt.core import qtutils
from tpDcc.libs.qt.widgets import layouts, label, lineedit, buttons


class SnapshotWindow(tpDcc.Window, object):
    saved = Signal(str)

    def __init__(self, path=None, image_type='png', width=512, height=512, on_save=None, parent=None):

        self._default_width = width
        self._default_height = height
        self._save_path = path
        self._image_type = image_type
        self._keep_aspect = True
        self._locked = False
        self._last_saved_location = None

        super(SnapshotWindow, self).__init__(title='Snapshot', height=height, width=width, parent=parent)

        if on_save:
            self.saved.connect(on_save)

        self.set_snapshot_size(width, height)

    def get_main_layout(self):
        main_layout = layouts.VerticalLayout(spacing=0)
        main_layout.setContentsMargins(0, 0, 0, 0)

        return main_layout

    def ui(self):
        super(SnapshotWindow, self).ui()

        camera_icon = tpDcc.ResourcesMgr().icon('camera')
        cancel_icon = tpDcc.ResourcesMgr().icon('delete')
        self._link_icon = tpDcc.ResourcesMgr().icon('link')
        self._unlink_icon = tpDcc.ResourcesMgr().icon('unlink')
        self._lock_icon = tpDcc.ResourcesMgr().icon('lock')
        self._unlock_icon = tpDcc.ResourcesMgr().icon('unlock')

        self.window().setWindowFlags(self.window().windowFlags() | Qt.WindowStaysOnTopHint)

        self._setup_dragger()
        self.main_widget.setStyleSheet('WindowContents { background-color: transparent; }')

        snap_layout = layouts.HorizontalLayout(spacing=0)

        self._snap_widget = QWidget()
        self._snap_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        left_panel = SnapshotFrame(self)
        left_panel.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding)
        left_panel.setFixedWidth(qtutils.dpi_scale(5))
        right_panel = SnapshotFrame(self)
        right_panel.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.MinimumExpanding)
        right_panel.setFixedWidth(qtutils.dpi_scale(5))
        snap_layout.addWidget(left_panel)
        snap_layout.addWidget(self._snap_widget, stretch=1)
        snap_layout.addWidget(right_panel)

        buttom_layout = layouts.HorizontalLayout(margins=(5, 5, 5, 5))
        self._bottom_bar = SnapshotFrame(self)
        self._bottom_bar.setLayout(buttom_layout)
        self._bottom_bar.setFixedHeight(qtutils.dpi_scale(33))

        self._image_size_lbl = label.BaseLabel('Image Size')
        qtutils.set_horizontal_size_policy(self._image_size_lbl, QSizePolicy.Ignored)
        self._image_size_lbl.setMaximumWidth(self._image_size_lbl.sizeHint().width())
        self._width_line = lineedit.BaseLineEdit(input_mode='int')
        self._width_line.setMinimumWidth(qtutils.dpi_scale(19))
        qtutils.set_size_hint(self._width_line, qtutils.size_by_dpi(QSize(40, self._width_line.sizeHint().height())))
        self._height_line = lineedit.BaseLineEdit(input_mode='int')
        self._height_line.setMinimumHeight(qtutils.dpi_scale(19))
        qtutils.set_size_hint(self._height_line, qtutils.size_by_dpi(QSize(40, self._height_line.sizeHint().height())))
        self._aspect_link_button = buttons.BaseToolButton()
        self._aspect_link_button.setIcon(self._link_icon)
        self._aspect_link_button.setCheckable(True)
        self._aspect_link_button.setMinimumWidth(qtutils.dpi_scale(24))
        self._aspect_link_button.setChecked(True)
        self._lock_button = buttons.BaseToolButton()
        self._lock_button.setCheckable(True)
        self._lock_button.setIcon(self._unlock_icon)
        self._snapshot_button = buttons.BaseButton('Snapshot', camera_icon)
        self._snapshot_button.setMaximumWidth(self._snapshot_button.sizeHint().width())
        self._cancel_button = buttons.BaseButton('Cancel', cancel_icon)
        qtutils.set_horizontal_size_policy(self._cancel_button, QSizePolicy.Ignored)
        self._cancel_button.setMaximumWidth(self._cancel_button.sizeHint().width())

        self._cancel_button.setMinimumWidth(qtutils.dpi_scale(24))

        buttom_layout.addWidget(self._image_size_lbl, 4)
        buttom_layout.addWidget(self._width_line)
        buttom_layout.addWidget(self._aspect_link_button)
        buttom_layout.addWidget(self._height_line)
        buttom_layout.addWidget(self._lock_button)
        buttom_layout.addStretch()
        buttom_layout.addWidget(self._snapshot_button, 3)
        buttom_layout.addWidget(self._cancel_button, 3)

        self.main_layout.addLayout(snap_layout)
        self.main_layout.addWidget(self._bottom_bar)

        self.window().setMinimumSize(98, 142)

    def setup_signals(self):
        [r.windowResized.connect(self._on_window_resize) for r in self.get_horizontal_resizers()]
        [r.windowResized.connect(self._on_vertical_resize) for r in self.get_vertical_resizers()]
        [r.windowResized.connect(self._on_window_resize) for r in self.get_corner_resizers()]
        self._width_line.editingFinished.connect(self._on_size_edit_changed)
        self._height_line.editingFinished.connect(self._on_size_edit_changed)
        self._aspect_link_button.clicked.connect(self._on_toggle_aspect)
        self._lock_button.clicked.connect(self._on_toggle_lock)
        self._snapshot_button.clicked.connect(self.snapshot)
        self._cancel_button.clicked.connect(self.fade_close)

    def showEvent(self, event):
        super(SnapshotWindow, self).showEvent(event)

        if hasattr(self, '_snap_widget'):
            self.set_snapshot_size(512, 512)
            qtutils.process_ui_events()
            self._update_widgets()
            self.resizeEvent(QResizeEvent(self.size(), self.size()))

    def resizeEvent(self, event):
        super(SnapshotWindow, self).resizeEvent(event)

        widgets_to_hide = ((self._image_size_lbl, 250), (self._lock_button, 240),
                           (self._aspect_link_button, 185), (self._cancel_button, 155))
        for widget, size in widgets_to_hide:
            widget.setVisible(self.width() > qtutils.dpi_scale(size))

        spacer_threshold = 120

        self._bottom_bar.layout().setSpacing(0) if self.width() < spacer_threshold else \
            self._bottom_bar.layout().setSpacing(6)

    def set_save_path(self, file_path):
        """
        Sets the path where snapshot will be saved
        :param file_path: str
        """

        self._save_path = file_path

    def set_snapshot_size(self, width, height):
        """
        Sets the size of the snapshot widget
        :param width: int
        :param height: int
        """

        width_offset = self._get_width_offset()
        height_offset = self._get_height_offset()

        if self._keep_aspect:
            if self.sender() == self._width_line:
                self.window().resize(width + width_offset, width + height_offset)
            elif self.sender() == self._height_line:
                self.window().resize(height + width_offset, height + height_offset)
            else:
                self.window().resize(width + width_offset, width + height_offset)
        else:
            self.window().resize(width + width_offset, height + height_offset)

        self._update_widgets()

    def snapshot(self):
        """
        Takes snapshot
        """

        if not self._save_path:
            tpDcc.logger.error('Path not specificed for snapshot.')
            return

        rect = QRect(self._snap_widget.rect())
        pos = self._snap_widget.mapToGlobal(QPoint(0, 0))
        rect.translate(pos.x(), pos.y())
        self.setWindowOpacity(0)
        self._snapshot_pixmap = qtutils.desktop_pixmap_from_rect(rect)
        dir_path = os.path.dirname(self._save_path)
        if not os.path.exists(os.path.dirname(dir_path)):
            os.makedirs(dir_path)

        self.save(self._save_path, self._image_type)

    def save(self, file_path, image_type='png'):
        """
        Saves screen to a file
        :param file_path: str, path to save image into
        :param image_type: str, image type (png or jpg)
        """

        saved = None
        if not file_path:
            tpDcc.logger.error('Path not specificed for snapshot.')
            self.saved.emit(saved)
            return
        file_dir, file_name, file_ext = path_utils.split_path(file_path)
        image_type = image_type if image_type.startswith('.') else '.{}'.format(image_type)
        if image_type != file_ext:
            if file_ext not in ('.png', '.jpg'):
                tpDcc.logger.warning('Image of type "{}" is not supported by snapshot!'.format(file_ext))
                return
            image_type = file_ext

        saved = self._snapshot_pixmap.save(file_path, image_type)
        self._last_saved_location = file_path
        self.saved.emit(file_path if saved else None)

    def _setup_dragger(self):
        """
        Internal function that setup window dragger
        :return:
        """

        self._dragger.set_minimize_enabled(False)
        self._dragger.set_maximized_enabled(False)
        self._dragger.set_height(20)
        self._dragger.hide_logo()

    def _get_width_offset(self):
        return self.window().size().height() - self._snap_widget.height()

    def _get_height_offset(self):
        return self.window().size().width() - self._snap_widget.width()

    def _get_bottom_size(self):
        return self._bottom_bar.height() + self._bottom_resizer.height() + qtutils.dpi_scale(2)

    def _update_widgets(self):
        self._width_line.blockSignals(True)
        self._height_line.blockSignals(True)
        try:
            self._width_line.setText(str(self._snap_widget.width()))
            self._height_line.setText(str(self._snap_widget.height()))
            self.setFocus()
        finally:
            self._width_line.blockSignals(False)
            self._height_line.blockSignals(False)

    def _on_toggle_aspect(self):
        """
        Internal callback function that is called when toggle aspect ratio button is clicked by the user
        """

        self._keep_aspect = not self._keep_aspect
        if self._keep_aspect:
            self._aspect_link_button.setIcon(self._link_icon)
            self.window().resize(
                self.window().height() + self._get_width_offset(), self.window().height() + self._get_height_offset())
            self._update_widgets()
        else:
            self._aspect_link_button.setIcon(self._unlink_icon)

    def _on_toggle_lock(self):
        """
        Internal callback function that is called when the lock button is clicked by the user
        """

        self._locked = not self._locked
        if self._locked:
            self.set_resizers_enabled(False)
            self._lock_button.setIcon(self._lock_icon)
            self._lock_button.setChecked(True)
            self._width_line.setEnabled(False)
            self._height_line.setEnabled(False)
            self._aspect_link_button.setEnabled(False)
        else:
            self.set_resizers_enabled(True)
            self._lock_button.setIcon(self._unlock_icon)
            self._lock_button.setChecked(False)
            self._width_line.setEnabled(True)
            self._height_line.setEnabled(True)
            self._aspect_link_button.setEnabled(True)

    def _on_window_resize(self):
        """
        Internal callback function that is called when window is resized in width or through the corners
        """

        if self._keep_aspect:
            self.window().resize(self.window().width(), self.window().width() + self._get_bottom_size())
        self._update_widgets()

    def _on_vertical_resize(self):
        """
        Internal callback function that is called when window is resized vertically
        """

        if self._keep_aspect:
            self.window().resize(self.window().height() - self._get_bottom_size(), self.window().height())
        self._update_widgets()

    def _on_size_edit_changed(self):
        """
        Internal callback function that is called when height/width line edit values are changed
        """

        if self._locked or (self.sender() is not None and self.sender().signalsBlocked()):
            return

        width = int(self._width_line.text())
        height = int(self._height_line.text())
        self.set_snapshot_size(width, height)


class SnapshotFrame(QFrame, object):
    pass
