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

"""
Main Application Window.
"""

# Standard library imports
from __future__ import absolute_import, division
import json
import os
import sys

# Third party imports
from qtpy.QtCore import QCoreApplication, QSize, Qt, Signal, QUrl
from qtpy.QtGui import QIcon, QPixmap, QPainter, QPen, QColor, QDesktopServices
from qtpy.QtWidgets import (QApplication, QFrame, QHBoxLayout, QLabel,
                            QMainWindow, QVBoxLayout, QWidget, QProgressBar)

# Local imports
from anaconda_navigator import images
from anaconda_navigator.api import AnacondaAPI
from anaconda_navigator.config import CHANNELS_PATH, CONF
from anaconda_navigator.utils.analytics import GATracker
from anaconda_navigator.utils.qthelpers import create_action
from anaconda_navigator.utils.styles import load_style_sheet
from anaconda_navigator.widgets import ButtonBase, ButtonLabel, ButtonLink
from anaconda_navigator.widgets.customtabbar import TabWidgetBody
from anaconda_navigator.widgets.dialogs import (MessageBoxError,
                                                MessageBoxInformation,
                                                )
from anaconda_navigator.widgets.dialogs.about import AboutDialog
from anaconda_navigator.widgets.dialogs.login import AuthenticationDialog
from anaconda_navigator.widgets.dialogs.preferences import PreferencesDialog
from anaconda_navigator.widgets.dialogs.update import DialogUpdateApplication
from anaconda_navigator.widgets.dialogs.quit import QuitApplicationDialog
from anaconda_navigator.widgets.logger import LogViewerDialog
from anaconda_navigator.widgets.tabs.community import CommunityTab
from anaconda_navigator.widgets.tabs.home import HomeTab
from anaconda_navigator.widgets.tabs.environments import EnvironmentsTab
from anaconda_navigator.utils.logs import logger


# --- Widgets used with CSS styling
# -----------------------------------------------------------------------------
class ButtonLinkLogin(ButtonLink):
    pass


class ButtonLabelLogin(ButtonLabel):
    pass


class ButtonHeaderUpdate(ButtonBase):
    pass


class ButtonLogin(ButtonBase):
    pass


class FrameHeader(QFrame):
    pass


class FrameBody(QFrame):
    pass


class LabelHeaderLogo(QLabel):
    pass


class LabelHeaderUpdate(QLabel):
    pass


# --- Main widget
# -----------------------------------------------------------------------------
class MainWindow(QMainWindow):
    sig_logged_in = Signal()
    sig_logged_out = Signal()

    DOCS_URL = 'https://docs.continuum.io/anaconda/navigator'
    VIDEOS_URL = "http://content.continuum.io/api/videos"
    EVENTS_URL = "http://content.continuum.io/api/events"
    WEBINARS_URL = "http://content.continuum.io/api/webinars"

    def __init__(self, splash=None):
        super(MainWindow, self).__init__()
        self.tracker = None
        self.splash = splash

        # Anaconda API
        self.api = AnacondaAPI()
        self.busy = False
        self.logged = False
        self.username = ''
        self._login_text = 'Sign in to Anaconda Cloud'
        self.first_run = CONF.get('main', 'first_run')
        self.application_update_version = None

        # Widgets
        self.frame_header = FrameHeader(self)
        self.frame_body = FrameBody(self)
        self.label_logo = LabelHeaderLogo('ANACONDA NAVIGATOR')
        self.button_logged_text = ButtonLabelLogin('')
        self.button_logged_username = ButtonLinkLogin('')
        self.label_update_available = LabelHeaderUpdate('Update available!')
        self.button_update_available = ButtonHeaderUpdate('Update')
        self.button_login = ButtonLogin(self._login_text)
        self.central_widget = QWidget()
        self.statusbar = self.statusBar()
        self.progressbar = QProgressBar()

        self.stack = TabWidgetBody(self)
        self.home_tab = HomeTab(parent=self)
        self.environments_tab = EnvironmentsTab(parent=self)
        self.learning_tab = CommunityTab(
            parent=self,
            tags=['webinar', 'documentation', 'video', 'training'],
            content_urls=[self.VIDEOS_URL, self.WEBINARS_URL]
            )
        self.community_tab = CommunityTab(
            parent=self,
            tags=['event', 'forum', 'social'],
            content_urls=[self.EVENTS_URL]
            )

