# -*- coding: utf-8 -*-

# Copyright (c) 2004 - 2006 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing a dialog showing an imports diagram of a package.
"""

import glob
import os

import sys

from qt import *
from qtcanvas import *

from KdeQt.KQProgressDialog import KQProgressDialog

from UMLCanvasView import UMLCanvasView
from Utilities.ModuleParser import readModule
from ModuleWidget import ModuleWidget, ModuleModel
from AssociationWidget import AssociationWidget, Imports
import GraphicsUtilities
import Utilities

class ImportsDiagram(QDialog):
    """
    Class implementing a dialog showing an imports diagram of a package.
    
    Note: Only package internal imports are show in order to maintain
    some readability.
    """
    allClasses = {}
    
    def __init__(self,package,parent = None,name = None,modal = 0,fl = 0):
        """
        Constructor
        
        @param package name of a python package to show the import 
            relationships (string)
        @param parent parent widget of the view (QWidget)
        @param name name of the view widget (QString or string)
        @param flags the window flags to be passed to the view widget
        """
        QDialog.__init__(self,parent,name,modal,fl)

        if not name:
            self.setName("ImportsDiagram")

        UMLFormLayout = QVBoxLayout(self,6,6,"UMLFormLayout")

        self.canvas = QCanvas(800, 600)
        self.umlCanvas = UMLCanvasView(self.canvas,self,"umlCanvas")
        self.umlCanvas.setGeometry(QRect(6,6,788,555))
        UMLFormLayout.addWidget(self.umlCanvas)

        layout1 = QHBoxLayout(None,0,6,"layout1")
        spacer = QSpacerItem(40,20,QSizePolicy.Expanding,QSizePolicy.Minimum)
        layout1.addItem(spacer)

        self.closeButton = QPushButton(self,"closeButton")
        layout1.addWidget(self.closeButton)
        spacer_2 = QSpacerItem(40,20,QSizePolicy.Expanding,QSizePolicy.Minimum)
        layout1.addItem(spacer_2)
        UMLFormLayout.addLayout(layout1)

        self.languageChange()

        self.resize(QSize(800,604).expandedTo(self.minimumSizeHint()))
        self.clearWState(Qt.WState_Polished)

        self.connect(self.closeButton,SIGNAL("clicked()"),self,SLOT("close()"))
        
        self.packagePath = Utilities.normabspath(package)
        self.package = self.packagePath.replace(os.sep, '.')[1:]


    def languageChange(self):
        """
        Private method used to show the localized strings for this dialog.
        """
        self.setCaption(self.__tr("Imports-Diagram"))
        self.closeButton.setText(self.__tr("&Close"))
        self.closeButton.setAccel(self.__tr("Alt+C"))


    def __tr(self,s,c = None):
        """
        Private method to translate the display strings.
        """
        return qApp.translate("ImportsDiagram",s,c)


    def getDiagramName(self):
        """
        Method to retrieve a name for the diagram.
        
        @return name for the diagram
        """
        return self.packagePath
        
    def buildModulesDict(self):
        """
        Private method to build a dictionary of modules contained in the package.
        
        @return dictionary of modules contained in the package.
        """
        moduleDict = {}
        modules = glob.glob(Utilities.normjoinpath(self.packagePath,'*.py')) + \
                  glob.glob(Utilities.normjoinpath(self.packagePath,'*.ptl'))
        tot = len(modules)
        try:
            prog = 0
            progress = KQProgressDialog(self.trUtf8("Parsing modules..."),
                QString.null, tot, None, None, 1)
            progress.show()
            qApp.processEvents()
            for module in modules:
                progress.setProgress(prog)
                qApp.processEvents()
                prog = prog + 1
                if module.endswith("__init__.py"):
                    continue
                try: 
                    mod = readModule(module)
                except ImportError:
                    continue
                else:
                    name = mod.name
                    if name.startswith(self.package):
                        name = name[len(self.package) + 1:]
                    moduleDict[name] = mod
        finally:
            progress.setProgress(tot)
        return moduleDict
        
    def buildImports(self):
        """
        Private method to build the modules shapes of the diagram.
        """
        if not os.path.exists(os.path.join(self.packagePath, '__init__.py')):
            ct = QCanvasText(self.trUtf8("The directory '%1' is not a Python package.")
                                 .arg(self.packagePath), self.canvas)
            ct.setX(10)
            ct.setY(10)
            ct.show()
            return
        
        shapes = {}
        p = 10
        y = 10
        maxHeight = 0
        
        modules = self.buildModulesDict()
        sortedkeys = modules.keys()
        sortedkeys.sort()
        for module in sortedkeys:
            impLst = []
            for i in modules[module].imports:
                if i.startswith(self.package):
                    n = i[len(self.package) + 1:]
                else:
                    n = i
                if modules.has_key(i):
                    impLst.append(n)
            for i in modules[module].from_imports.keys():
                if i.startswith(self.package):
                    n = i[len(self.package) + 1:]
                else:
                    n = i
                if modules.has_key(i):
                    impLst.append(n)
            classNames = []
            for cls in modules[module].classes.keys():
                className = modules[module].classes[cls].name
                if className not in classNames:
                    classNames.append(className)
            shape = self.addModule(module, classNames, p, y)
            shapes[module] = (shape, impLst)
            p += shape.width() + 10
            maxHeight = max(maxHeight, shape.height())
            if p > self.canvas.width():
                p = 10
                y += maxHeight + 10
                maxHeight = 0
                
        self.createAssociations(shapes)
        
        # resize the canvas to accomodate the widgets
        rect = self.umlCanvas.getDiagramRect(10) # 10 pixel border
        newSize = self.canvas.size()
        if rect.width() > newSize.width():
            newSize.setWidth(rect.width())
        if rect.height() > newSize.height():
            newSize.setHeight(rect.height())
        self.canvas.resize(newSize.width(), newSize.height())
        
    def addModule(self, name, classes, x, y):
        """
        Private method to add a module to the diagram.
        
        @param name module name to be shown (string)
        @param classes list of class names contained in the module
            (list of strings)
        @param x x-coordinate (integer)
        @param y y-coordinate (integer)
        """
        classes.sort()
        impM = ModuleModel(name, classes)
        impW = ModuleWidget(self.canvas, impM, x, y)
        impW.show()
        rect = impW.rect()
        newSize = self.canvas.size()
        if rect.bottom() > newSize.height():
            newSize.setHeight(rect.bottom()+10)
        self.canvas.resize(newSize.width(), newSize.height())
        return impW
        
    def createAssociations(self, shapes):
        """
        Private method to generate the associations between the module shapes.
        
        @param shapes list of shapes
        """
        for module in shapes.keys():
            for rel in shapes[module][1]:
                assoc = AssociationWidget(self.canvas, 
                        shapes[module][0], shapes[rel][0],
                        Imports)
                assoc.show()
                
    def show(self):
        """
        Overriden method to show the dialog.
        """
        self.buildImports()
        QDialog.show(self)
        self.allClasses.clear()
        
    def relayout(self):
        """
        Method to relayout the diagram.
        """
        self.buildImports()
