# -*- coding: utf-8 -*-
"""
This module contains the main window implementation.
"""
import weakref

import mimetypes
import os

from qtpy import QtCore
from qtpy import QtWidgets

from pyqodeng.core import api
from pyqodeng.core import modes
from pyqodeng.core import widgets

from .forms.main_window_ui import Ui_MainWindow
from pyqodeng.core.api import ColorScheme
from pyqodeng.core.widgets import FileSystemTreeView, FileSystemContextMenu, SplittableCodeEditTabWidget


class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        # Load our UI (made in Qt Designer)
        self.setupUi(self)
        self.setup_recent_files_menu()
        self.setup_treeview()
        self.setup_actions()
        self.setup_mimetypes()
        self.setup_status_bar_widgets()
        self.on_current_tab_changed(None)
        self.styles_group = None

    def on_treeView_activated(self, index):
        path = self.treeView.filePath(index)
        if os.path.isfile(path):
            self.open_file(path)

    def setup_treeview(self):
        self.treeView.activated.connect(self.on_treeView_activated)
        path = os.path.join(os.getcwd())
        self.treeView.set_root_path(path)

    def setup_status_bar_widgets(self):
        self.lbl_filename = QtWidgets.QLabel()
        self.lbl_encoding = QtWidgets.QLabel()
        self.lbl_cursor_pos = QtWidgets.QLabel()
        self.statusbar.addPermanentWidget(self.lbl_filename, 200)
        self.statusbar.addPermanentWidget(self.lbl_encoding, 20)
        self.statusbar.addPermanentWidget(self.lbl_cursor_pos, 20)

    def setup_actions(self):
        """ Connects slots to signals """
        self.actionOpen.triggered.connect(self.on_open)
        self.actionNew.triggered.connect(self.on_new)
        self.actionSave.triggered.connect(self.on_save)
        self.actionSave_as.triggered.connect(self.on_save_as)
        self.actionQuit.triggered.connect(QtWidgets.QApplication.instance().quit)
        self.tabWidget.current_changed.connect(
            self.on_current_tab_changed)
        self.actionAbout.triggered.connect(self.on_about)

    def setup_recent_files_menu(self):
        """ Setup the recent files menu and manager """
        self.recent_files_manager = widgets.RecentFilesManager(
            'pyQode', 'notepad')
        self.menu_recents = widgets.MenuRecentFiles(
            self.menuFile, title='Recents',
            recent_files_manager=self.recent_files_manager)
        self.menu_recents.open_requested.connect(self.open_file)
        self.menuFile.insertMenu(self.actionSave, self.menu_recents)
        self.menuFile.insertSeparator(self.actionSave)

    def setup_mimetypes(self):
        """ Setup additional mime types. """
        # setup some specific mimetypes
        mimetypes.add_type('text/xml', '.ui')  # qt designer forms forms
        mimetypes.add_type('text/x-rst', '.rst')  # rst docs
        mimetypes.add_type('text/x-cython', '.pyx')  # cython impl files
        mimetypes.add_type('text/x-cython', '.pxd')  # cython def files
        mimetypes.add_type('text/x-python', '.py')
        mimetypes.add_type('text/x-python', '.pyw')
        mimetypes.add_type('text/x-c', '.c')
        mimetypes.add_type('text/x-c', '.h')
        mimetypes.add_type('text/x-c++hdr', '.hpp')
        mimetypes.add_type('text/x-c++src', '.cpp')
        mimetypes.add_type('text/x-c++src', '.cxx')
        # cobol files
        for ext in ['.cbl', '.cob', '.cpy']:
            mimetypes.add_type('text/x-cobol', ext)
            mimetypes.add_type('text/x-cobol', ext.upper())

    def setup_mnu_edit(self, editor):
        self.menuEdit.addActions(editor.actions())
        self.menuEdit.addSeparator()
        self.setup_mnu_style(editor)

    def setup_mnu_modes(self, editor):
        for mode in editor.modes:
            a = QtWidgets.QAction(self.menuModes)
            a.setText(mode.name)
            a.setCheckable(True)
            a.setChecked(True)
            a.changed.connect(self.on_mode_state_changed)
            a.mode = weakref.proxy(mode)
            self.menuModes.addAction(a)

    def setup_mnu_panels(self, editor):
        for panel in editor.panels:
            a = QtWidgets.QAction(self.menuModes)
            a.setText(panel.name)
            a.setCheckable(True)
            a.setChecked(True)
            a.changed.connect(self.on_panel_state_changed)
            a.panel = weakref.proxy(panel)
            self.menuPanels.addAction(a)

    def setup_mnu_style(self, editor):
        """ setup the style menu for an editor tab """
        menu = QtWidgets.QMenu('Styles', self.menuEdit)
        group = QtWidgets.QActionGroup(self)
        self.styles_group = group
        current_style = editor.syntax_highlighter.color_scheme.name
        group.triggered.connect(self.on_style_changed)
        for s in sorted(modes.PYGMENTS_STYLES):
            a = QtWidgets.QAction(menu)
            a.setText(s)
            a.setCheckable(True)
            if s == current_style:
                a.setChecked(True)
            group.addAction(a)
            menu.addAction(a)
        self.menuEdit.addMenu(menu)

    def closeEvent(self, QCloseEvent):
        """
        Delegates the close event to the tabWidget to be sure we do not quit
        the application while there are some still some unsaved tabs.
        """
        self.tabWidget.closeEvent(QCloseEvent)

    def open_file(self, path):
        """
        Creates a new GenericCodeEdit, opens the requested file and adds it
        to the tab widget.

        :param path: Path of the file to open
        """
        if path:
            editor = self.tabWidget.open_document(path)
            editor.cursorPositionChanged.connect(
                self.on_cursor_pos_changed)
            self.recent_files_manager.open_file(path)
            self.menu_recents.update_actions()

    def on_new(self):
        """
        Add a new empty code editor to the tab widget
        """
        editor = self.tabWidget.create_new_document()
        editor.cursorPositionChanged.connect(self.on_cursor_pos_changed)
        self.refresh_color_scheme()

    def on_open(self):
        """
        Shows an open file dialog and open the file if the dialog was
        accepted.

        """
        filename, filter = QtWidgets.QFileDialog.getOpenFileName(
            self, _('Open'))
        if filename:
            self.open_file(filename)

    def on_save(self):
        self.tabWidget.save_current()
        self._update_status_bar(self.tabWidget.current_widget())

    def on_save_as(self):
        """
        Save the current editor document as.
        """
        self.tabWidget.save_current_as()
        self._update_status_bar(self.tabWidget.current_widget())

    def on_current_tab_changed(self, editor):
        self.menuEdit.clear()
        self.menuModes.clear()
        self.menuPanels.clear()
        self.menuEdit.setEnabled(editor is not None)
        self.menuModes.setEnabled(editor is not None)
        self.menuPanels.setEnabled(editor is not None)
        self.actionSave.setEnabled(editor is not None)
        self.actionSave_as.setEnabled(editor is not None)
        self.actionClose_tab.setEnabled(editor is not None)
        self.actionClose_all_tabs.setEnabled(editor is not None)
        self.actionClose_other_tabs.setEnabled(
            editor is not None and self.tabWidget.count() > 1)
        if editor:
            self.setup_mnu_edit(editor)
            self.setup_mnu_modes(editor)
            self.setup_mnu_panels(editor)
        self._update_status_bar(editor)

    def _update_status_bar(self, editor):
        if editor:
            l, c = api.TextHelper(editor).cursor_position()
            self.lbl_cursor_pos.setText(
                '%d:%d' % (l + 1, c + 1))
            self.lbl_encoding.setText(editor.file.encoding)
            self.lbl_filename.setText(editor.file.path)
        else:
            self.lbl_cursor_pos.clear()
            self.lbl_encoding.clear()
            self.lbl_filename.clear()

    def refresh_color_scheme(self):
        if self.styles_group and self.styles_group.checkedAction():
            style = self.styles_group.checkedAction().text()
            style = style.replace('&', '')  # qt5 bug on kde?
        else:
            style = 'qt'
        for editor in self.tabWidget.widgets():
            editor.syntax_highlighter.color_scheme = ColorScheme(style)
            editor.modes.get(modes.CaretLineHighlighterMode).refresh()

    def on_style_changed(self, action):
        self.pygments_style = action.text()
        self.refresh_color_scheme()

    def on_panel_state_changed(self):
        action = self.sender()
        action.panel.enabled = action.isChecked()
        action.panel.setVisible(action.isChecked())

    def on_mode_state_changed(self):
        action = self.sender()
        action.mode.enabled = action.isChecked()

    def on_about(self):
        QtWidgets.QMessageBox.about(
            self, 'pyQode notepad',
            'This notepad application is an example of what you can do with '
            'pyqodeng.core.')

    def on_cursor_pos_changed(self):
        if self.tabWidget.current_widget():
            editor = self.tabWidget.current_widget()
            l, c = api.TextHelper(editor).cursor_position()
            self.lbl_cursor_pos.setText(
                '%d:%d' % (l + 1, c + 1))
