#!/usr/bin/env python
#--!-- coding: utf8 --!--

import os
import re
from random import *

from PyQt5.QtCore import Qt, QRect, QStandardPaths, QObject, QRegExp, QDir
from PyQt5.QtCore import QUrl, QTimer
from PyQt5.QtGui import QBrush, QIcon, QPainter, QColor, QImage, QPixmap
from PyQt5.QtGui import QDesktopServices
from PyQt5.QtWidgets import qApp, QFileDialog, QTextEdit

from manuskript.enums import Outline

# Used to detect multiple connections
AUC = Qt.AutoConnection | Qt.UniqueConnection
MW = None


def wordCount(text):
    t = text.strip().replace(" ", "\n").split("\n")
    t = [l for l in t if l]
    return len(t)

validate_ok = lambda *args, **kwargs: True
def uiParse(input, default, converter, validator=validate_ok):
    """
    uiParse is a utility function that intends to make it easy to convert
    user input to data that falls in the range of expected values the
    program is expecting to handle.

    It swallows all exceptions that happen during conversion.
    The validator should return True to permit the converted value.
    """
    result = default
    try:
        result = converter(input)
    except:
        pass  # failed to convert

    # Whitelist default value in case default type differs from converter output.
    if (result != default) and not validator(result):
        result = default
    return result


def toInt(text):
    if text:
        try:
            return int(text)
        except ValueError:
            pass

    return 0


def toFloat(text):
    if text:
        return float(text)
    else:
        return 0.


def toString(text):
    if text in [None, "None"]:
        return ""
    else:
        return str(text)


def drawProgress(painter, rect, progress, radius=0):
    from manuskript.ui import style as S
    progress = toFloat(progress)  # handle invalid input (issue #561)
    painter.setPen(Qt.NoPen)
    painter.setBrush(QColor(S.base)) # "#dddddd"
    painter.drawRoundedRect(rect, radius, radius)

    painter.setBrush(QBrush(colorFromProgress(progress)))

    r2 = QRect(rect)
    r2.setWidth(r2.width() * min(progress, 1))
    painter.drawRoundedRect(r2, radius, radius)


def colorFromProgress(progress):
    progress = toFloat(progress)
    c1 = QColor(Qt.red)
    c2 = QColor(Qt.blue)
    c3 = QColor(Qt.darkGreen)
    c4 = QColor("#FFA500")

    if progress < 0.3:
        return c1
    elif progress < 0.8:
        return c2
    elif progress > 1.2:
        return c4
    else:
        return c3


def mainWindow():
    global MW
    if not MW:
        for i in qApp.topLevelWidgets():
            if i.objectName() == "MainWindow":
                MW = i
                return MW
        return None
    else:
        return MW


def iconColor(icon):
    """Returns a QRgb from a QIcon, assuming its all the same color"""
    px = icon.pixmap(5, 5)
    if px.width() != 0:
        return QColor(QImage(px).pixel(2, 2))
    else:
        return QColor(Qt.transparent)


def iconFromColor(color):
    px = QPixmap(32, 32)
    px.fill(color)
    return QIcon(px)


def iconFromColorString(string):
    return iconFromColor(QColor(string))


def themeIcon(name):
    "Returns an icon for the given name."

    db = {
        "character":    "stock_people",
        "characters":   "stock_people",
        "plot":         "stock_shuffle",
        "plots":        "stock_shuffle",
        "world":        "emblem-web", #stock_timezone applications-internet
        "outline":      "gtk-index", #applications-versioncontrol
        "label":        "folder_color_picker",
        "status":       "applications-development",
        "text":         "view-text",
        "card":         "view-card",
        "outline":      "view-outline",
        "tree":         "view-list-tree",
        "spelling":     "tools-check-spelling"
    }

    if name in db:
        return QIcon.fromTheme(db[name])
    else:
        return QIcon()

def randomColor(mix=None):
    """Generates a random color. If mix (QColor) is given, mixes the random color and mix."""
    r = randint(0, 255)
    g = randint(0, 255)
    b = randint(0, 255)

    if mix:
        r = (r + mix.red()) / 2
        g = (g + mix.green()) / 2
        b = (b + mix.blue()) / 2

    return QColor(r, g, b)


def mixColors(col1, col2, f=.5):
    fromString = False
    if type(col1) == str:
        fromString = True
        col1 = QColor(col1)
    if type(col2) == str:
        col2 = QColor(col2)
    f2 = 1-f
    r = col1.red() * f + col2.red() * f2
    g = col1.green() * f + col2.green() * f2
    b = col1.blue() * f + col2.blue() * f2

    return QColor(r, g, b) if not fromString else QColor(r, g, b).name()


