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

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

"""
Module implementing the base class of the VCS project helper.
"""

import os
import sys
import shutil
import copy

from qt import *

from KdeQt import KQFileDialog, KQMessageBox, KQInputDialog

import VCS
from Config import ConfigVcsSystemIndicators, ConfigVcsSystems, \
                       ConfigVcsSystemsDisplay
from CommandOptionsDialog import vcsCommandOptionsDialog

from UI.E3Action import E3Action

import Utilities
import Preferences

class VcsProjectHelper(QObject):
    """
    Class implementing the base class of the VCS project helper.
    """
    def __init__(self, vcsObject, projectObject, parent=None, name=None):
        """
        Constructor
        
        @param vcsObject reference to the vcs object
        @param projectObject reference to the project object
        @param parent parent widget (QWidget)
        @param name name of this object (string or QString)
        """
        QObject.__init__(self, parent, name)
        
        self.vcs = vcsObject
        self.project = projectObject
        
        self.actions = []
        
        self.initActions()
        self.initMenu()
        
    def initActions(self):
        """
        Private method to generate the action objects.
        """
        self.vcsNewAct = E3Action(self.trUtf8('New from repository'),
                self.trUtf8('&New from repository...'), 0, 0, self, 'vcs_new')
        self.vcsNewAct.setStatusTip(self.trUtf8(
            'Create a new project from the VCS repository'
        ))
        self.vcsNewAct.setWhatsThis(self.trUtf8(
            """<b>New from repository</b>"""
            """<p>This creates a new local project from the VCS repository.</p>"""
        ))
        self.vcsNewAct.connectIt(SIGNAL('activated()'),self.vcsCheckout)
        self.actions.append(self.vcsNewAct)
        
        self.vcsExportAct = E3Action(self.trUtf8('Export from repository'),
                self.trUtf8('&Export from repository...'), 0, 0, self, 'vcs_export')
        self.vcsExportAct.setStatusTip(self.trUtf8(
            'Export a project from the repository'
        ))
        self.vcsExportAct.setWhatsThis(self.trUtf8(
            """<b>Export from repository</b>"""
            """<p>This exports a project from the repository.</p>"""
        ))
        self.vcsExportAct.connectIt(SIGNAL('activated()'),self.vcsExport)
        self.actions.append(self.vcsExportAct)
        
        self.vcsAddAct = E3Action(self.trUtf8('Add to repository'),
                self.trUtf8('&Add to repository...'), 0, 0, self, 'vcs_add')
        self.vcsAddAct.setStatusTip(self.trUtf8(
            'Add the local project to the VCS repository'
        ))
        self.vcsAddAct.setWhatsThis(self.trUtf8(
            """<b>Add to repository</b>"""
            """<p>This adds (imports) the local project to the VCS repository.</p>"""
        ))
        self.vcsAddAct.connectIt(SIGNAL('activated()'),self.vcsImport)
        self.actions.append(self.vcsAddAct)
        
        self.cvsLoginAct = E3Action(self.trUtf8('Login to repository'),
                self.trUtf8('Log&in to repository...'), 0, 0, self, 'vcs_login')
        self.cvsLoginAct.setStatusTip(self.trUtf8(
            'Login to repository'
        ))
        self.cvsLoginAct.setWhatsThis(self.trUtf8(
            """<b>Login to repository</b>"""
            """<p>This performs a login to the VCS repository.</p>"""
        ))
        self.cvsLoginAct.connectIt(SIGNAL('activated()'),self.cvsLogin)
        self.actions.append(self.cvsLoginAct)
        
        self.cvsLogoutAct = E3Action(self.trUtf8('Logout from repository'),
                self.trUtf8('Log&out from repository...'), 0, 0, self, 'vcs_logout')
        self.cvsLogoutAct.setStatusTip(self.trUtf8(
            'Logout from repository'
        ))
        self.cvsLogoutAct.setWhatsThis(self.trUtf8(
            """<b>Logout from repository</b>"""
            """<p>This performs a logout from the VCS repository.</p>"""
        ))
        self.cvsLogoutAct.connectIt(SIGNAL('activated()'),self.cvsLogout)
        self.actions.append(self.cvsLogoutAct)
    
    def initMenu(self):
        """
        Private method to generate the VCS menu.
        """
        try:
            menu = self.project.vcsMenu
        except:
            return
        menu.clear()
        
        menu.insertTearOffHandle()
        self.vcsNewAct.addTo(menu)
        self.vcsExportAct.addTo(menu)
        menu.insertSeparator()
        self.vcsAddAct.addTo(menu)
        menu.insertSeparator()
        # build CVS admin submenu
        cvsMenu = QPopupMenu(menu)
        self.cvsLoginAct.addTo(cvsMenu)
        self.cvsLogoutAct.addTo(cvsMenu)
        menu.insertItem(self.trUtf8("CVS Administration"), cvsMenu)

    def handleShowMenu(self):
        """
        Private slot called before the vcs menu is shown.
        """
        self.vcsAddAct.setEnabled(self.project.isOpen())

    def vcsCheckout(self, export=0):
        """
        Private slot used to create a local project from the repository.
        
        @param export flag indicating whether an export or a checkout
                should be performed
        """
        if not self.project.checkDirty():
            return
            
        vcsSelected, ok = KQInputDialog.getItem(\
            self.trUtf8("New Project"),
            self.trUtf8("Select version control system for the project"),
            ConfigVcsSystemsDisplay[1:],
            0, 0)
        vcsSystem = ConfigVcsSystems[ConfigVcsSystemsDisplay.findIndex(vcsSelected)]
        if not ok:
            return
            
        self.project.pdata["VCS"] = [vcsSystem]
        self.project.vcs = self.project.initVCS(vcsSystem)
        if self.project.vcs is not None:
            vcsdlg = self.project.vcs.vcsNewProjectOptionsDialog()
            if vcsdlg.exec_loop() == QDialog.Accepted:
                self.project.closeProject()
                projectdir, vcsDataDict = vcsdlg.getData()
                self.project.pdata["VCS"] = [vcsSystem]
                self.project.vcs = self.project.initVCS(vcsSystem)
                # edit VCS command options
                vcores = KQMessageBox.question(None,
                    self.trUtf8("New Project"),
                    self.trUtf8("""Would you like to edit the VCS command options?"""),
                    self.trUtf8("&Yes"),
                    self.trUtf8("&No"),
                    QString.null,
                    1, -1)
                if vcores == 0:
                    codlg = vcsCommandOptionsDialog(self.vcs)
                    if codlg.exec_loop() == QDialog.Accepted:
                        self.project.vcs.vcsSetOptions(codlg.getOptions())
            
                # create the project directory if it doesn't exist already
                if not os.path.isdir(projectdir):
                    try:
                        os.makedirs(projectdir)
                    except:
                        KQMessageBox.critical(None,
                            self.trUtf8("Create project directory"),
                            self.trUtf8("<p>The project directory <b>%1</b> could not be created.</p>")
                                .arg(projectdir),
                            self.trUtf8("&OK"))
                        self.project.pdata["VCS"] = ['None']
                        self.project.vcs = self.initVCS()
                        return
                
                # create the project from the VCS
                self.project.vcs.vcsSetDataFromDict(vcsDataDict)
                if export:
                    ok = self.project.vcs.vcsExport(vcsDataDict, projectdir)
                else:
                    ok = self.project.vcs.vcsCheckout(vcsDataDict, projectdir, 0)
                if ok:
                    projectdir = os.path.normpath(projectdir)
                    d = QDir(projectdir)
                    plist = d.entryInfoList("*.e3p *.e3pz")
                    if len(plist):
                        if len(plist) == 1:
                            self.project.openProject(plist[0].absFilePath())
                            self.project.emit(PYSIGNAL('newProject'), ())
                        else:
                            pfilenamelist = d.entryList("*.e3p *.e3pz") # same as above
                            pfilename, ok = KQInputDialog.getItem(
                                self.trUtf8("New project from repository"),
                                self.trUtf8("Select a project file to open."),
                                pfilenamelist, 0, 0)
                            if ok:
                                self.project.openProject(QFileInfo(d, pfilename).absFilePath())
                                self.project.emit(PYSIGNAL('newProject'), ())
                        if export:
                            self.project.pdata["VCS"] = ['None']
                            self.project.vcs = self.project.initVCS()
                            self.project.setDirty(1)
                            self.project.saveProject()
                    else:
                        res = KQMessageBox.question(None,
                            self.trUtf8("New project from repository"),
                            self.trUtf8("The project retrieved from the repository"
                                " does not contain a project file (*.e3p *.e3pz)."
                                " Create it automatically?"),
                            self.trUtf8("&Yes"), self.trUtf8("&No"))
                        if res == 0:
                            self.project.ppath = projectdir
                            self.project.opened = 1
                            
                            # search the project directory for files with extensions
                            # of 'py', 'ptl', 'idl', 'ui' and 'ts'
                            for filespec in ['*.py', '*.ptl', '*.idl', '*.ui', '*.ui.h']:
                                files = Utilities.direntries(projectdir, 1, filespec)
                                for file in files:
                                    self.project.appendFile(file)
                                    
                            # special handling for translation files (*.ts)
                            tslist = d.entryList('*.ts')
                            if len(tslist) and tslist[0].contains('_'):
                                # the first entry determines the filename pattern
                                mainscriptname = tslist[0].section('_', 0, 0)
                                for ts in tslist:
                                    if ts.startsWith(mainscriptname):
                                        self.project.pdata["TRANSLATIONS"].append(str(ts))
                                        self.project.emit(PYSIGNAL('projectLanguageAdded'), (str(ts),))
                                if self.project.pdata["PROGLANGUAGE"][0] == "Python":
                                    self.project.pdata["MAINSCRIPT"] = ['%s.py' % unicode(mainscriptname)]
                                elif self.project.pdata["PROGLANGUAGE"][0] == "Ruby":
                                    self.project.pdata["MAINSCRIPT"] = ['%s.rb' % unicode(mainscriptname)]
                                
                            # now save the new project file and reload it
                            self.project.saveProjectAs()
                            self.project.openProject(self.project.pfile)
                            if not export:
                                res = KQMessageBox.question(None,
                                    self.trUtf8("New project from repository"),
                                    self.trUtf8("Shall the project file be added to the repository?"),
                                    self.trUtf8("&Yes"), self.trUtf8("&No"))
                                if res == 0:
                                    self.project.vcs.vcsAdd(self.project.pfile)
                else:
                    KQMessageBox.critical(None,
                        self.trUtf8("New project from repository"),
                        self.trUtf8("""The project could not be retrieved from the repository."""),
                        self.trUtf8("&OK"),
                        QString.null,
                        QString.null,
                        0, -1)

    def vcsExport(self):
        """
        Private slot used to export a project from the repository.
        """
        self.vcsCheckout(1)

    def vcsImport(self):
        """
        Private slot used to import the local project into the repository.
        
        <b>NOTE</b>:
            This does not necvessarily make the local project a vcs controlled
            project. You may have to checkout the project from the repository in
            order to accomplish that.
        """
        pdata_vcs = self.project.pdata["VCS"]
        pdata_vcsoptions = copy.deepcopy(self.project.pdata["VCSOPTIONS"])
        pdata_vcsother = copy.deepcopy(self.project.pdata["VCSOTHERDATA"])
        vcs = self.project.vcs
        vcsHelper = self.project.vcsProjectHelper
        vcsSelected, ok = KQInputDialog.getItem(\
            self.trUtf8("Import Project"),
            self.trUtf8("Select version control system for the project"),
            ConfigVcsSystemsDisplay[1:],
            0, 0)
        vcsSystem = ConfigVcsSystems[ConfigVcsSystemsDisplay.findIndex(vcsSelected)]
        if not ok:
            return
            
        self.project.pdata["VCS"] = [vcsSystem]
        self.project.vcs = self.project.initVCS(vcsSystem)
        if self.project.vcs is not None:
            vcsdlg = self.project.vcs.vcsOptionsDialog(self.project, self.project.name, 1)
            if vcsdlg.exec_loop() == QDialog.Accepted:
                vcsDataDict = vcsdlg.getData()
                # edit VCS command options
                vcores = KQMessageBox.question(None,
                    self.trUtf8("Import Project"),
                    self.trUtf8("""Would you like to edit the VCS command options?"""),
                    self.trUtf8("&Yes"),
                    self.trUtf8("&No"),
                    QString.null,
                    1, -1)
                if vcores == 0:
                    codlg = vcsCommandOptionsDialog(self.project.vcs)
                    if codlg.exec_loop() == QDialog.Accepted:
                        self.project.vcs.vcsSetOptions(codlg.getOptions())
                self.project.setDirty(1)
                self.project.vcs.vcsSetDataFromDict(vcsDataDict)
                self.project.saveProject()
                isVcsControlled = \
                    self.project.vcs.vcsImport(vcsDataDict, self.project.ppath)[1]
                if isVcsControlled:
                    # reopen the project
                    self.project.openProject(self.project.pfile)
                else:
                    # revert the changes to the local project 
                    # because the project dir is not a VCS directory
                    self.project.pdata["VCS"] = pdata_vcs
                    self.project.pdata["VCSOPTIONS"] = copy.deepcopy(pdata_vcsoptions)
                    self.project.pdata["VCSOTHERDATA"] = copy.deepcopy(pdata_vcsother)
                    self.project.vcs = vcs
                    self.project.vcsProjectHelper = vcsHelper
                    self.project.setDirty(1)
                    self.project.saveProject()

    def vcsUpdate(self):
        """
        Private slot used to update the local project from the repository.
        """
        self.vcs.vcsUpdate(self.project.ppath)
        
    def vcsCommit(self):
        """
        Private slot used to commit changes to the local project to the repository.
        """
        if Preferences.getVCS("AutoSaveProject"):
            self.project.saveProject()
        if Preferences.getVCS("AutoSaveFiles"):
            self.project.saveAllScripts()
        self.vcs.vcsCommit(self.project.ppath, '')
        
    def vcsRemove(self):
        """
        Private slot used to remove the local project from the repository.
        
        Depending on the parameters set in the vcs object the project
        may be removed from the local disk as well.
        """
        res = KQMessageBox.warning(None,
            self.trUtf8("Remove project from repository"),
            self.trUtf8("Dou you really want to remove this project from the repository (and disk)?"),
            self.trUtf8("&Yes"), self.trUtf8("&No"), QString.null, 1)
        if res == 0:
            self.vcs.vcsRemove(self.project.ppath, 1)
            self.vcsCommit()
            if not os.path.exists(self.project.pfile):
                ppath = self.project.ppath
                self.setDirty(0)
                self.project.closeProject()
                shutil.rmtree(ppath, 1)
            
    def vcsCommandOptions(self):
        """
        Private slot to edit the VCS command options.
        """
        codlg = vcsCommandOptionsDialog(self.vcs)
        if codlg.exec_loop() == QDialog.Accepted:
            self.vcs.vcsSetOptions(codlg.getOptions())
            self.project.setDirty(1)
        
    def vcsLog(self):
        """
        Private slot used to show the log of the local project.
        """
        self.vcs.vcsLog(self.project.ppath)
        
    def vcsDiff(self):
        """
        Private slot used to show the difference of the local project to the repository.
        """
        self.vcs.vcsDiff(self.project.ppath)
        
    def vcsStatus(self):
        """
        Private slot used to show the status of the local project.
        """
        self.vcs.vcsStatus(self.project.ppath)
        
    def vcsTag(self):
        """
        Private slot used to tag the local project in the repository.
        """
        self.vcs.vcsTag(self.project.ppath)
        
    def vcsRevert(self):
        """
        Private slot used to revert changes made to the local project.
        """
        self.vcs.vcsRevert(self.project.ppath)
        
    def vcsSwitch(self):
        """
        Private slot used to switch the local project to another tag/branch.
        """
        self.vcs.vcsSwitch(self.project.ppath)
        
    def vcsMerge(self):
        """
        Private slot used to merge changes of a tag/revision into the local project.
        """
        self.vcs.vcsMerge(self.project.ppath)
        
    def vcsCleanup(self):
        """
        Private slot used to cleanup the local project.
        """
        self.vcs.vcsCleanup(self.project.ppath)
        
    def vcsCommand(self):
        """
        Private slot used to execute an arbitrary vcs command.
        """
        self.vcs.vcsCommandLine(self.project.ppath)

    ################################################################################
    ## Some cvs specific methods below
    ################################################################################

    def cvsLogin(self):
        """
        Private slot used to login to a remote repository.
        """
        try:
            vcs = VCS.factory("CVS")
        except ImportError:
            return
            
        if not vcs.vcsExists():
            KQMessageBox.critical(None,
                self.trUtf8("Version Control System"),
                self.trUtf8("<p><b>CVS</b> is not available.</p>"),
                self.trUtf8("OK"))
        else:
            vcs.cvsLogin()
        
    def cvsLogout(self):
        """
        Private slot used to logout from a remote repository.
        """
        try:
            vcs = VCS.factory("CVS")
        except ImportError:
            return
            
        if not vcs.vcsExists():
            KQMessageBox.critical(None,
                self.trUtf8("Version Control System"),
                self.trUtf8("<p><b>CVS</b> is not available.</p>"),
                self.trUtf8("OK"))
        else:
            vcs.cvsLogout()
