#!/usr/bin/python3
"""
Email-Reminder Editor - edit special occasion reminders

Copyright (C) 2020 by Francois Marier

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
"""

import argparse
import pwd
import os
from pathlib import Path
import subprocess
import sys

from PySide2.QtCore import QModelIndex, Qt
from PySide2.QtGui import QIcon, QKeySequence
from PySide2.QtWidgets import (
    QAbstractItemView, QApplication, QCheckBox, QDialog, QHBoxLayout, QLabel,
    QLineEdit, QMainWindow, QMessageBox, QMenuBar, QPushButton, QSpinBox,
    QTableView, QTabWidget, QToolBar, QVBoxLayout)

from EmailReminder.EventList import EventList

APP = QApplication(sys.argv)

CONFIG_FILE = '.email-reminders'
SEND_REMINDERS = '/usr/bin/send-reminders'
VERSION = '0.8.1'


class EventTab:
    def __init__(self, model):
        self.model = model
        self.table = QTableView()
        self.table.setModel(model)
        self.table.setShowGrid(False)
        self.table.verticalHeader().setVisible(False)
        self.table.horizontalHeader().setStretchLastSection(True)
        self.table.setCornerButtonEnabled(False)
        self.table.setSelectionBehavior(QTableView.SelectRows)
        self.table.setSelectionMode(QAbstractItemView.SingleSelection)
        self.table.setSortingEnabled(False)  # TODO: enable this (bug 724121)

    def getTable(self):
        return self.table

    def addEvent(self):
        if self.model.append():
            last = self.model.rowCount() - 1
            index = self.model.index(last, 0)
            self.table.setCurrentIndex(index)
            self.table.scrollToBottom()

    def deleteSelectedEvent(self):
        indexes = self.table.selectionModel().selectedIndexes()
        if indexes and indexes[0].isValid():
            if self.model.delete(indexes[0].row()):
                self.table.setCurrentIndex(QModelIndex())

    def getSelectedEventReminders(self):
        indexes = self.table.selectionModel().selectedIndexes()
        if indexes and indexes[0].isValid():
            return self.model.getReminders(indexes[0].row())
        return None

    def setSelectedEventReminders(self, values):
        indexes = self.table.selectionModel().selectedIndexes()
        if indexes and indexes[0].isValid():
            self.model.setReminders(indexes[0].row(), values)


class TabWidget(QTabWidget):
    def __init__(self, *args, **kwargs):
        super(TabWidget, self).__init__(*args, **kwargs)

        self.eventTabs = []

    def append(self, model):
        event = EventTab(model)
        self.eventTabs.append(event)
        self.addTab(event.getTable(), model.LABEL)

    def addEvent(self):
        self.eventTabs[self.currentIndex()].addEvent()

    def deleteEvent(self):
        self.eventTabs[self.currentIndex()].deleteSelectedEvent()

    def getEventReminders(self):
        return self.eventTabs[self.currentIndex()].getSelectedEventReminders()

    def setEventReminders(self, values):
        self.eventTabs[self.currentIndex()].setSelectedEventReminders(values)


class EditRemindersDialog(QDialog):

    def __init__(self, tabs, *args, **kwargs):
        super(EditRemindersDialog, self).__init__(*args, **kwargs)

        self.tabs = tabs

        self.setWindowTitle("Edit reminders")

        self.event_name_label = QLabel("Current reminders for '':")

        self.same_day_checkbox = QCheckBox("&Same day")
        self.same_day_checkbox.setChecked(True)

        days_in_advance_layout = QHBoxLayout()
        self.days_in_advance_checkbox = QCheckBox("&Days in advance:")
        self.days_in_advance_spinbox = QSpinBox()
        self.days_in_advance_spinbox.setRange(1, 364)
        self.days_in_advance_spinbox.setSingleStep(1)
        self.days_in_advance_spinbox.setValue(1)
        self.days_in_advance_spinbox.setDisabled(True)
        self.days_in_advance_checkbox.stateChanged.connect(self.toggleSpinBox)
        days_in_advance_layout.addWidget(self.days_in_advance_checkbox)
        days_in_advance_layout.addWidget(self.days_in_advance_spinbox)

        close_layout = QHBoxLayout()
        close_button = QPushButton("Close")
        close_button.clicked.connect(self.accept)
        close_button.setDefault(True)
        close_layout.addStretch()
        close_layout.addWidget(close_button)

        layout = QVBoxLayout()
        layout.addWidget(self.event_name_label)
        layout.addWidget(self.same_day_checkbox)
        layout.addLayout(days_in_advance_layout)
        layout.addLayout(close_layout)
        self.setLayout(layout)

        self.accepted.connect(self.save)

    def toggleSpinBox(self):
        self.days_in_advance_spinbox.setDisabled(
            not self.days_in_advance_checkbox.isChecked())

    def load(self):
        reminders = self.tabs.getEventReminders()
        if reminders:
            self.event_name_label.setText(
                "Current reminders for '%s':" % reminders['name'])
            self.same_day_checkbox.setChecked(reminders['sameday'])
            self.days_in_advance_checkbox.setChecked(
                reminders['daysinadvance'] > 0)
            self.days_in_advance_spinbox.setValue(reminders['daysinadvance'])
        return reminders is not None

    def save(self):
        values = {'sameday': self.same_day_checkbox.isChecked(),
                  'daysinadvance': 0}
        if self.days_in_advance_checkbox.isChecked():
            values['daysinadvance'] = self.days_in_advance_spinbox.value()
        self.tabs.setEventReminders(values)


