#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Smewt - A smart collection manager
# Copyright (c) 2008 Nicolas Wack <wackou@gmail.com>
#
# Smewt is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Smewt 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

import sys, logging, os, os.path

MAIN_LOGGING_LEVEL = logging.INFO
LOGGING_TCP_PORT = 9020

# when we create a py2exe, we want to redirect the output the a file ourselves,
# otherwise it gives an error when closing the app
if sys.platform == 'win32' and os.path.exists(os.path.join(os.getcwd(), 'library.zip')):
    userdir = os.path.join(os.environ['USERPROFILE'], 'Application Data', 'Smewt')
    if not os.path.exists(userdir):
        os.makedirs(userdir)
    sys.stderr = open(os.path.join(userdir, 'smewt.log'), 'w')



import logging
import sys

GREEN_FONT = "\x1B[0;32m"
YELLOW_FONT = "\x1B[0;33m"
BLUE_FONT = "\x1B[0;34m"
RED_FONT = "\x1B[0;31m"
RESET_FONT = "\x1B[0m"


def setupLogging(colored = True):
    """Sets up a nice colored logger as the main application logger (not only smewt itself)."""

    class SimpleFormatter(logging.Formatter):
        def __init__(self):
            self.fmt = '%(levelname)-8s %(module)s:%(funcName)s -- %(message)s'
            logging.Formatter.__init__(self, self.fmt)

    class ColoredFormatter(logging.Formatter):
        def __init__(self):
            self.fmt = '%(levelname)-8s ' + BLUE_FONT + '%(mname)-8s %(mmodule)s:%(funcName)s' + RESET_FONT + ' -- %(message)s'
            logging.Formatter.__init__(self, self.fmt)

        def format(self, record):
            modpath = record.name.split('.')
            record.mname = modpath[0]
            record.mmodule = '.'.join(modpath[1:])
            result = logging.Formatter.format(self, record)
            if record.levelno == logging.DEBUG:
                return BLUE_FONT + result
            elif record.levelno == logging.INFO:
                return GREEN_FONT + result
            elif record.levelno == logging.WARNING:
                return YELLOW_FONT + result
            else:
                return RED_FONT + result


    ch = logging.StreamHandler()
    if colored and sys.platform != 'win32':
        ch.setFormatter(ColoredFormatter())
    else:
        ch.setFormatter(SimpleFormatter())
    logging.getLogger().addHandler(ch)


setupLogging()


logging.getLogger().setLevel(MAIN_LOGGING_LEVEL)

import logging.handlers
logging.getLogger().addHandler(logging.handlers.SocketHandler('localhost', LOGGING_TCP_PORT))

# we most likely never want this to be on debug mode, as it spits out way too much information
if MAIN_LOGGING_LEVEL == logging.DEBUG:
    logging.getLogger('pygoo').setLevel(logging.INFO)
    logging.getLogger('imdbpy').setLevel(logging.INFO)


from PyQt4.QtGui import QApplication, QMainWindow, QWidget, QStatusBar, QProgressBar, QHBoxLayout, QStackedWidget, QIcon, QSystemTrayIcon, QAction, QMenu, QMessageBox, QToolBar, QKeySequence
from PyQt4.QtCore import SIGNAL, QSize, Qt, QSettings, QVariant, QPoint, QSize, QObject, QTimer
from smewt.gui import MainWidget, FeedWatchWidget, AboutDialog
from smewt.base import cache, utils
from smewt.base.utils import smewtDirectory, smewtUserDirectory

from os.path import join

log = logging.getLogger('smewg')
DEFAULT_WIDTH = 960
DEFAULT_HEIGHT = 720


class StatusWidget(QWidget):
    def __init__(self):
        super(QWidget,  self).__init__()

        layout = QHBoxLayout()
        layout.addStretch()

        self.progressBar = QProgressBar()
        layout.addWidget(self.progressBar)

        # don't show progess bar initially
        self.progressBar.hide()

        self.setLayout(layout)


