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

This widget does not perform the actual actions but it emits signals
that should be connected to the final controller on the main window.
"""

# yapf: disable

from __future__ import absolute_import, division, print_function

# Standard library imports
import os
import sys

# Third party imports
from qtpy.QtCore import Qt, Signal
from qtpy.QtGui import QPixmap
from qtpy.QtWidgets import QHBoxLayout, QLabel, QProgressBar, QVBoxLayout

# Local imports
from anaconda_navigator.api.anaconda_api import AnacondaAPI
from anaconda_navigator.static import images
from anaconda_navigator.utils import constants as C
from anaconda_navigator.utils.styles import load_style_sheet
from anaconda_navigator.widgets import (ButtonNormal, ComboBoxBase,
                                        FrameTabContent, FrameTabFooter,
                                        FrameTabHeader, LabelBase,
                                        SpacerHorizontal, WidgetBase)
from anaconda_navigator.widgets.lists.apps import (ListItemApplication,
                                                   ListWidgetApplication)


# yapf: enable


# --- Custom widgets used with CSS styling
# -----------------------------------------------------------------------------
class ButtonHomeRefresh(ButtonNormal):
    """QFrame used for CSS styling refresh button inside the Home Tab."""


class ComboHomeEnvironment(ComboBoxBase):
    """Widget Used for CSS styling."""


class ButtonHomeChannels(ButtonNormal):
    """Widget Used for CSS styling."""


class LabelHome(LabelBase):
    """QLabel used for CSS styling the Home Tab label."""

    pass


# --- Main widget
# -----------------------------------------------------------------------------
class HomeTab(WidgetBase):
    """Home applications tab."""

    sig_pointer_updated = Signal(object, object)
    sig_status_updated = Signal(object, object, object, object)
    sig_application_updated = Signal(object, object)

    # button_widget, sender
    sig_channels_dialog_requested = Signal(object, object)

    # application_name, command, prefix, leave_path_alone, sender
    sig_launch_action_requested = Signal(object, object, bool, object, object)

    # action, application_name, version, sender
    sig_conda_action_requested = Signal(object, object, object, object)

    # name, prefix, sender
    sig_environment_changed = Signal(object, object, object)

    sig_environment_updated = Signal()

    # url
    sig_url_clicked = Signal(object)

    # TODO: Connect these signals to have more granularity
    # [{'name': package_name, 'version': version}...], sender
    sig_install_action_requested = Signal(object, object)
    sig_remove_action_requested = Signal(object, object)

    def __init__(self, parent=None):
        """Home applications tab."""
        super(HomeTab, self).__init__(parent)

        # Variables
        self._parent = parent
        self.api = AnacondaAPI()
        self.setObjectName('Tab')
        self.style_sheet = None

        # Widgets
        main_icon = QLabel()
        self.list_applications = ListWidgetApplication()
        self.apps = None
        self.app_timers = None
        self.button_channels = ButtonHomeChannels('Channels')
        self.button_refresh = ButtonHomeRefresh('Refresh')
        self.combo_environments = ComboHomeEnvironment()
        self.frame_home_top = FrameTabHeader(self)
        self.frame_home_body = FrameTabContent(self)
        self.frame_home_bottom = FrameTabFooter(self)
        self.label_home = LabelHome('')
        self.label_status_action = QLabel('')
        self.label_status = QLabel('')
        self.progress_bar = QProgressBar()
        self.first_widget = self.combo_environments

        # Widget setup
        self.progress_bar.setTextVisible(False)
        pixmap = QPixmap(images.ANACONDA_ICON_128_PATH)
        main_icon.setPixmap(pixmap)
        main_text = ('Applications on')
        self.label_home.setText(main_text)
        self.list_applications.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)

        # Layout
        layout_top = QHBoxLayout()
        layout_top.addWidget(self.label_home)
        layout_top.addWidget(SpacerHorizontal())
        layout_top.addWidget(self.combo_environments)
        layout_top.addWidget(SpacerHorizontal())
        layout_top.addWidget(self.button_channels)
        layout_top.addWidget(SpacerHorizontal())
        layout_top.addStretch()
        layout_top.addWidget(self.button_refresh)
        self.frame_home_top.setLayout(layout_top)

        layout_body = QVBoxLayout()
        layout_body.addWidget(self.list_applications)
        self.frame_home_body.setLayout(layout_body)

        layout_bottom = QHBoxLayout()
        layout_bottom.addWidget(self.label_status_action)
        layout_bottom.addWidget(SpacerHorizontal())
        layout_bottom.addWidget(self.label_status)
        layout_bottom.addStretch()
        layout_bottom.addWidget(self.progress_bar)
        self.frame_home_bottom.setLayout(layout_bottom)

        layout = QVBoxLayout()
        layout.addWidget(self.frame_home_top)
        layout.addWidget(self.frame_home_body)
        layout.addWidget(self.frame_home_bottom)
        self.setLayout(layout)

        # Signals
        self.list_applications.sig_conda_action_requested.connect(
            self.sig_conda_action_requested
        )
        self.list_applications.sig_url_clicked.connect(self.sig_url_clicked)
        self.list_applications.sig_launch_action_requested.connect(
            self.sig_launch_action_requested
        )
        self.button_channels.clicked.connect(self.show_channels_dialog)
        self.button_refresh.clicked.connect(self.refresh_cards)
        self.progress_bar.setVisible(False)

        # Setup
        self.set_environments()  # Set the envs combobox and hook signal

    # --- Helpers
    # -------------------------------------------------------------------------
    def _environment_updated(self, index):
        """Notify that the environment changed."""
        selected_env = self.combo_environments.itemText(index)
        environments = self.api.environments
        for env in environments:
            if env == selected_env:
                prefix = environments[env]
                break

        if prefix:
            self.sig_environment_changed.emit(
                selected_env, prefix, C.HOME_APPLICATION
            )
            self.setup_tab(prefix=prefix)

    def update_style_sheet(self, style_sheet=None):
        """Update custom CSS style sheet."""
        if style_sheet is None:
            self.style_sheet = load_style_sheet()
        else:
            self.style_sheet = style_sheet

        self.list_applications.update_style_sheet(style_sheet=self.style_sheet)
        self.setStyleSheet(self.style_sheet)

    def refresh_cards(self):
        """Refresh application widgets.

        List widget items sometimes are hidden on resize. This method tries
        to compensate for that refreshing and repainting on user demand.
        """
        self.list_applications.update_style_sheet()
        self.list_applications.repaint()
        for item in self.list_applications.items():
            if not item.widget.isVisible():
                item.widget.repaint()

    # --- Public api
    # -------------------------------------------------------------------------
    def set_environments(self, prefix=None):
        """Setup the environments list."""
        try:
            self.combo_environments.currentIndexChanged.disconnect()
        except TypeError:
            pass

        self.combo_environments.clear()
        environments = self.api.environments

        for env in environments:
            self.combo_environments.addItem('{0}'.format(env))

        if prefix is None:
            prefix = os.environ.get('CONDA_PREFIX', self.api.ROOT_PREFIX)

        index = 0
        if prefix:
            for i, env in enumerate(environments):
                if prefix == environments[env]:
                    index = i
                    break

        self.combo_environments.setCurrentIndex(index)
        self.combo_environments.currentIndexChanged.connect(
            self._environment_updated
        )

    def setup_tab(self, apps=None, prefix=None):
        """Setup the tab content."""
        if apps:
            self.apps = apps

        if prefix is None:
            prefix = self.api.ROOT_PREFIX

        self.set_environments(prefix=prefix)
        self.set_applications(prefix=prefix)
        self.set_widgets_enabled(True)
        self.sig_environment_updated.emit()
        self.update_style_sheet()

    def set_applications(self, prefix):
        """Build the list of applications present in the current conda env."""
        # Prevent problems if self.apps has not been defined
        apps = self.apps if self.apps is not None else {}

        all_applications = []
        installed_applications = []
        not_installed_applications = []

        # Check if some installed applications are not on the apps dict
        # for example when the channel was removed.
        linked_apps = self.api.conda_linked_apps_info(prefix)
        missing_apps = [app for app in linked_apps if app not in apps]
        for app in missing_apps:
            apps[app] = linked_apps[app]

        for app_name in sorted(list(apps.keys())):
            app = apps[app_name]
            item = ListItemApplication(
                name=app['name'],
                description=app['description'],
                versions=app['versions'],
                command=app['command'],
                image_path=app['image_path'],
                prefix=prefix,
                needs_license=app.get('needs_license', False)
            )
            if item.installed:
                installed_applications.append(item)
            else:
                not_installed_applications.append(item)

        all_applications = installed_applications + not_installed_applications

        self.list_applications.clear()
        for i in all_applications:
            self.list_applications.addItem(i)

    @property
    def last_widget(self):
        if self.list_applications.items():
            return self.list_applications.items()[-1].widget

    def show_channels_dialog(self):
        """TODO:."""
        self.sig_channels_dialog_requested.emit(
            self.button_channels,
            C.HOME_APPLICATION,
        )

    def update_list(self, name=None, version=None):
        """Update applications list."""
        self.set_applications()
        self.label_status.setVisible(False)
        self.label_status_action.setVisible(False)
        self.progress_bar.setVisible(False)

    def update_status(self, action=None, status=None, progress=0, max_value=0):
        """Update the application action status."""
        visible = bool(action)
        self.label_status_action.setVisible(visible)
        self.label_status.setVisible(visible)
        self.progress_bar.setVisible(visible)

        if action:
            self.label_status_action.setText(action)
            self.label_status.setText(status)
            self.progress_bar.setMaximum(max_value)
            self.progress_bar.setValue(progress)

            for item in self.list_applications._items:
                item.widget.setVisible(True)
                item.setHidden(False)
                item.widget.repaint()

    def update_versions(self, apps=None):
        """Update applications versions."""
        self.items = []

        for i in range(self.list_applications.count()):
            item = self.list_applications.item(i)
            self.items.append(item)
            if isinstance(item, ListItemApplication):
                name = item.name
                meta = apps.get(name)
                if meta:
                    versions = meta['versions']
                    version = self.api.get_dev_tool_version(item.path)
                    item.update_versions(version, versions)

    def set_widgets_enabled(self, value):
        """Enable or disable widgets."""
        self.combo_environments.setEnabled(value)
        self.button_channels.setEnabled(value)
        self.button_refresh.setEnabled(value)
        for item in self.list_applications.items():
            item.button_install.setEnabled(value)
            item.button_options.setEnabled(value)

            if value:
                item.set_loading(not value)

    def current_environment(self):
        """Return the current selected environment."""
        env_name = self.combo_environments.currentText()
        return self.api.conda_get_prefix_envname(env_name)

    def ordered_widgets(self, next_widget=None):
        """Return a list of the ordered widgets."""
        ordered_widgets = [
            self.combo_environments,
            self.button_channels,
            self.button_refresh,
        ]
        ordered_widgets += self.list_applications.ordered_widgets()

        return ordered_widgets


# --- Local testing
# -----------------------------------------------------------------------------
def test():  # pragma: no cover
    """Run local test."""
    from anaconda_navigator.utils.qthelpers import qapplication

    app = qapplication()
    widget = HomeTab()
    apps = {}
    for i in range(6):
        name = "Package {0}".format(i)
        application = dict(
            name=name,
            description="Scientific PYthon Development EnviRonment",
            versions=[str(i), str(i + 1), str(i + 2)],
            command=None,
            image_path=None
        )
        apps[name] = application
    widget.setup_tab(apps)
    widget.update_style_sheet()
    widget.showMaximized()
    sys.exit(app.exec_())


if __name__ == "__main__":  # pragma: no cover
    test()