def outlineItemColors(item):

    from manuskript.ui import style as S

    """Takes an OutlineItem and returns a dict of colors."""
    colors = {}
    mw = mainWindow()

    # POV
    colors["POV"] = QColor(Qt.transparent)
    POV = item.data(Outline.POV)
    if POV == "":
        col = QColor(Qt.transparent)
    else:
        for i in range(mw.mdlCharacter.rowCount()):
            if mw.mdlCharacter.ID(i) == POV:
                colors["POV"] = iconColor(mw.mdlCharacter.icon(i))

    # Label
    lbl = item.data(Outline.label)
    if lbl == "":
        col = QColor(Qt.transparent)
    else:
        col = iconColor(mw.mdlLabels.item(toInt(lbl)).icon())
    # if col == Qt.black:
    #     # Don't know why, but transparent is rendered as black
    #     col = QColor(Qt.transparent)
    colors["Label"] = col

    # Progress
    pg = item.data(Outline.goalPercentage)
    colors["Progress"] = colorFromProgress(pg)

    # Compile
    if item.compile() in [0, "0"]:
        colors["Compile"] = mixColors(QColor(S.text), QColor(S.window))
    else:
        colors["Compile"] = QColor(Qt.transparent) # will use default

    return colors


def colorifyPixmap(pixmap, color):
    # FIXME: ugly
    p = QPainter(pixmap)
    p.setCompositionMode(p.CompositionMode_Overlay)
    p.fillRect(pixmap.rect(), color)
    return pixmap


def appPath(suffix=None):
    p = os.path.realpath(os.path.join(os.path.split(__file__)[0], "../.."))
    if suffix:
        p = os.path.join(p, suffix)
    return p


def writablePath(suffix=None):
    if hasattr(QStandardPaths, "AppLocalDataLocation"):
        p = QStandardPaths.writableLocation(QStandardPaths.AppLocalDataLocation)
    else:
        # Qt < 5.4
        p = QStandardPaths.writableLocation(QStandardPaths.DataLocation)
    if suffix:
        p = os.path.join(p, suffix)
    if not os.path.exists(p):
        os.makedirs(p)
    return p


def allPaths(suffix=None):
    paths = []
    # src directory
    paths.append(appPath(suffix))
    # user writable directory
    paths.append(writablePath(suffix))
    return paths

def tempFile(name):
    "Returns a temp file."
    return os.path.join(QDir.tempPath(), name)


def totalObjects():
    return len(mainWindow().findChildren(QObject))


def printObjects():
    print("Objects:", str(totalObjects()))


def findWidgetsOfClass(cls):
    """
    Returns all widgets, children of MainWindow, whose class is cls.
    @param cls: a class
    @return: list of QWidgets
    """
    return mainWindow().findChildren(cls, QRegExp())


def findBackground(filename):
    """
    Returns the full path to a background file of name filename within resources folders.
    """
    return findFirstFile(re.escape(filename), "resources/backgrounds")


def findFirstFile(regex, path="resources"):
    """
    Returns full path of first file matching regular expression regex within folder path,
    otherwise returns full path of last file in folder path.
    """
    paths = allPaths(path)
    for p in paths:
        lst = os.listdir(p)
        for l in lst:
            if re.match(regex, l):
                return os.path.join(p, l)

