# -*- coding: utf-8 -*-
#
# Copyright 2016 Continuum Analytics, Inc.
# May be copied and distributed freely only as part of an Anaconda or
# Miniconda installation.
#

"""
"""

# Standard library imports
import base64
import gettext
import os
import shutil
import subprocess

# Third party imports
from conda_manager.widgets.packages import CondaPackagesWidget
from qtpy.compat import getopenfilename, getexistingdirectory
from qtpy.QtCore import Qt, QTimer, Signal
from qtpy.QtWidgets import (QApplication, QHBoxLayout, QListWidgetItem,
                            QPushButton, QTabWidget, QVBoxLayout, QWidget)
import qtawesome as qta

# Local imports
from anaconda_navigator.api import AnacondaAPI
from anaconda_navigator.config import CHANNELS_PATH, DEFAULT_PROJECT_PATH
from anaconda_navigator.widgets.dialogs.environment import (CloneEnvironmentDialog,
                                                            CreateEnvironmentDialog,
                                                            RemoveEnvironmentDialog)
from anaconda_navigator.widgets.explorer import ExplorerWidget
from anaconda_navigator.widgets.list import EditableList
from anaconda_navigator.widgets.helperwidgets import (EditableLineEdit,
                                                      EditablePathEdit)
from anaconda_navigator.config import get_home_dir
from anaconda_navigator.config.main import CONF
from anaconda_navigator.utils.launch import (launch, run_notebook,
                                             run_python_console,
                                             run_python_file)
from anaconda_navigator.utils.qthelpers import update_pointer
from anaconda_navigator.utils.logs import logger


_ = gettext.gettext


# --- Project widget tabs
# -----------------------------------------------------------------------------
class ExplorerTab(QWidget):
    """
    File explorer tab widget.
    """
    def __init__(self):
        super(ExplorerTab, self).__init__()
        self.explorer = ExplorerWidget(show_all=True)
        self.treewidget = self.explorer.treewidget

        layout = QVBoxLayout()
        layout.addWidget(self.explorer)
        self.setLayout(layout)