class PreferencesDialog(QDialog):

    def __init__(self, events, *args, **kwargs):
        super(PreferencesDialog, self).__init__(*args, **kwargs)

        self.events = events

        self.setWindowTitle("Preferences")

        name_layout = QHBoxLayout()
        self.first_name_edit = QLineEdit()
        self.last_name_edit = QLineEdit()
        name_layout.addWidget(QLabel("Name:"))
        name_layout.addWidget(self.first_name_edit)
        name_layout.addWidget(self.last_name_edit)

        email_layout = QHBoxLayout()
        self.email_edit = QLineEdit()
        email_layout.addWidget(QLabel("Email:"))
        email_layout.addWidget(self.email_edit)

        close_layout = QHBoxLayout()
        close_button = QPushButton("Close")
        close_button.clicked.connect(self.accept)
        close_button.setDefault(True)
        close_layout.addStretch()
        close_layout.addWidget(close_button)

        layout = QVBoxLayout()
        layout.addWidget(
            QLabel("Set the default recipient for the reminder emails:"))
        layout.addLayout(name_layout)
        layout.addLayout(email_layout)
        layout.addLayout(close_layout)
        self.setLayout(layout)

        self.accepted.connect(self.save)

    def load(self):
        user = self.events.getUser()
        if not user['first_name'] and not user['last_name']:
            # Default to the full name of the OS account.
            unixName = pwd.getpwuid(os.getuid())[4].split(',')[0]
            user['first_name'] = unixName.split(' ')[0]
            user['last_name'] = unixName.split(' ')[1]
            self.events.setUser({
                'first_name': user['first_name'],
                'last_name': user['last_name'],
            })

        if not user['email']:
            user['email'] = os.environ['EMAIL']
            self.events.setUser({
                'email': user['email'],
            })

        self.first_name_edit.setText(user['first_name'])
        self.last_name_edit.setText(user['last_name'])
        self.email_edit.setText(user['email'])

    def save(self):
        self.events.setUser({
            'first_name': self.first_name_edit.text(),
            'last_name': self.last_name_edit.text(),
            'email': self.email_edit.text(),
        })


