# coding: utf-8

import sys
import numpy as np
import math
import copy
import PySide.QtCore as qtcore
import PySide.QtGui as qtgui
import traceback

class Afficheur(qtgui.QWidget):
    afficheurs = []
    size = (800, 600)
    coord = np.array((50, 50))
    decalage = np.array((40,40))

    def __init__(self, sujet, centre, ech):
        super(Afficheur, self).__init__()
        if(not "trace" in dir(sujet)):
            raise Exception('Méthode "trace" non définie dans la classe ' + str(sujet.__class__))

        app = qtgui.QApplication.instance()
        if not app:
            app = qtgui.QApplication(sys.argv)
        desktop = app.desktop()
        screen = desktop.screenGeometry(desktop.screenNumber(self))

        self.rayon = 4
        self.ech = ech
        self.centre = np.array(centre)
        self.crayon = None
        self.sujet = sujet
        self.setStyleSheet("background-color: white")
        self.center = np.array(Afficheur.size) / 2.
        self.setGeometry(Afficheur.coord[0] + screen.x(), Afficheur.coord[1] + screen.y(), Afficheur.size[0], Afficheur.size[1])
        Afficheur.coord += Afficheur.decalage
        self.setWindowTitle("Afficheur")
        self.show()

    def __pointVersPixel(self, p):
        p = np.array(p)
        pixel = (p - self.centre) * self.ech
        pixel[1] = -pixel[1]
        pixel += self.center
        return qtcore.QPoint(pixel[0],pixel[1])

    def __pixelVersPoint(self, pixel):
        p = np.array((pixel.x(), pixel.y())) - self.center
        p[1] = -p[1]
        p = p / self.ech + self.centre
        return p

    def __pixelVersVecteur(self, pixel1, pixel2):
        assert isinstance(pixel1, qtcore.QPoint)
        assert isinstance(pixel2, qtcore.QPoint)
        v = np.array((pixel2.x(), pixel2.y())) - np.array((pixel1.x(), pixel1.y()))
        v[1] = -v[1]
        v = v / self.ech
        return v

    def tracePoint(self, p):
        assert self.crayon != None
        assert isinstance(p, tuple) or isinstance(p, np.ndarray)
        assert isinstance(p[0], float)
        assert isinstance(p[1], float)
        pixel = self.__pointVersPixel(p)
        self.crayon.drawEllipse(pixel, self.rayon, self.rayon)

    def traceLigne(self, p1, p2):
        assert self.crayon != None
        assert isinstance(p1, tuple) or isinstance(p1, np.ndarray)
        assert isinstance(p1[0], float)
        assert isinstance(p1[1], float)
        assert isinstance(p2, tuple) or isinstance(p2, np.ndarray)
        assert isinstance(p2[0], float)
        assert isinstance(p2[1], float)
        pixel1 = self.__pointVersPixel(p1)
        pixel2 = self.__pointVersPixel(p2)
        self.crayon.drawLine(pixel1, pixel2)

    def changeCouleur(self, couleur):
        assert self.crayon != None
        assert isinstance(couleur, tuple)
        assert isinstance(couleur[0], float)
        assert isinstance(couleur[1], float)
        assert isinstance(couleur[2], float)
        c = qtgui.QColor(couleur[0]*255, couleur[1]*255, couleur[2]*255)
        self.crayon.setPen(c)
        self.crayon.setBrush(c)

    def traceTexte(self, p, texte):
        assert isinstance(p, tuple) or isinstance(p, np.ndarray)
        assert isinstance(p[0], float)
        assert isinstance(p[1], float)
        assert isinstance(texte, str)
        pixel = self.__pointVersPixel(p)
        pixel += qtcore.QPoint(10,-10)
        self.crayon.drawText(pixel, texte)

    def renomme(self, titre):
#        assert isinstance(titre, str)
#        titre = unicode(titre, 'utf-8')
        self.setWindowTitle(titre)

    def mousePressEvent(self, QMouseEvent):
        pos = QMouseEvent.pos()
        self.clic = pos
        pos = self.__pixelVersPoint(pos)
        self.centre = self.centre

    def mouseMoveEvent(self, QMouseEvent):
        pos = QMouseEvent.pos()
        self.centre -= self.__pixelVersVecteur(self.clic, pos)
        self.clic = pos
        self.update()

    def mouseReleaseEvent(self, QMouseEvent):
        pos = QMouseEvent.pos()
        self.centre -= self.__pixelVersVecteur(self.clic, pos)
        self.clic = pos
        self.update()

    def wheelEvent(self, event):
        pos = event.pos()
        C = self.__pixelVersPoint(pos)
        k1 = self.ech
        k2 = k1 * math.exp(0.001 * event.delta())
        self.centre = (self.centre * k1 + C * (k2 -k1)) / k2
        self.ech = k2
        self.update()

    def paintEvent(self, event):
        if(self.crayon != None):
            return
        self.crayon = qtgui.QPainter(self)
        self.trace()
        self.crayon = None

    def trace(self):
        self.crayon.setFont(qtgui.QFont('Decorative', 10))
        if(self.sujet != None):
            try:
                self.sujet.trace(self)
            except AttributeError:
                self.close()
                qtgui.QApplication.quit()
                raise Exception('Méthode "trace" non définie dans la classe ' + str(self.sujet.__class__))
            except BaseException:
                traceback.print_exc()
                self.close()
                qtgui.QApplication.quit()
        L = 100.;
        x = 20; y = 20;
        self.crayon.setPen('black')
        msg = '{0:.2f} km'.format(L / self.ech)
        l = self.crayon.fontMetrics().boundingRect(msg).width()
        self.crayon.drawText(x + (L-l)/2, y-2, msg);
        self.crayon.drawLine(x, y, x + L, y);
        self.crayon.drawLine(x, y - L/20, x, y + L/10);
        self.crayon.drawLine(x + L, y - L/20, x + L, y + L/10);


    def sauvegarde(self):
        filename = qtgui.QFileDialog.getSaveFileName(self, 'Fichier PDF')
        if filename:
            printer = qtgui.QPrinter(qtgui.QPrinter.HighResolution)
            printer.setPageSize(qtgui.QPrinter.A4)
            printer.setColorMode(qtgui.QPrinter.Color)
            printer.setOutputFormat(qtgui.QPrinter.PdfFormat)
            printer.setOutputFileName(filename)

            self.crayon = qtgui.QPainter(printer)
            self.trace()
            self.crayon = None

def affiche(sujet, centre, ech, blocage = True):
    '''Affiche dans une fenêtre l'objet sujet qui doit avoir une méthode trace'''
    assert isinstance(centre, tuple)
    assert isinstance(ech, float)
    sujet = copy.deepcopy(sujet)
    app = qtgui.QApplication.instance()
    if not app:
        app = qtgui.QApplication(sys.argv)
    a = Afficheur(sujet, centre, ech)
    Afficheur.afficheurs.append(a)
    if(blocage):
        bloque()
    return a

def bloque():
    app = qtgui.QApplication.instance()
    if not app:
        app = qtgui.QApplication(sys.argv)
    app.exec_()