class PackagesTab(QWidget):
    """
    Packages, environments and commands tab widget.
    """
    sig_status_updated = Signal(object, object, object, object)
    sig_updated = Signal(object, object)
    sig_project_commands_updated = Signal(object, object)
    sig_apps_updated = Signal()

    def __init__(self):
        super(PackagesTab, self).__init__()

        self.api = AnacondaAPI()
        self.path = None
        self.project = None

        # Widgets
        self.list_environments = EditableList(title='Environments')
        self.list_commands = EditableList(title='Commands', min_items=0)
        self.package_manager = CondaPackagesWidget(
            self,
            setup=False,
            data_directory=CHANNELS_PATH,
            )

        # Widget setup
        self.list_environments.button_add.clicked.disconnect()
        self.list_environments.button_edit.setVisible(False)

        # Layout
        top_layout = QHBoxLayout()
        top_layout.addWidget(self.list_environments)
        top_layout.addStretch()
        top_layout.addWidget(self.list_commands)
        top_layout.addStretch()

        main_layout = QVBoxLayout()
        main_layout.addLayout(top_layout)
        main_layout.addWidget(self.package_manager)
        self.setLayout(main_layout)

        # Signals
        self.list_commands.sig_item_removed.connect(self.edit_commands)
        self.list_commands.sig_item_edited.connect(self.add_command)
        self.list_commands.sig_item_selected.connect(self.select_command)

        self.list_environments.button_add.clicked.connect(self.add_environment)
        self.list_environments.sig_item_removed.connect(
            self.remove_environment)
        self.list_environments.sig_item_selected.connect(
            self.choose_environment)

        self.package_manager.sig_channels_updated.connect(self.set_channels)

    def load_information(self, path, project=None):
        """
        """
        self.path = path

        if project is None:
            project = self.api.load_project(path)

        logger.debug(str((path, project.name)))
        self.project = project

        # Load commands
        commands = project.commands
        self.list_commands.clear()

        for command in commands:
            item = QListWidgetItem(command)
            self.list_commands.add(item)
        self.list_commands.setCurrentRow(0)

        # Load environments
        environments = project.environments
        self.list_environments.clear()

        selected_row = 0
        for i, environment in enumerate(environments):
            item = QListWidgetItem(environment)
            self.list_environments.add(item)
            if environment == project.default_environment:
                selected_row = i
        self.list_environments.setCurrentRow(selected_row)

        # Load channels
        self.package_manager.set_environment(prefix=project.env_prefix(path))
        self.package_manager.update_channels(project.default_channels,
                                             project.default_channels)

    def set_channels(self, channels, active_channels):
        """
        """
        logger.debug(channels)
        self.project.default_channels = list(active_channels)
        self.project.save(self.path)

    # --- Environments
    # -------------------------------------------------------------------------
    def add_environment(self, text=None, pyver=None):
        """
        Create new basic environment with selectable python version.

        Actually makes new env on disc, in directory within the project
        whose name depends on the env name. New project state is saved.
        Should also sync to spec file.
        """
        dlg = CreateEnvironmentDialog(parent=self,
                                      envs=self.project.environments)

        if dlg.exec_():
            text = dlg.text_name.text().strip()
            pyver = dlg.combo_version.currentText()

        if text:
            self.setEnabled(False)
            QApplication.setOverrideCursor(Qt.BusyCursor)

            # FIXME: This does not seem like a very safe way. What if it fails?
            self.project.default_environment = text
            self.project.environments[text] = "{0}.yaml".format(text)
            prefix = self.project.env_prefix(self.path)

            if pyver:
                pkgs = ['python=' + pyver, 'jupyter']
            else:
                pkgs = ['jupyter']

            channels = self.project.default_channels
            logger.debug(str((prefix, pkgs, channels)))
            worker = self.api.conda_create(prefix=prefix, pkgs=pkgs,
                                           channels=channels)
            worker.text = text
            worker.sig_finished.connect(self._environment_added)

    def _environment_added(self, worker, output, error):
        """
        Callback for worker after envrionemnt creation has finished.
        """
        text = worker.text
        item = QListWidgetItem(text)
        self.list_environments.add(item)
        self.list_environments.setCurrentRow(len(self.project.environments)-1)
        self.setEnabled(True)
        self.project.save(self.path)
        self.choose_environment()
        QApplication.restoreOverrideCursor()

    def remove_environment(self, item):
        """
        Delete environment from project and project directory.
        """
        text = item.text()
        self.project.default_environment = text
        path = self.project.env_prefix(self.path)
        logger.debug(str((path, text)))
        shutil.rmtree(path)
        self.project.environments.pop(text)
        self.project.save(self.path)
        self.choose_environment()

    def choose_environment(self):
        """
        The user has picked a different environment.

        Refresh the package list and save the new state of the project.
        """
        current_item_text = self.list_environments.currentItem().text()
        logger.debug(current_item_text)
        self.project.default_environment = current_item_text
        self.project.save(self.path)
        prefix = self.project.env_prefix(self.path)
        self.package_manager.set_environment(prefix=prefix)
        self.package_manager.setup()

    # --- Commands
    # -------------------------------------------------------------------------
    def add_command(self, command=None):
        """
        Add a new command in the commands list. Adds to the bottom of the list.
        """
        if command:
            item = QListWidgetItem(command)
            self.list_commands.add(item)

        commands = [self.list_commands.item(i).text() for i in
                    range(self.list_commands.count())]
        self.project.commands = commands
        self.project.save(self.path)
        self.sig_project_commands_updated.emit(self.path,
                                               self.project.commands)
        logger.debug(self.project.commands)

    def edit_commands(self):
        """
        """
        commands = [self.list_commands.item(i).text() for i in
                    range(self.list_commands.count())]
        self.project.commands = commands
        self.project.save(self.path)
        self.sig_project_commands_updated.emit(self.path,
                                               self.project.commands)
        logger.debug(self.project.commands)

    def select_command(self):
        """
        """
        self.sig_apps_updated.emit()
        logger.debug(self.project.commands)


class AdvancedTab(QWidget):
    """
    Advanced tab widget. Cluster, Auth, Services.
    """

    def __init__(self):
        super(AdvancedTab, self).__init__()

        # Widgets
        self.button_cluster = QPushButton(qta.icon('fa.sitemap'), 'Cluster')
        self.button_service = QPushButton(qta.icon('fa.database'), 'Services')
        self.button_auth = QPushButton(qta.icon('fa.lock'), 'Auth')
        self.widget = QWidget()

        # Layouts
        buttons_layout = QVBoxLayout()
        buttons_layout.addWidget(self.button_cluster)
        buttons_layout.addWidget(self.button_service)
        buttons_layout.addWidget(self.button_auth)
        buttons_layout.addStretch()

        layout = QHBoxLayout()
        layout.addLayout(buttons_layout)
        layout.addStretch()

        main_layout = QVBoxLayout()
        main_layout.addWidget(self.widget)

        self.widget.setLayout(layout)
        self.setLayout(main_layout)

        # Signals
        self.button_auth.clicked.connect(self.ask_havoc)
        self.button_service.clicked.connect(self.ask_havoc)
        self.button_cluster.clicked.connect(self.ask_havoc)

    def ask_havoc(self):
        logger.debug("")
        dlg = AskHavoc()
        dlg.exec_()