class SmewtGui(QMainWindow):

    def __init__(self):
        super(SmewtGui, self).__init__()
        self.setWindowTitle('Smewg - An Ordinary Smewt Gui')

        self.readWindowSettings()

        self.icon = QIcon(smewtDirectory('smewt', 'icons', 'smewt.svg'))
        self.setWindowIcon(self.icon)

        self.createWidgets()
        self.createActions()

        # create menubar
        mainMenu = self.menuBar().addMenu('Main')
        #mainMenu.addAction(self.logviewAction)
        # done every time we start the application, as it seems there's no way to tell webkit to reload them
        #mainMenu.addAction(self.regenerateThumbnailsAction)
        mainMenu.addAction(self.clearCacheAction)
        mainMenu.addSeparator()
        mainMenu.addAction(self.quitAction)

        importMenu = self.menuBar().addMenu('Collection')
        importMenu.addAction(self.selectMoviesFoldersAction)
        importMenu.addAction(self.selectSeriesFoldersAction)
        importMenu.addSeparator()
        importMenu.addAction(self.updateCollectionAction)
        importMenu.addAction(self.rescanCollectionAction)
        importMenu.addAction(self.clearCollectionAction)

        helpMenu = self.menuBar().addMenu('Help')
        helpMenu.addAction(self.aboutAction)
        helpMenu.addAction(self.aboutQtAction)

        # create toolbar
        navigationToolBar = QToolBar('Navigation')
        navigationToolBar.addAction(self.backAction)
        navigationToolBar.addAction(self.fwdAction)
        navigationToolBar.addAction(self.refreshAction)
        navigationToolBar.addAction(self.homeAction)
        navigationToolBar.addSeparator()
        navigationToolBar.addAction(self.zoomOutAction)
        navigationToolBar.addAction(self.zoomInAction)
        navigationToolBar.addSeparator()
        navigationToolBar.addAction(self.fullScreenAction)
        navigationToolBar.setIconSize(QSize(32,32))
        navigationToolBar.setObjectName('navigationToolBar')
        self.addToolBar(navigationToolBar)
        self.createTrayIcon()

        # tmp
        self.connect(self.mainWidget, SIGNAL('feedwatcher'),
                     self.showFeedWatcher)

        # first-run wizard
        settings = QSettings()
        configured = settings.value('configured').toBool()

        if not configured:
            self.regenerateSpeedDialThumbnails()
            settings.setValue('configured', True)
        else:
            # regenerate thumbnails once everything is setup, otherwise it seems somehow
            # we can;t get the correct window size for taking the screenshot
            QTimer.singleShot(1000, self.regenerateSpeedDialThumbnails)

            # only start the update of the collections once our GUI is fully setup
            # do not rescan as it would be too long and we might delete some files that
            # are on an unaccessible network share or an external HDD
            QTimer.singleShot(2000, self.reloadCacheAndUpdateCollections)


    def showFeedWatcher(self):
        self.tabWidget.setCurrentIndex(1)

    def showSpeedDial(self):
        self.tabWidget.setCurrentIndex(0)
        self.mainWidget.speedDial()

    def createWidgets(self):
        self.mainWidget = MainWidget()
        self.feedWatchWidget = FeedWatchWidget()

        self.tabWidget = QStackedWidget()
        self.tabWidget.addWidget(self.mainWidget)
        self.tabWidget.addWidget(self.feedWatchWidget)

        self.setCentralWidget(self.tabWidget)

        self.statusWidget = StatusWidget()
        self.statusBar().addPermanentWidget(self.statusWidget, 0)

        self.mainWidget.smewtd.taskManager.progressChanged.connect(self.progressChanged)
        self.mainWidget.smewtd.taskManager.displayStatusBar.connect(self.displayStatusBar)


    def createActions(self):
        self.logviewAction = QAction('Show log', self)
        self.connect(self.logviewAction, SIGNAL('triggered()'),
                     self.viewLog)

        self.regenerateThumbnailsAction = QAction('Refresh speed dial', self)
        self.connect(self.regenerateThumbnailsAction, SIGNAL('triggered()'),
                     self.regenerateSpeedDialThumbnails)

        self.clearCacheAction = QAction('Clear internet cache', self)
        self.connect(self.clearCacheAction, SIGNAL('triggered()'),
                     self.clearCache)

        self.quitAction = QAction('Quit', self)
        self.quitAction.setShortcuts(QKeySequence.Quit)
        self.connect(self.quitAction, SIGNAL('triggered()'),
                     self.quit)

        self.minimizeAction = QAction('Minimize', self)
        self.connect(self.minimizeAction, SIGNAL('triggered()'),
                     self.hide)

        self.restoreAction = QAction('Restore', self)
        self.connect(self.restoreAction, SIGNAL('triggered()'),
                     self.showNormal)

        self.aboutAction = QAction('About', self)
        self.connect(self.aboutAction, SIGNAL('triggered()'),
                     self.about)

        self.aboutQtAction = QAction('About Qt', self)
        self.connect(self.aboutQtAction, SIGNAL('triggered()'),
                     self.aboutQt)


        # navigation bar
        self.backAction = QAction(QIcon(smewtDirectory('smewt', 'icons', 'go-previous.png')), 'Back', self)
        self.backAction.setStatusTip('Go back')
        self.connect(self.backAction, SIGNAL('triggered()'),
                     self.mainWidget.back)

        self.fwdAction = QAction(QIcon(smewtDirectory('smewt', 'icons', 'go-next.png')), 'Forward', self)
        self.fwdAction.setStatusTip('Go forward')
        self.connect(self.fwdAction, SIGNAL('triggered()'),
                     self.mainWidget.forward)

        self.refreshAction = QAction(QIcon(smewtDirectory('smewt', 'icons', 'view-refresh.png')), 'Refresh', self)
        self.refreshAction.setStatusTip('Refresh the main view')
        self.connect(self.refreshAction, SIGNAL('triggered()'),
                     self.mainWidget.refreshCollectionView)

        self.homeAction = QAction(QIcon(smewtDirectory('smewt', 'icons', 'go-home.png')), 'Home (Speed Dial)', self)
        self.homeAction.setStatusTip('Return to speed dial')
        self.connect(self.homeAction, SIGNAL('triggered()'),
                     self.showSpeedDial)


        self.zoomInAction = QAction(QIcon(smewtDirectory('smewt', 'icons', 'zoom-in.png')), 'Zoom in', self)
        self.zoomInAction.setStatusTip('Make the text larger')
        self.connect(self.zoomInAction, SIGNAL('triggered()'),
                     self.mainWidget.zoomIn)

        self.zoomOutAction = QAction(QIcon(smewtDirectory('smewt', 'icons', 'zoom-out.png')), 'Zoom out', self)
        self.zoomOutAction.setStatusTip('Make the text smaller')
        self.connect(self.zoomOutAction, SIGNAL('triggered()'),
                     self.mainWidget.zoomOut)

        self.fullScreenAction = QAction(QIcon(smewtDirectory('smewt', 'icons', 'view-fullscreen.png')), 'Full Screen', self)
        self.fullScreenAction.setStatusTip('Toggle fullscreen mode')
        self.fullScreenAction.setCheckable(True)
        self.connect(self.fullScreenAction, SIGNAL('triggered()'),
                     self.toggleFullScreen)

        # import actions

        self.selectMoviesFoldersAction = QAction('Select movies folders', self)
        self.selectMoviesFoldersAction.setStatusTip('Select the folders containing movies')
        self.connect(self.selectMoviesFoldersAction,  SIGNAL('triggered()'),
                     self.mainWidget.selectMoviesFolders)

        self.selectSeriesFoldersAction = QAction('Select series folders', self)
        self.selectSeriesFoldersAction.setStatusTip('Select the folders containing series')
        self.connect(self.selectSeriesFoldersAction,  SIGNAL('triggered()'),
                     self.mainWidget.selectSeriesFolders)

        self.updateCollectionAction = QAction('Update collection', self)
        self.updateCollectionAction.setStatusTip('Update the collection')
        self.connect(self.updateCollectionAction,  SIGNAL('triggered()'),
                     self.mainWidget.updateCollection)

        self.rescanCollectionAction = QAction('Rescan collection', self)
        self.rescanCollectionAction.setStatusTip('Rescan the collection')
        self.connect(self.rescanCollectionAction,  SIGNAL('triggered()'),
                     self.mainWidget.rescanCollection)

        self.clearCollectionAction = QAction('Clear collection', self)
        self.clearCollectionAction.setStatusTip('Clear the collection')
        self.connect(self.clearCollectionAction,  SIGNAL('triggered()'),
                     self.mainWidget.clearCollection)

        # for single app to bring up the window we need a signal that can go through threads
        self.connect(self, SIGNAL('bringUp()'),
                     self.bringUpWindow)

    def bringUpWindow(self):
        self.setVisible(True)
        self.setWindowState(sgui.windowState() & ~Qt.WindowMinimized);
        self.show()
        self.raise_()
        self.activateWindow()


    def toggleFullScreen(self):
        flag = Qt.WindowFullScreen if self.fullScreenAction.isChecked() else ~Qt.WindowFullScreen
        self.setWindowState(self.windowState() ^ Qt.WindowFullScreen  )

    def reloadCacheAndUpdateCollections(self):
        # do this now instead of at the very beginning so that our GUI can be setup faster
        cache.load(utils.smewtUserPath(smewt.APP_NAME + '.cache'))
        self.mainWidget.smewtd.updateCollections()

    def viewLog(self):
        # FIXME: this won't work in release mode
        logview = os.path.join(os.path.split(__file__)[0], 'logview.py 2>&1 >/dev/null &')
        os.system(logview)

    thumbnails = { 'smewt://media/speeddial/': 'speeddial.png',
                   'smewt://media/series/all': 'allseries.png',
                   'smewt://media/movie/all':  'allmovies.png',
                   'smewt://media/movie/recent': 'recentmovies.png',
                   'smewt://media/series/suggestions': 'episodesuggestions.png',
                   'smewt://media/movie/spreadsheet': 'moviespreadsheet.png',
                   }

    def regenerateSpeedDialThumbnails(self):
        log.info('Regenerating thumbnails for the speed dial...')
        thumbsDir = smewtUserDirectory('speeddial')
        for url, filename in SmewtGui.thumbnails.items():
            screenshot = self.mainWidget.webpageScreenshot(self.mainWidget.renderSmewtUrl(url))
            screenshot.scaledToWidth(200, Qt.SmoothTransformation).save(os.path.join(thumbsDir, filename))

        if self.mainWidget.smewtUrl.mediaType == 'speeddial':
            # also refresh our view then
            self.mainWidget.refreshCollectionView()


    def checkSpeedDialThumbnails(self):
        if any(not os.path.exists(os.path.join(smewtUserDirectory('speeddial'), filename)) for filename in SmewtGui.thumbnails.values()):
            # if any single thumbnail is missing, we regenerate them all anyway
            # could probably be optimized but is it really worth doing?
            self.regenerateSpeedDialThumbnails()

    def clearCache(self):
        cache.clear()
        cacheFile = utils.smewtUserPath(smewt.APP_NAME + '.cache')
        log.info('Deleting cache file: %s' % cacheFile)
        os.remove(cacheFile)

    def quit(self):
        log.info('SmewtGui quitting...')
        self.writeWindowSettings()
        self.mainWidget.quit()

        # save cache
        cache.save(utils.smewtUserPath(smewt.APP_NAME + '.cache'))

        log.info('Quitting application...')
        QApplication.instance().quit()

    def readWindowSettings(self):
        settings = QSettings()
        rect = QApplication.desktop().availableGeometry(self)

        size = settings.value("MainWindow/size", QVariant(QSize(min(DEFAULT_WIDTH, rect.width()),
                                                                min(DEFAULT_HEIGHT, rect.height())))).toSize()
        self.resize(size)

        center = rect.center()
        pos = settings.value("MainWindow/pos", QVariant(QPoint(center.x()-self.width()/2.0, center.y()-self.height()/2.0))).toPoint()
        self.move(pos)

        self.restoreState(settings.value("MainWindow/windowstate").toByteArray())

    def writeWindowSettings(self):
        settings = QSettings()
        settings.setValue("MainWindow/pos", QVariant(self.pos()))
        settings.setValue("MainWindow/size", QVariant(self.size()))

        settings.setValue("MainWindow/windowstate", QVariant(self.saveState()))


    def createTrayIcon(self):
        trayMenu = QMenu(self)
        trayMenu.addAction(self.minimizeAction)
        trayMenu.addAction(self.restoreAction)
        trayMenu.addSeparator()
        trayMenu.addAction(self.quitAction)

        self.trayIcon = QSystemTrayIcon(self.icon, self)
        self.trayIcon.setContextMenu(trayMenu)
        self.trayIcon.setVisible(True)

        self.connect(self.trayIcon, SIGNAL('activated(QSystemTrayIcon::ActivationReason)'),
                     self.iconActivated)

    def setVisible(self, visible):
        self.minimizeAction.setEnabled(visible)
        self.restoreAction.setEnabled(not visible)
        QMainWindow.setVisible(self, visible)


    def iconActivated(self, reason):
        if reason == QSystemTrayIcon.Trigger or reason == QSystemTrayIcon.DoubleClick:
            if self.isVisible():
                self.setVisible(False)
            else:
                self.setVisible(True)

    def closeEvent(self, event):
        self.writeWindowSettings()
        self.hide()
        event.ignore()

    def progressChanged(self, tagged, total):
        from threading import current_thread
        log.debug('Received signal in thread %d' % current_thread().ident)

        if total == 0:
            log.debug('Resetting progress bar')
            self.statusWidget.progressBar.reset()
            self.statusWidget.progressBar.hide()
            self.statusBar().clearMessage()
        else:
            log.debug('Setting progress bar to %d/%d' % (tagged, total))
            self.statusWidget.progressBar.setMaximum(total)
            self.statusWidget.progressBar.setValue(tagged)
            self.statusWidget.progressBar.show()

    def displayStatusBar(self, msg):
        self.statusBar().showMessage(msg, 60000)


    def about(self):
        AboutDialog().exec_()

    def aboutQt(self):
        QMessageBox.aboutQt(self)


if __name__ == '__main__':
    import smewt
    app = QApplication(sys.argv)
    app.setOrganizationName(smewt.ORG_NAME)
    app.setOrganizationDomain('smewt.com')
    app.setApplicationName(smewt.APP_NAME)

    sgui = SmewtGui()

    def cb():
        sgui.emit(SIGNAL('bringUp()'))

    from smewt.base import singleapplication
    singleapplication.PORT = smewt.SINGLE_APP_PORT
    singleapplication.CALLBACK = cb
    sapp = singleapplication.SingleApplicationWatcher()


    sgui.show()
    app.exec_()

    log.info('Exiting')
    sys.exit() # why is this necessary when running from eric?
