# -*- coding: utf-8 -*-
"""
OnionShare | https://onionshare.org/

Copyright (C) 2016 Micah Lee <micah@micahflee.com>

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 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""
import os
from PyQt5 import QtCore, QtWidgets, QtGui

from onionshare import strings, helpers

class FileList(QtWidgets.QListWidget):
    """
    The list of files and folders in the GUI.
    """
    files_dropped = QtCore.pyqtSignal()
    files_updated = QtCore.pyqtSignal()

    def __init__(self, parent=None):
        super(FileList, self).__init__(parent)
        self.setAcceptDrops(True)
        self.setIconSize(QtCore.QSize(32, 32))
        self.setSortingEnabled(True)

        class DropHereLabel(QtWidgets.QLabel):
            """
            When there are no files or folders in the FileList yet, display the
            'drop files here' message and graphic.
            """
            def __init__(self, parent, image=False):
                self.parent = parent
                super(DropHereLabel, self).__init__(parent=parent)
                self.setAcceptDrops(True)
                self.setAlignment(QtCore.Qt.AlignCenter)

                if image:
                    self.setPixmap(QtGui.QPixmap.fromImage(QtGui.QImage(helpers.get_resource_path('images/drop_files.png'))))
                else:
                    self.setText(strings._('gui_drag_and_drop', True))
                    self.setStyleSheet('color: #999999;')

                self.hide()

            def dragEnterEvent(self, event):
                self.parent.drop_here_image.hide()
                self.parent.drop_here_text.hide()
                event.ignore()

        self.drop_here_image = DropHereLabel(self, True)
        self.drop_here_text = DropHereLabel(self, False)

        self.filenames = []
        self.update()

    def update(self):
        """
        Update the GUI elements based on the current state.
        """
        # file list should have a background image if empty
        if len(self.filenames) == 0:
            self.drop_here_image.show()
            self.drop_here_text.show()
        else:
            self.drop_here_image.hide()
            self.drop_here_text.hide()

    def resizeEvent(self, event):
        """
        When the widget is resized, resize the drop files image and text.
        """
        self.drop_here_image.setGeometry(0, 0, self.width(), self.height())
        self.drop_here_text.setGeometry(0, 0, self.width(), self.height())

    def dragEnterEvent(self, event):
        """
        dragEnterEvent for dragging files and directories into the widget.
        """
        if event.mimeData().hasUrls:
            event.accept()
        else:
            event.ignore()

    def dragLeaveEvent(self, event):
        """
        dragLeaveEvent for dragging files and directories into the widget.
        """
        event.accept()
        self.update()

    def dragMoveEvent(self, event):
        """
        dragMoveEvent for dragging files and directories into the widget.
        """
        if event.mimeData().hasUrls:
            event.setDropAction(QtCore.Qt.CopyAction)
            event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):
        """
        dropEvent for dragging files and directories into the widget.
        """
        if event.mimeData().hasUrls:
            event.setDropAction(QtCore.Qt.CopyAction)
            event.accept()
            for url in event.mimeData().urls():
                filename = str(url.toLocalFile())
                self.add_file(filename)
        else:
            event.ignore()
        self.files_dropped.emit()

    def add_file(self, filename):
        """
        Add a file or directory to this widget.
        """
        if filename not in self.filenames:
            self.filenames.append(filename)

            fileinfo = QtCore.QFileInfo(filename)
            basename = os.path.basename(filename.rstrip('/'))
            ip = QtWidgets.QFileIconProvider()
            icon = ip.icon(fileinfo)

            if os.path.isfile(filename):
                size = helpers.human_readable_filesize(fileinfo.size())
            else:
                size = helpers.human_readable_filesize(helpers.dir_size(filename))
            item_name = '{0:s} ({1:s})'.format(basename, size)
            item = QtWidgets.QListWidgetItem(item_name)
            item.setToolTip(size)

            item.setIcon(icon)
            self.addItem(item)

            self.files_updated.emit()


class FileSelection(QtWidgets.QVBoxLayout):
    """
    The list of files and folders in the GUI, as well as buttons to add and
    delete the files and folders.
    """
    def __init__(self):
        super(FileSelection, self).__init__()
        self.server_on = False

        # file list
        self.file_list = FileList()
        self.file_list.currentItemChanged.connect(self.update)
        self.file_list.files_dropped.connect(self.update)

        # buttons
        self.add_files_button = QtWidgets.QPushButton(strings._('gui_add_files', True))
        self.add_files_button.clicked.connect(self.add_files)
        self.add_dir_button = QtWidgets.QPushButton(strings._('gui_add_folder', True))
        self.add_dir_button.clicked.connect(self.add_dir)
        self.delete_button = QtWidgets.QPushButton(strings._('gui_delete', True))
        self.delete_button.clicked.connect(self.delete_file)
        button_layout = QtWidgets.QHBoxLayout()
        button_layout.addWidget(self.add_files_button)
        button_layout.addWidget(self.add_dir_button)
        button_layout.addWidget(self.delete_button)

        # add the widgets
        self.addWidget(self.file_list)
        self.addLayout(button_layout)

        self.update()

    def update(self):
        """
        Update the GUI elements based on the current state.
        """
        # all buttons should be disabled if the server is on
        if self.server_on:
            self.add_files_button.setEnabled(False)
            self.add_dir_button.setEnabled(False)
            self.delete_button.setEnabled(False)
        else:
            self.add_files_button.setEnabled(True)
            self.add_dir_button.setEnabled(True)

            # delete button should be disabled if item isn't selected
            current_item = self.file_list.currentItem()
            if not current_item:
                self.delete_button.setEnabled(False)
            else:
                self.delete_button.setEnabled(True)

        # update the file list
        self.file_list.update()

    def add_files(self):
        """
        Add files button clicked.
        """
        filenames = QtWidgets.QFileDialog.getOpenFileNames(
            caption=strings._('gui_choose_files', True), options=QtWidgets.QFileDialog.ReadOnly)
        if filenames:
            for filename in filenames[0]:
                self.file_list.add_file(filename)
        self.update()

    def add_dir(self):
        """
        Add folder button clicked.
        """
        filename = QtWidgets.QFileDialog.getExistingDirectory(
            caption=strings._('gui_choose_folder', True), options=QtWidgets.QFileDialog.ReadOnly)
        if filename:
            self.file_list.add_file(str(filename))
        self.update()

    def delete_file(self):
        """
        Delete button clicked
        """
        current_row = self.file_list.currentRow()
        self.file_list.filenames.pop(current_row)
        self.file_list.takeItem(current_row)
        self.update()

    def server_started(self):
        """
        Gets called when the server starts.
        """
        self.server_on = True
        self.file_list.setAcceptDrops(False)
        self.update()

    def server_stopped(self):
        """
        Gets called when the server stops.
        """
        self.server_on = False
        self.file_list.setAcceptDrops(True)
        self.update()

    def get_num_files(self):
        """
        Returns the total number of files and folders in the list.
        """
        return len(self.file_list.filenames)