#        self.projects_tab = ProjectsTab(parent=self)

        # Note: Icons are set in CSS
        self.stack.addTab(self.home_tab, text='Home')
        self.stack.addTab(self.environments_tab, text='Environments')
        self.stack.addTab(self.learning_tab, text='Learning')
        self.stack.addTab(self.community_tab, text='Community')
#        self.stack.addTab(self.projects_tab, 'Projects')

        # Widget setup
        self.button_login.setDefault(True)
        self.label_logo.setPixmap(QPixmap(images.ANACONDA_NAVIGATOR_LOGO))
        self.setWindowTitle("Anaconda Navigator")
        self.statusbar.addPermanentWidget(self.progressbar)
        self.progressbar.setVisible(False)

        # Layout
        header_layout = QHBoxLayout()
        header_layout.addWidget(self.label_logo)
        header_layout.addSpacing(18)
        header_layout.addWidget(self.label_update_available, 0, Qt.AlignCenter)
        header_layout.addWidget(self.button_update_available, 0,
                                Qt.AlignCenter)
        header_layout.addStretch()
        header_layout.addWidget(self.button_logged_text, 0, Qt.AlignTrailing)
        header_layout.addWidget(self.button_logged_username, 0,
                                Qt.AlignTrailing)
        header_layout.addWidget(self.button_login, 0, Qt.AlignTrailing)
        header_layout.setContentsMargins(0, 0, 0, 0)
        self.frame_header.setLayout(header_layout)

        body_layout = QHBoxLayout()
        body_layout.addWidget(self.stack)
        body_layout.setContentsMargins(0, 0, 0, 0)
        self.frame_body.setLayout(body_layout)

        main_layout = QVBoxLayout()
        main_layout.addWidget(self.frame_header)
        main_layout.addWidget(self.frame_body)
        main_layout.setContentsMargins(0, 0, 0, 0)
        main_layout.setSpacing(0)
        self.central_widget.setLayout(main_layout)
        self.setContentsMargins(0, 0, 0, 0)
        self.setCentralWidget(self.central_widget)

        # Signals
        self.button_login.clicked.connect(self.login)
        self.button_logged_username.clicked.connect(self.open_login_page)
        self.button_update_available.clicked.connect(self.update_application)
        self.stack.currentChanged.connect(self._track_tab)

        # This needs to be reworked!
#        self.projects_tab.sig_apps_updated.connect(
#            self.home_tab.set_applications)
#        self.projects_tab.sig_apps_changed.connect(
#            self.home_tab.set_applications)
#        self.projects_tab.sig_project_updated.connect(
#            self.home_tab.set_applications)
#        self.projects_tab.sig_status_updated.connect(self.update_status_bar)

        # Setup
        self.api.set_data_directory(CHANNELS_PATH)
        self.update_style_sheet()

    # Helpers
    # -------------------------------------------------------------------------
    def _track_tab(self, index=None):
        """
        Tracks the active tab by index, or set `Home` when no index has been
        provided.
        """
        if index is None:
            index = self.stack.currentIndex()

        text = self.stack.currentText().lower()

        if self.tracker:
            page = '/{0}'.format(text)
            self.tracker.track_page(page)

    def _metadata_updated(self, worker, path, error):
        self.set_splash('Updating repodata...')
        if error:
            logger.error(str(error))

        if path and os.path.isfile(path):
            with open(path, 'r') as f:
                data = f.read()
        try:
            self._metadata = json.loads(data)
        except Exception:
            self._metadata = {}

        channels = CONF.get('main', 'conda_channels', default=tuple())
        if not channels:
            channels = self.api.conda_get_condarc_channels()
            CONF.set('main', 'conda_channels', channels)
            CONF.set('main', 'conda_active_channels', channels)

        self.api.update_repodata(channels=channels)
        self.api.sig_repodata_updated.connect(self._repodata_updated)

    def _repodata_updated(self, paths):
        self.set_splash('Loading repodata...')
        self.api.sig_repodata_updated.disconnect(self._repodata_updated)

        if self.first_run:
            self.set_splash('Initial configuration...')
            self.api.create_default_project()

        worker = self.api.client_load_repodata(paths, self._metadata)
        worker.sig_finished.connect(self.create_application_projects)

    # --- Public API
    # -------------------------------------------------------------------------
    def setup(self):
        """
        Perform initial setup and configuration.
        """
        self.set_splash('Updating metadata...')
        user = self.api.client_set_domain()
        self.update_login_status(user)
        self.setup_toolbars()
        self.set_application_icon()
        worker = self.api.update_metadata()
        worker.sig_finished.connect(self._metadata_updated)
        statusbar = self.statusBar()
        statusbar.setVisible(False)
        statusbar.setMaximumHeight(0)
        statusbar.hide()

    def create_application_projects(self, worker, output, error):
        if error:
            logger.error(str(error))
        packages, apps = output
        self.api.create_application_projects(
            apps,
            add_project=self.first_run,
            )

        self.post_setup(apps)
        self.check_for_updates(packages)

    def post_setup(self, apps):
        CONF.set('main', 'first_run', False)
        self.set_splash('Loading applications...')
        self.home_tab.setup_tab(apps)