# --- Main Project Widget
# -----------------------------------------------------------------------------
class ProjectWidget(QWidget):
    """
    Project information widget.
    """
    sig_apps_updated = Signal()
    sig_project_updated = Signal()
    sig_project_commands_updated = Signal(object, object)
    sig_project_name_updated = Signal(object, object, object)
    sig_project_icon_updated = Signal(object, object, object)
    sig_status_updated = Signal(object, object, object, object)

    def __init__(self, parent=None):
        super(ProjectWidget, self).__init__(parent)

        # Variables
        self.api = AnacondaAPI()
        self._parent = parent
        self.path = None
        self.project = None
        self.timer = QTimer()
        self.timeout = 6000

        # Widgets
        self.text_name = EditableLineEdit('Name', '')
        self.text_location = EditablePathEdit('Location', '',
                                              caption="Select new project "
                                                      "directory",
                                              basedir=get_home_dir())
        self.text_icon = EditablePathEdit('Icon', '', fileselect=True,
                                          caption='Select icon image file',
                                          filters="Image files (*.png *.jpg *.jpeg)",
                                          basedir=get_home_dir())
        self.button_launch = QPushButton('Launch')
        self.button_upload = QPushButton('Upload to Anaconda Cloud')

        self.tabs = QTabWidget(parent=self)
        self.tab_packages = PackagesTab()
#        self.tab_advanced = AdvancedTab()
        self.tab_explorer = ExplorerTab()

        # Widget Setup
        self.setWindowTitle('Project Editor')
        self.button_upload.setObjectName('ButtonUpdate')
        self.button_launch.setObjectName('ButtonUpdate')
        self.tabs.addTab(self.tab_explorer, 'Files')
        self.tabs.addTab(self.tab_packages, 'Packages')
#        self.tabs.addTab(self.tab_advanced, 'Advanced options')
        self.text_icon._text.setVisible(False)

        # Layouts
        project_buttons = QHBoxLayout()
        project_buttons.addWidget(self.button_upload)
#        project_buttons.addWidget(self.button_launch)
        project_buttons.addStretch()

        information_layout = QHBoxLayout()
        information_layout.addWidget(self.text_name, 2)
        information_layout.addWidget(self.text_location, 4)
        information_layout.addWidget(self.text_icon, 1)

        main_layout = QVBoxLayout()