class MainWindow(QMainWindow):

    def __init__(self, events, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        self.events = events

        self.setWindowTitle("Email-Reminder Editor")

        self.tabs = TabWidget()
        for eventType in EventList.SUPPORTED_TYPES:
            self.tabs.append(events.model(eventType))
        self.tabs.setDocumentMode(True)
        self.setCentralWidget(self.tabs)

        self.addToolBar(MainToolbar(self, self.tabs))
        self.setMenuBar(MainMenu(self, self.events, self.tabs))

        self.preferences_dialog = PreferencesDialog(self.events)
        self.edit_reminders_dialog = EditRemindersDialog(self.tabs)

    def show_preferences(self):
        self.preferences_dialog.load()
        self.preferences_dialog.exec_()

    def show_edit_reminders(self):
        if self.edit_reminders_dialog.load():
            self.edit_reminders_dialog.exec_()

    def closeEvent(self, event):
        user = self.events.getUser()
        if user['email']:
            event.accept()
            return

        # Warn the user about a missing email address.
        confirmation_box = QMessageBox()
        confirmation_box.setText("You will not receive any reminders since "
                                 "you have not set your email address.")
        confirmation_box.setInformativeText("Would you like to set your email "
                                            "address in the preferences now "
                                            "or quit?")
        confirmation_box.setIcon(QMessageBox.Question)
        confirmation_box.addButton("Open Preferences", QMessageBox.YesRole)
        confirmation_box.addButton("Quit", QMessageBox.NoRole)

        if confirmation_box.exec():
            event.accept()
        else:
            event.ignore()
            self.show_preferences()


class MainToolbar(QToolBar):

    def __init__(self, window, tabs, *args, **kwargs):
        super(MainToolbar, self).__init__(*args, **kwargs)

        self.setMovable(False)
        self.setFloatable(False)
        self.setContextMenuPolicy(Qt.PreventContextMenu)

        # Maintain the original look-and-feel.
        self.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)

        new_event_action = self.addAction(QIcon.fromTheme('document-new'),
                                          'New')
        new_event_action.triggered.connect(tabs.addEvent)
        delete_event_action = self.addAction(QIcon.fromTheme('edit-delete'),
                                             'Delete')
        delete_event_action.triggered.connect(tabs.deleteEvent)
        self.addSeparator()
        edit_event_action = self.addAction('Edit reminders')
        edit_event_action.triggered.connect(window.show_edit_reminders)


class MainMenu(QMenuBar):

    def __init__(self, window, events, tabs, *args, **kwargs):
        super(MainMenu, self).__init__(*args, **kwargs)

        self.events = events

        self.setNativeMenuBar(True)

        file_menu = self.addMenu('File')
        test_config_action = file_menu.addAction("Test configuration")
        test_config_action.triggered.connect(self.runTest)

        file_menu.addSeparator()

        quit_action = file_menu.addAction("Quit")
        quit_action.setShortcut(QKeySequence.Quit)
        quit_action.triggered.connect(window.close)

        edit_menu = self.addMenu('Edit')
        new_event_action = edit_menu.addAction("New event")
        new_event_action.setShortcut(QKeySequence.New)
        new_event_action.triggered.connect(tabs.addEvent)
        delete_event_action = edit_menu.addAction("Delete event")
        delete_event_action.setShortcut(QKeySequence.Delete)
        delete_event_action.triggered.connect(tabs.deleteEvent)
        edit_event_action = edit_menu.addAction("Edit reminders...")
        edit_event_action.setShortcut(QKeySequence("Ctrl+e"))
        edit_event_action.triggered.connect(window.show_edit_reminders)
        edit_menu.addSeparator()
        preferences_event_action = edit_menu.addAction("Preferences...")
        preferences_event_action.setShortcut(QKeySequence.Preferences)
        preferences_event_action.triggered.connect(window.show_preferences)

    def runTest(self):
        warning_box = QMessageBox()
        warning_box.setIcon(QMessageBox.Warning)
        if not os.access(SEND_REMINDERS, os.X_OK):
            warning_box.setText("Cannot run '%s'." % SEND_REMINDERS)
            warning_box.setInformativeText("Check your installation.")
            warning_box.exec()
            return

        self.events.save()

        result = subprocess.run([SEND_REMINDERS], stdout=subprocess.PIPE,
                                stderr=subprocess.STDOUT, check=False)
        if result.stdout:
            warning_box.setText("Error while running '%s':" % SEND_REMINDERS)
            warning_box.setInformativeText(result.stdout.decode('utf-8'))
            warning_box.exec()


def main():
    parser = argparse.ArgumentParser(description="Simple editor for modifying "
                                     "special occasion email reminders")
    parser.add_argument('-s', '--simulate', dest='simulate',
                        action='store_true', help="don't save any changes")
    parser.add_argument('-v', '--verbose', dest='verbose',
                        action='store_true', help="print more information")
    parser.add_argument('-V', '--version', action='version',
                        version='Email-Reminder Editor %s' % VERSION)
    args = parser.parse_args()

    events = EventList(args.simulate, args.verbose)
    if not events.load(os.path.join(Path.home(), CONFIG_FILE)):
        return 1

    QIcon.setThemeName('gnome')  # TODO: remove (icon+name toolbar buttons)
    window = MainWindow(events)
    window.show()
    status = APP.exec_()
    events.save()
    return status


sys.exit(main())