#        self.set_splash('Loading projects...')
#        self.projects_tab.setup_tab()
        self.set_splash('Loading environments...')
        self.environments_tab.setup_tab(metadata=self._metadata)
        self.set_splash('Loading content...')
        self.community_tab.setup_tab()
        self.set_splash('Loading content...')
        self.learning_tab.setup_tab()
        self.update_style_sheet()

        self.showMaximized()
        self.post_visible_setup()

    def set_application_icon(self):
        """
        """
        app = QCoreApplication.instance()
        app_icon = QIcon()
        app_icon.addFile(images.ANACONDA_ICON_16_PATH, QSize(16, 16))
        app_icon.addFile(images.ANACONDA_ICON_24_PATH, QSize(24, 24))
        app_icon.addFile(images.ANACONDA_ICON_32_PATH, QSize(32, 32))
        app_icon.addFile(images.ANACONDA_ICON_48_PATH, QSize(48, 48))
        app_icon.addFile(images.ANACONDA_ICON_256_PATH, QSize(256, 256))
        app.setWindowIcon(app_icon)

    def setup_toolbars(self):
        menubar = self.menuBar()

        file_menu = menubar.addMenu('&File')
        file_menu.addAction(create_action(self, "&Preferences",
                                          triggered=self.show_preferences,
                                          shortcut="Ctrl+P"))
        file_menu.addAction(create_action(self, "&Quit",
                                          triggered=self.close,
                                          shortcut="Ctrl+Q"))

        helpmenu = menubar.addMenu('&Help')
        helpmenu.addAction(create_action(self, "&Online Documentation",
                                         triggered=lambda:
                                             self.open_url(self.DOCS_URL)))
        helpmenu.addAction(create_action(self,
                                         "&Logs viewer",
                                         triggered=self.show_log_viewer,
                                         shortcut="F6"))
        helpmenu.addSeparator()
        helpmenu.addAction(create_action(self,
                                         "&About",
                                         triggered=self.show_about))

    def post_visible_setup(self):
        if self.splash:
            self.splash.hide()

        CONF.set('main', 'first_run', False)

        # Start the tracker only after post_visible_setup
        self.tracker = GATracker()
        self._track_tab(0)  # Start tracking home
        self.fix_tab_ordering()
        self.show_welcome_screen()

    def check_for_updates(self, packages=None):
        # Check if there is an update for navigator!
        version = self.api.conda_package_version(name='root',
                                                 pkg='anaconda-navigator')
        # Temporal mock test
        # mock_versions = [version, '1.1.0']
        # packages['anaconda-navigator'] = {'versions': mock_versions}
        self.button_update_available.setVisible(False)
        self.label_update_available.setVisible(False)

        text = ''
        if packages:
            package_data = packages.get('anaconda-navigator')
            if package_data:
                versions = package_data.get('versions')
                if versions and version != versions[-1]:
                    self.application_update_version = versions[-1]
                    self.label_update_available.setText(text)
                    self.label_update_available.setVisible(True)
                    self.button_update_available.setVisible(True)

    def fix_tab_ordering(self):
        return
        for tab in [self.community_tab, self.learning_tab]:
            self.setTabOrder(self.stack.tabbar.buttons[-1],
                             tab.filter_widgets[0])
            for i in range(len(tab.filter_widgets)-1):
                self.setTabOrder(tab.filter_widgets[i],
                                 tab.filter_widgets[i+1])
            self.setTabOrder(tab.filter_widgets[-1], tab.text_filter)
            self.setTabOrder(tab.text_filter.button_icon, tab.list)
            self.setTabOrder(tab.list, self.button_login)