#        main_layout.addLayout(project_buttons, 1)
#        main_layout.addSpacing(6)
        main_layout.addLayout(information_layout, 1)
        main_layout.addWidget(self.tabs, 10)
        main_layout.addWidget(self.button_launch, 0, Qt.AlignRight)

        self.setLayout(main_layout)

        # Signals
        self.button_launch.clicked.connect(self.launch)

        self.tab_explorer.explorer.sig_home_clicked.connect(
            self.set_explorer_path)
        self.tab_explorer.treewidget.sig_edit.connect(self.edit_file)
        self.tab_explorer.treewidget.sig_open_py.connect(self.run_python_file)
        self.tab_explorer.treewidget.sig_open_py_con.connect(
            self.run_python_console)
        self.tab_explorer.treewidget.sig_open_notebook.connect(
            self.run_notebook)
        self.tab_explorer.treewidget.sig_add_endpoint.connect(
            self.make_command_from_file)

        self.tab_packages.sig_project_commands_updated.connect(
            self.sig_project_commands_updated)
        self.tab_packages.sig_apps_updated.connect(self.sig_apps_updated)

        self.timer.timeout.connect(self._launched)
        self.text_location.sig_text_changed.connect(self.update_location)
        self.text_name.sig_text_changed.connect(self.update_name)
        self.text_icon.sig_text_changed.connect(self.update_icon)

        # Setup
        self.button_upload.setDisabled(True)
        self.button_upload.setVisible(False)

    # --- Global methods
    # -------------------------------------------------------------------------
    def project_updated(self, environments, commands):
        """
        """
        self.sig_project_updated.emit()

    def update_status(self, message='', timeout=0):
        """
        Update project tab status.
        """
        self.sig_status_updated.emit(message, timeout, -1, -1)

    def load_project_information(self, path, project=None):
        """
        Load project information on project widget.
        """
        if project is None:
            project = self.api.load_project(path)
        self.path = path
        self.project = project

        self.text_name.setText(project.name)
        self.text_location.setText(path)

        self.set_explorer_path(path)
        self.tab_packages.load_information(path)

        self.text_location.button_edit.setVisible(False)  # FIXME: Temporal!
        if self.is_default():
            self.button_launch.setDisabled(True)
            self.text_name.button_edit.setDisabled(True)
            self.text_icon.button_edit.setDisabled(True)
            self.text_location.button_edit.setDisabled(True)
            self.tab_packages.list_commands.setVisible(False)
            self.tab_packages.list_environments.setVisible(False)
        else:
            if self.tab_packages.list_commands.count():
                self.button_launch.setDisabled(False)
            else:
                self.button_launch.setDisabled(True)
            self.text_icon.button_edit.setDisabled(False)
            self.text_name.button_edit.setDisabled(False)
            self.text_location.button_edit.setDisabled(True)
            self.tab_packages.list_commands.setVisible(True)
            self.tab_packages.list_environments.setVisible(True)

    def update_name(self, old_name, new_name):
        """
        Update project name.
        """
        if self.project is not None:
            self.project.name = new_name
            logger.debug(new_name)
            self.project.save(self.path)
            self.sig_project_name_updated.emit(self.path, old_name, new_name)

    def update_location(self, old_path, new_path):
        """
        FIXME: To be implemented
        """
        path = getexistingdirectory(caption="New location", basedir=self.path)
        logger.debug(path)
        if path:
            try:
                if os.path.exists(path):
                    os.rmdir(path)
                shutil.move(self.path, path)
                projects_path = CONF.get('main', 'projects_path', [])
                projects_path.remove(self.path)
                projects_path.append(path)
                CONF.set('main', 'projects_path', projects_path)
                self.project.path = path
                self.path = path
                self.sig_project_updated.emit()
            except Exception as e:
                print(e)

    def update_icon(self, old_image_path, new_image_path):
        """
        Update project icon/image.
        """
        self.project.icon = 'icon.png'
        logger.debug(new_image_path)
        self.project.save(self.path)
        self.sig_project_icon_updated.emit(self.path, old_image_path,
                                           new_image_path)

    def launch(self):
        """
        Execute the selected command in the current project.
        """
        list_commands = self.tab_packages.list_commands
        if list_commands.count() != 0:
            self.button_launch.setDisabled(True)
            self.timer.setInterval(self.timeout)
            self.timer.start()
            update_pointer(Qt.BusyCursor)
            command = list_commands.currentItem().text()
            logger.debug(str((self.path, command)))
            launch(self.path, command)

    def _launched(self):
        """
        """
        self.button_launch.setDisabled(False)
        update_pointer()

    def select_icon(self):
        """
        """
        out = getopenfilename(caption='Select icon',
                              filters='PNG Image (*.png)',
                              )
        logger.debug(str(out))
        if out[0]:
            with open(out[0], 'rb') as f:
                image = f.read()

            self.project.icon = base64.b64encode(image)
            self.project.save(self.path)
            self.load_icon()

    def is_default(self):
        """
        Check if current project is the default project.
        """
        return (self.text_name.text() == 'default' and
                self.path == DEFAULT_PROJECT_PATH)

    # --- Explorer Commands
    # -------------------------------------------------------------------------
    def set_explorer_path(self, path=None):
        """
        """
        path = path or self.path
        self.tab_explorer.treewidget.set_current_folder(path)

    def edit_file(self, filename=""):
        """
        Open file with spyder.
        """
        logger.debug(filename)
        subprocess.Popen(['spyder', filename])

    def run_notebook(self, filename=""):
        """
        Start notebook server.
        """
        logger.debug(filename)
        run_notebook(self.path, self.project, filename)

    def run_python_file(self, filename=""):
        """
        Execute python in environment.
        """
        logger.debug(filename)
        run_python_file(self.path, self.project, filename)

    def run_python_console(self, filename=""):
        """
        Execute python in env with console.
        """
        logger.debug(filename)
        run_python_console(self.path, self.project, filename)

    def make_command_from_file(self, filename):
        """
        Add a command in the commands list based on the current file.
        """
        logger.debug(filename)
        bin_path = os.sep.join([self.project.env_prefix(self.path), 'bin'])

        if filename.endswith('.py'):
            cmd = 'python ' + filename.replace(self.path, '{PREFIX}')

        elif filename.endswith('.ipynb'):
            cmd = 'jupyter notebook ' + filename.replace(self.path, '{PREFIX}')

        elif (os.path.isfile(filename) and os.access(filename, os.X_OK) and
              bin_path in filename):
            new_bin_path = os.sep.join([self.project.env_prefix(self.path),
                                        'bin'])
            cmd = filename.replace(new_bin_path, '')
        else:
            return
        self.add_command(text=cmd)

    def upload_notebook(self, filepath):
        """
        """
        pass

    # --- Commands
    # -------------------------------------------------------------------------
    def add_command(self, text):
        """
        New endpoint: go into edit mode.
        """
        self.tab_packages.add_command(text)
        logger.debug(text)


def test():
    from anaconda_navigator.utils.qthelpers import qapplication
    app = qapplication()
    w = ProjectWidget(parent=None)
    w.show()
    app.exec_()


if __name__ == '__main__':
    test()