def customIcons():
    """
    Returns a list of possible customIcons. String from theme.
    """

    r = [
        "text-plain",
        "gnome-settings",
        "applications-internet",
        "applications-debugging",
        "applications-development",
        "system-help",
        "info",
        "dialog-question",
        "dialog-warning",
        "stock_timezone",
        "stock_people",
        "stock_shuffle",
        "gtk-index",
        "folder_color_picker",
        "applications-versioncontrol",

        "stock_home",
        "stock_trash_empty",
        "stock_trash_full",
        "stock_yes",
        "stock_no",
        "stock_notes",
        "stock_calendar",
        "stock_mic",
        'stock_score-lowest', 'stock_score-lower', 'stock_score-low', 'stock_score-normal', 'stock_score-high', 'stock_score-higher', 'stock_score-highest',
        "stock_task",
        "stock_refresh",
        "application-community",
        "applications-chat",
        "application-menu",
        "applications-education",
        "applications-science",
        "applications-puzzles",
        "applications-roleplaying",
        "applications-sports",
        "applications-libraries",
        "applications-publishing",
        "applications-development",
        "applications-games",
        "applications-boardgames",
        "applications-geography",
        "applications-physics",
        "package_multimedia",
        "media-flash",
        "media-optical",
        "media-floppy",
        "media-playback-start",
        "media-playback-pause",
        "media-playback-stop",
        "media-playback-record",
        "media-playback-start-rtl",
        "media-eject",
        "document-save",
        "gohome",
        'purple-folder', 'yellow-folder', 'red-folder', 'custom-folder', 'grey-folder', 'blue-folder', 'default-folder', 'pink-folder', 'orange-folder', 'green-folder', 'brown-folder',
        'folder-home', 'folder-remote', 'folder-music', 'folder-saved-search', 'folder-projects', 'folder-sound', 'folder-publicshare', 'folder-pictures', 'folder-saved-search-alt', 'folder-tag',
        'calendar-01', 'calendar-02', 'calendar-03', 'calendar-04', 'calendar-05', 'calendar-06', 'calendar-07', 'calendar-08', 'calendar-09', 'calendar-10',
        'arrow-down', 'arrow-left', 'arrow-right', 'arrow-up', 'arrow-down-double', 'arrow-left-double', 'arrow-right-double', 'arrow-up-double',
        'emblem-added', 'emblem-checked', 'emblem-downloads', 'emblem-dropbox-syncing', 'emblem-danger', 'emblem-development', 'emblem-dropbox-app', 'emblem-art', 'emblem-camera', 'emblem-dropbox-selsync', 'emblem-insync-des-error', 'emblem-insync-error', 'emblem-generic', 'emblem-favorites', 'emblem-error', 'emblem-dropbox-uptodate', 'emblem-marketing', 'emblem-money', 'emblem-music', 'emblem-noread', 'emblem-people', 'emblem-personal', 'emblem-sound', 'emblem-shared', 'emblem-sales', 'emblem-presentation', 'emblem-plan', 'emblem-system', 'emblem-urgent', 'emblem-videos', 'emblem-web',
        'face-angel', 'face-clown', 'face-angry', 'face-cool', 'face-devilish', 'face-sick', 'face-sleeping', 'face-uncertain', 'face-monkey', 'face-ninja', 'face-pirate', 'face-glasses', 'face-in-love', 'face-confused',
        'feed-marked-symbolic', 'feed-non-starred', 'feed-starred', 'feed-unmarked-symbolic',
        'notification-new-symbolic',
        ]

    return sorted(r)

def statusMessage(message, duration=5000, importance=1):
    """
    Shows a message in MainWindow's status bar.
    Importance: 0 = low, 1 = normal, 2 = important, 3 = critical.
    """
    from manuskript.ui import style as S
    MW.statusBar().hide()
    MW.statusLabel.setText(message)
    if importance == 0:
        MW.statusLabel.setStyleSheet("color:{};".format(S.textLighter))
    elif importance == 1:
        MW.statusLabel.setStyleSheet("color:{};".format(S.textLight))
    elif importance == 2:
        MW.statusLabel.setStyleSheet("color:{}; font-weight: bold;".format(S.text))
    elif importance == 3:
        MW.statusLabel.setStyleSheet("color:red; font-weight: bold;")
    MW.statusLabel.adjustSize()
    g = MW.statusLabel.geometry()
    # g.moveCenter(MW.mapFromGlobal(MW.geometry().center()))
    s = MW.layout().spacing() / 2
    g.setLeft(s)
    g.moveBottom(MW.mapFromGlobal(MW.geometry().bottomLeft()).y() - s)
    MW.statusLabel.setGeometry(g)
    MW.statusLabel.show()
    QTimer.singleShot(duration, MW.statusLabel.hide)

def openURL(url):
    """
    Opens url (string) in browser using desktop default application.
    """
    QDesktopServices.openUrl(QUrl(url))

def getSaveFileNameWithSuffix(parent, caption, directory, filter, options=None, selectedFilter=None, defaultSuffix=None):
    """
    A reimplemented version of QFileDialog.getSaveFileName() because we would like to make use
    of the QFileDialog.defaultSuffix property that getSaveFileName() does not let us adjust.

    Note: knowing the selected filter is not an invitation to change the chosen filename later.
    """
    dialog = QFileDialog(parent=parent, caption=caption, directory=directory, filter=filter)
    if options:
        dialog.setOptions(options)
    if defaultSuffix:
        dialog.setDefaultSuffix(defaultSuffix)
    dialog.setFileMode(QFileDialog.AnyFile)
    if hasattr(dialog, 'setSupportedSchemes'): # Pre-Qt5.6 lacks this.
        dialog.setSupportedSchemes(("file",))
    dialog.setAcceptMode(QFileDialog.AcceptSave)
    if selectedFilter:
        dialog.selectNameFilter(selectedFilter)
    if (dialog.exec() == QFileDialog.Accepted):
        return dialog.selectedFiles()[0], dialog.selectedNameFilter()
    return None, None

def inspect():
    """
    Debugging tool. Call it to see a stack of calls up to that point.
    """
    import inspect, os
    print("-----------------------")
    for s in inspect.stack()[1:]:
        print(" * {}:{} // {}".format(
            os.path.basename(s.filename),
            s.lineno,
            s.function))
        print("   " + "".join(s.code_context))

# Spellchecker loads writablePath from this file, so we need to load it after they get defined
from manuskript.functions.spellchecker import Spellchecker