#        self.button_login.setFocus()

        self.setTabOrder(self.stack.tabbar.buttons[-1],
                         self.environments_tab.text_search)

        self.environments_tab.packages_widget.table_last_row.add_focus_widget(
            self.button_login)
        self.setTabOrder(self.environments_tab.packages_widget.table_last_row,
                         self.button_login)

    def update_style_sheet(self):
        style_sheet = load_style_sheet()
#        self.home_tab.update_style_sheet(style_sheet)
        self.environments_tab.update_style_sheet(style_sheet)
#        self.community_tab.update_style_sheet(style_sheet)
#        self.learning_tab.update_style_sheet(style_sheet)
        self.setStyleSheet(style_sheet)

    def set_splash(self, message):
        """
        Set splash message.
        """
        if self.splash:
            self.splash.show_message(message)
        QApplication.processEvents()

    # --- Login
    # -------------------------------------------------------------------------
    def update_login_status(self, user_data=None):
        """
        Update login button and information.
        """
        if user_data:
            self.username = user_data.get('login', '')
            self.logged = True

        if self.logged:
            username = self.username
            anaconda_api_url = CONF.get('main', 'anaconda_api_url')
            token = self.api.client_load_token(anaconda_api_url)
            self.button_logged_text.setText('Signed in as')
            self.button_logged_username.setText(username)
            url = "{0}/{1}".format(CONF.get('main', 'conda_url'), username)
            self.button_logged_username.setToolTip(url)
            self.button_login.setText('Sign out')
            self.environments_tab.packages_widget.set_token(token)
        else:
            self.button_logged_text.setText('')
            self.button_logged_username.setText('')
            self.button_login.setText(self._login_text)
        QApplication.restoreOverrideCursor()

    def login(self):
        """
        Open up login dialog or log out depending on logged status.
        """
        if self.logged:
            QApplication.setOverrideCursor(Qt.WaitCursor)
            self.api.client_logout()
            self.api.client_remove_token()
            self.logged = False
            self.sig_logged_out.emit()
            self.tracker.track_event('authenticate', 'logout',
                                     label=self.username)
        else:
            dlg = AuthenticationDialog(self.api, parent=self)

            if self.tracker:
                self.tracker.track_page('/login', pagetitle='Login dialog')

            if dlg.exec_():
                self.api.client_store_token(dlg.token)
                self.username = dlg.username
                self.logged = True
                self.sig_logged_in.emit()

                if self.tracker:
                    self.tracker.track_event('authenticate', 'login',
                                             label=self.username)
            self._track_tab()

        self.update_login_status()
        logger.debug(str((self.logged, self.username)))

    # --- Dialogs
    # -------------------------------------------------------------------------
    def show_preferences(self):
        """
        Display the preferences dialog and apply the needed actions.
        """
        dlg = PreferencesDialog(self)
        self.tracker.track_page('/preferences',
                                pagetitle='Preferences dialog')
        set_domains = self.environments_tab.packages_widget.update_domains
        set_domains = self.environments_tab.packages_widget.update_domains

        dlg.sig_urls_updated.connect(set_domains)
        dlg.sig_urls_updated.connect(lambda au, cu: self.login())
        dlg.exec_()
        self._track_tab()

    def show_about(self):
        """
        Display the `About` dialog with information on the project.
        """
        dlg = AboutDialog(self)
        self.tracker.track_page('/about', pagetitle='About dialog')
        dlg.exec_()
        self._track_tab()

    def show_log_viewer(self):
        """
        Display the logs viewer to the user
        """
        dlg = LogViewerDialog()
        self.tracker.track_page('/logs', pagetitle='Log Viewer Dialog')
        dlg.exec_()
        self._track_tab()

    def show_welcome_screen(self):
        if getattr(self, 'showme', True) and CONF.get('main', 'show_startup',
                                                      True):
            from anaconda_navigator.widgets.splash import FirstSplash

            self.showme = False
            self.splash.hide()
            dlg = FirstSplash()
            dlg.raise_()
            dlg.exec_()

    # --- Update Navigator
    # -------------------------------------------------------------------------
    def _update_application(self, worker, output, error):
        self.button_update_available.setDisabled(False)
        if error:
            text = 'Anaconda Navigator Update error:'
            dlg = MessageBoxError(text=text, error=error,
                                  title='Application Update Error')
            self.tracker.track_page('/update/error',
                                    pagetitle='Update Application Error '
                                              'Message Box')
            dlg.exec_()
        else:
            text = ('Anaconda Navigator Updated succefully.\n\n'
                    'Please restart the application')
            dlg = MessageBoxInformation(text=text, title='Application Update')
            self.tracker.track_page('/update/successful',
                                    pagetitle='Application Update Succesful '
                                              'Message Box')
            dlg.exec_()
        self._track_tab()

    def update_application(self):
        version = self.application_update_version
        if version:
            dlg = DialogUpdateApplication(version=version)
            self.tracker.track_page('/update',
                                    pagetitle='Update Application Dialog')
            reply = dlg.exec_()
            if reply:
                self.tracker.track_event('application', 'updated', version)
                self.busy = True
                pkg = 'anaconda-navigator={}'.format(version)
                worker = self.api.conda_install(name='root', pkgs=[pkg])
                worker.sig_finished.connect(self._update_application)
                self.button_update_available.setDisabled(True)
            self._track_tab()

    def update_status_bar(self, message='', timeout=0, val=-1, max_val=-1):
        """ """
        statusbar = self.statusBar()
        if val != -1 and max_val != -1:
            self.progressbar.setVisible(True)
            self.progressbar.setValue(val)
            self.progressbar.setMaximum(max_val)
        else:
            self.progressbar.setVisible(False)

        if message:
            statusbar.showMessage(message, timeout)
        else:
            statusbar.clearMessage()
        statusbar.setVisible(False)
        statusbar.setMaximumHeight(0)
        statusbar.hide()

    # --- Url handling
    # -------------------------------------------------------------------------
    def open_url(self, url):
        qurl = QUrl(url)
        QDesktopServices.openUrl(qurl)
        self.tracker.track_event('help', 'documentation', url)

    def open_login_page(self):
        """
        """
        conda_url = CONF.get('main', 'conda_url')
        url = "{0}/{1}".format(conda_url, self.username)
        qurl = QUrl(url)
        QDesktopServices.openUrl(qurl)
        self.tracker.track_event('content', 'clicked', url)

    # --- Qt methods
    # -------------------------------------------------------------------------
    def closeEvent(self, event):
        """
        Catch close event.
        """
        # TODO: check if an update is not in progress or things might break!!
#        if self.busy:
        show_dialog = not CONF.get('main', 'hide_quit_dialog')
        if show_dialog:
            if self.tracker:
                self.tracker.track_page('/quit', pagetitle='Quit dialog')
            dlg = QuitApplicationDialog()
            reply = dlg.exec_()

            if not reply:
                event.ignore()
                self._track_tab()

    def keyPressEvent(self, event):
        """
        Qt override.
        """
#        if event.key() in [Qt.Key_F5]:
#            self.update_style_sheet()
        super(MainWindow, self).keyPressEvent(event)

    def paintEvent(self, event):
        """
        Qt override.

        Draw lower left border of the main Stacked Widget.
        """
        super(MainWindow, self).paintEvent(event)
        tab = self.stack.tabbar
        tab_pos = self.mapTo(self, tab.pos())
        pane_pos = self.mapTo(self, self.stack.pos())

        stack_height = self.stack.height()
        menu_height = self.menuBar().height()
        header_height = 49   # From css
        padding = 20         # From css
        left = 1             # From css
        extra = 8            # Still wondering where this extra Y delta is
        deltay = menu_height + header_height + tab.height() + padding + extra
        x0 = tab_pos.x() + tab.width() + padding - left
        y0 = tab_pos.y() + deltay
        y1 = pane_pos.y() + stack_height + deltay - tab.height() - padding

        painter = QPainter(self)
        painter.setPen(QPen(QColor('#006f43'), 1, Qt.SolidLine, Qt.RoundCap))
        painter.drawLine(x0, y0, x0, y1)


def test():
    from anaconda_navigator.utils.qthelpers import qapplication
    app = qapplication()
    w = MainWindow()
    w.showMaximized()
    sys.exit(app.exec_())

if __name__ == "__main__":
    test()
