@python -x %~f0 %* & goto :EOF


import DCOracle
import os
import re
import string
import sys
import time
import win32api
import win32con
import msvcrt


from com.creo.araxi.database import GetAccountInfo
from pywintypes import FALSE, TRUE

EXIT_SUCCESS = 0
EXIT_FAILURE = 1
RESERVED = 0
NOPRINT = FALSE

SKIP = 'S'
CONTINUE = 'C'
QUIT = 'Q'

SYSTEM_JCKDID = '!!!!!!!!!!!!!!!!!!!!'

# Help screen
USAGE_TEXT = \
'''\
Check for conflicting files before merging two volumes using PChangeJobShare.

USAGE:    CheckConflicts SERVER1 SHARE1 SERVER2 SHARE2

'''

def main():
    params = GetOpts()
    if len( params ) != 4:
       print 'Wrong number of parameters!'
       PrintUsageAndExit()
    server1 = string.upper( params[0] )
    share1  = string.upper( params[1] )
    server2 = string.upper( params[2] )
    share2  = string.upper( params[3] )

    machineName = win32api.GetComputerName()

    node = PrinergyNodeInfo( None )
    node.Query()
    primaryServer = string.upper( node.GetPrimaryNodeName() )
    prinergyVersion = node.GetCurrentRelease()

    if server1 == primaryServer:
        server1 = '%PRIMARY%'
    if server2 == primaryServer:
        server2 = '%PRIMARY%'

    Log( '*********************************', NOPRINT )
    Log( 'Checking for file conflicts between %s and %s on %s.'\
         % ( os.path.join( '\\\\', server1, share1), os.path.join( '\\\\', server2, share2 ), machineName ) )

    conflicts = Conflicts( server1, share1, server2, share2 )

    print 'Cleaning up destroyed jobs.'
    conflicts.CleanUpDestroyedJobs()

    print 'Searching for conflicts. Please wait...'
    conflictingFiles = conflicts.FindConflictingFiles( )

    if len( conflictingFiles ) > 0:
        Log( 'Conflicting files:' )
        for file in conflictingFiles:
            Log( '    %s' % file )
    else:
        Log( 'No conflicting files found.' )

    Log( 'There are %s conflict(s).' % len( conflictingFiles ) )

    if len( conflictingFiles ) > 0:
        print
        print 'Searching for jobs that contain the conflicting files. Please wait...'
        print 'Press any key if you wish to interactively check jobs or quit.'
        print

        # Find which jobs are using those files
        interactive = FALSE
        for file in conflictingFiles:
            #Give the user the option to skip a file if it's taking too long
            if msvcrt.kbhit() == TRUE or interactive == TRUE:
                if interactive == FALSE:
                    print 'Entering interactive mode. Press C at the prompt to exit interactive mode.'
                    interactive = TRUE
                print 'Will find jobs using %s' % file
                userInput = ''
                while userInput not in [ SKIP, CONTINUE, QUIT ]:
                    userInput = string.upper( raw_input( 'Options: S - skip this file, C - check all files, Q- quit : ' ) )
                if userInput == SKIP:
                    continue
                elif userInput == CONTINUE:
                    interactive = FALSE
                else:
                    break
            Log( 'Conflicting file: %s' % file )
            jobs = conflicts.FindJobsUsingFile( file )
            for job in jobs:
                Log( '    Found in job: %s' % job )

    Log( 'Completed check.' )


def Log( message = '\n', printToScreen = TRUE ):
    ( path, file ) = os.path.split( sys.argv[ 0 ] )
    logFile = os.path.join( path, re.sub( '\..*$', '.log', file ) )
    hLogFile = open( logFile, 'a' )
    if printToScreen == TRUE:
        print message
    message = '%s\t %s\n' % ( time.strftime( '%Y %b %d %H:%M:%S', time.localtime( time.time() ) ), message )
    hLogFile.write( message )
    hLogFile.close()


# Get information about files that are common between two shares
class Conflicts:
    def __init__( self, server1, share1, server2, share2 ):
        self.server1 = server1
        self.share1  = share1
        self.server2 = server2
        self.share2  = share2
        self.sql = Db()

        node = PrinergyNodeInfo( None )
        node.Query()
        self.primaryServer = string.upper( node.GetPrimaryNodeName() )
        self.jobRootFolderPathPrefix = GetJobRootFolderPathPrefix()

        # dictionary of file paths, file ids and file locations
        # format: key = path of file including Jobs\<job name>\path of file
        #               The path always includes Jobs\<job name>\ even for relative file locations
        #         offset 0 = list of file ids
        #         offset 1 = list of unique server/share where the files are located
        self.fileInfo = {}

        # fileInfo pointers
        self.ID = 0
        self.PATHROOT = 1

        # dictionary of job information
        # format: key = jacket id
        #         offset 0 = server name
        #         offset 1 = share name
        #         offset 2 = path name
        #         offset 3 = job name
        self.jacketInfo = {}

        # jacketInfo pointers
        self.SERVERNAME = 0
        self.SHARENAME  = 1
        self.PATHNAME   = 2
        self.JCKDNAME   = 3

    def CleanUpDestroyedJobs( self ):
        self.sql.procedures.DBCS_CleanUpDestroyedJobs_D()

    def GetJobInfo( self ):
        query = \
        '''\
            SELECT jckdId,
                   jckdName,
                   UPPER( jckdRootFolderServerName ),
                   UPPER( jckdRootFolderShareName ),
                   jckdRootFolderPathName
            FROM   DBCS_Jacket
        '''
        # print query

   
        for ( jckdId, jckdName, jckdServerName, jckdShareName, jckdPathName ) in self.sql.Query( query ):
            self.jacketInfo[ jckdId ] = jckdServerName, jckdShareName, jckdPathName, jckdName


    def GetAllFileInfo( self ):

        self.GetJobInfo()

        # create a list of file locations
        query = \
        '''\
            SELECT regfileId, 
                   UPPER( regfileServerName ),
                   UPPER( regfileShareName ), 
                   regfilePathName, 
                   regfileContainingJckdId
            FROM   DBCS_RegisteredFile
        '''

        self.sql.Execute( query)

        while 1:
            results = self.sql.FetchOne( )
            if results == None:
                break

            fileId, fileserverName, shareName, pathName, containingJckdId = results

            if pathName == None:
                continue

            if containingJckdId != SYSTEM_JCKDID:
                # job-relative file, get server/share names and prepend "Jobs\<jobname>" to path
                fileserverName = self.jacketInfo[ containingJckdId ][ self.SERVERNAME ] 
                shareName = self.jacketInfo[ containingJckdId ][ self.SHARENAME ]
                pathName = os.path.join( self.jobRootFolderPathPrefix, self.jacketInfo[ containingJckdId ][ self.JCKDNAME ], pathName )

            if fileserverName == self.primaryServer:
                fileserverName = '%PRIMARY%'
            
            if self.fileInfo.has_key( pathName ):                   # append new file id to list
                fileIds = self.fileInfo[ pathName ][ self.ID ]
                fileIds = fileIds.append( fileId )

                paths = self.fileInfo[ pathName ][ self.PATHROOT ]
                if not ( fileserverName, shareName ) in paths:      # only add path if it isn't in the list
                    paths.append( fileserverName, shareName )
            else:                                                   # add new entry to dictionary
                self.fileInfo[ pathName ] = ( [ fileId ], [ ( fileserverName, shareName ) ] )

        # add all font and image search paths to the list of file locations
        query = \
        '''\
            SELECT jckdId,
                   UPPER( jacketLocListItemServerName ),
                   UPPER( jacketLocListItemShareName ),
                   jacketLocListItemPathName,
                   jacketLocListItemContainJckdId
            FROM DBCS_JacketLocationListItem
        '''

        query1 = \
        '''\
            SELECT jckdId,
                   UPPER( fileserverName ),
                   UPPER( shareName ),
                   pathName
            FROM DBCS_JacketLocListItem_R
        '''

#        for ( jckdId, fileserverName, shareName, pathName ) in self.sql.Query( query1 ):
#        for ( jckdId, fileserverName, shareName, pathName, containingJckdId ) in self.sql.Query( query ):

        self.sql.Execute( query)
        while 1:
            results =self.sql.FetchOne( )
            if results == None:
                break
            jckdId = results[0]
            fileserverName =  results[1]
            shareName = results[2]
            pathName = results[3]
            containingJckdId = results[4]

            if pathName == None:
                continue
            if fileserverName == ' ':
                fileserverName = self.jacketInfo[ containingJckdId ][ self.SERVERNAME ]
            if shareName == ' ':
                shareName = self.jacketInfo[ containingJckdId ][ self.SHARENAME ]

            if containingJckdId != SYSTEM_JCKDID:
                # job-relative path, get server/share names and prepend "Jobs\<jobname>" to path
                fileserverName = self.jacketInfo[ containingJckdId ][ self.SERVERNAME ] 
                shareName = self.jacketInfo[ containingJckdId ][ self.SHARENAME ]
                pathName = os.path.join( self.jobRootFolderPathPrefix, self.jacketInfo[ containingJckdId ][ self.JCKDNAME ], pathName )

            if self.fileInfo.has_key( pathName ):
                paths = self.fileInfo[ pathName ][ self.PATHROOT ]
                if not ( fileserverName, shareName ) in paths:
                    paths.append( fileserverName, shareName )
            else:
                self.fileInfo[ pathName ] = ( [ None ], [ ( fileserverName, shareName ) ] )
            

    def FindConflictingFiles( self ):

        self.GetAllFileInfo()

        conflictingFiles = []
        for file in self.fileInfo.keys():
            paths = self.fileInfo[ file ][ self.PATHROOT ]
            if len( paths ) > 1:
                if ( self.server1, self.share1) in paths and ( self.server2, self.share2) in paths:
                    conflictingFiles.append( file )
        return conflictingFiles

    def FindJobsUsingFile( self, file ):
        fileIds = self.GetFileIdsForFile( file )
        jobs = []
        for fileId in fileIds:
            jobs.extend( self.JobsUsingFile( fileId, file ) )
        return jobs


    def GetFileIdsForFile( self, file ):
         return self.fileInfo[ file ][ self.ID ]


    def JobsUsingFile( self, fileId, fileName ):
        fileId = re.sub( '\'','\'\'', fileId )  # replace quote characters with double quotes
        jobNames = []
        if fileId <> None:
            # This query finds the jobs that use the registered file 
            query = \
                '''\
                    SELECT jckdName
                    FROM   DBCS_Jacket
                    WHERE  jckdId
                    IN (
                          SELECT jckdId
                          FROM DBCS_RegisteredFileUsage
                          WHERE regfileId = \'%s\'
                       )
                ''' % ( fileId )

            # print query
            results = self.sql.Query ( query )

            for ( jobName, ) in results:
                jobNames.append( jobName )
        else:
            # fileId is None, so it must be a search path
            query = \
                '''\
                    SELECT jckdName
                    FROM   DBCS_Jacket
                    WHERE  jckdId
                    IN (
                          SELECT jckdId
                          FROM DBCS_JacketLocListItem_R
                          WHERE pathName = \'%s\'
                       )
                ''' % ( fileName )

            # print query
            results = self.sql.Query ( query )

            for ( jobName, ) in results:
                jobNames.append( jobName )

        return jobNames
        

#
# Get basic information about a Prinergy node.
# Divided into 4 steps:
# 1) Constructor just stores arguments
# 2) Query retrieves information, possibly raising exception
# 3) Format produces text, but does not write to a particular I/O device
# 4) Print writes formatted text to stdout
#
class PrinergyNodeInfo:
    def __init__( self, nodename ):
        self.nodename = NodeName( nodename )
        self.queried = FALSE
        
    def Query( self ):
        hklm = win32api.RegConnectRegistry( self.nodename, win32con.HKEY_LOCAL_MACHINE )
    
        subkey_Araxi = 'Software\\Creo\\Araxi'
        self.CurrentRelease = MyRegQueryValue( hklm, subkey_Araxi, 'CurrentRelease', win32con.REG_SZ )
    
        subkey_Araxi_CurrentRelease = subkey_Araxi + '\\' + self.CurrentRelease
        subkey_Araxi_CurrentRelease_Default = subkey_Araxi_CurrentRelease + '\\' + 'Default'
        subkey_Araxi_CurrentRelease_Default_System = subkey_Araxi_CurrentRelease_Default + '\\' + 'System'
        
        self.PrimaryNodeName = MyRegQueryValue( hklm, subkey_Araxi_CurrentRelease_Default_System, 'PrimaryNodeName', win32con.REG_SZ )
        self.NodeList = MyRegQueryValue( hklm, subkey_Araxi_CurrentRelease_Default_System, 'NodeList', win32con.REG_SZ )
        self.AraxiRootDirectory = MyRegQueryValue( hklm, subkey_Araxi_CurrentRelease_Default_System, 'AraxiRootDirectory', win32con.REG_SZ )
        try:
            # if the machine is a Emergency Plate Making (EPM) machine the OriginalPrimaryNodeName value will exist
            self.OriginalPrimaryNodeName = MyRegQueryValue( hklm, subkey_Araxi_CurrentRelease_Default_System, 'OriginalPrimaryNodeName', win32con.REG_SZ )
        except:
            self.OriginalPrimaryNodeName = None
        
        subkey = 'System\\CurrentControlSet\\Control\\Session Manager\\Environment'
        self.AraxiHomeEnvVar = MyRegQueryValue( hklm, subkey, 'AraxiHome', win32con.REG_SZ )
        
        subkey = 'System\\CurrentControlSet\\Services\\LanmanServer\\Shares'
        AraxiHomeShareInfo = MyRegQueryValue( hklm, subkey, 'AraxiHome', win32con.REG_MULTI_SZ )
        m = re.match( 'Path=(.+)', AraxiHomeShareInfo[1] )
        if m:
            self.AraxiHomeSharePath = m.group( 1 )
        else:
            self.AraxiHomeSharePath = '<Unavailable>'
        
        self.queried = 1
       
    
    def GetPrimaryNodeName( self ):
        if not self.queried:
            return '<Not queried>'

        return self.PrimaryNodeName
        
    def GetNodeName( self ):
        return self.nodename

    def GetCurrentRelease( self ):
        return self.CurrentRelease

def GetJobRootFolderPathPrefix( nodename = None ):
    hklm = win32api.RegConnectRegistry( nodename, win32con.HKEY_LOCAL_MACHINE )
  
    subkey_Araxi = 'Software\\Creo\\Araxi'
    currentRelease = MyRegQueryValue( hklm, subkey_Araxi, 'CurrentRelease', win32con.REG_SZ )
    
    subkey_Araxi_CurrentRelease = subkey_Araxi + '\\' + currentRelease
    subkey_Araxi_CurrentRelease_Default = subkey_Araxi_CurrentRelease + '\\' + 'Default'
    subkey_Araxi_CurrentRelease_Default_JobD = subkey_Araxi_CurrentRelease_Default + '\\' + 'JOBD'
    rootFolderPathPrefix = MyRegQueryValue( hklm, subkey_Araxi_CurrentRelease_Default_JobD, 'JobRootFolderPathPrefix', win32con.REG_SZ )

    return re.sub( '\\$', '', rootFolderPathPrefix )


#
# produce an NT computer name in 'canonical' form, without leading backslashes
# optional argument may have leading or missing double backslashes
# '.', '' or None is interpreted as the local computer
#
def NodeName( name=None ):
    nodename = ''
    if name:
        nodename = name
    if nodename[:2] == '\\\\':
        nodename = nodename[2:]
        if not nodename:
            nodename = name
            #raise ValueError( 'invalid node name' )
    if nodename == '.':
        nodename = ''
    if not nodename:
        nodename = win32api.GetComputerName()
    return string.upper( nodename )


def MyRegQueryValue( rootkey, subkeypath, valuename, expectedtype=None ):
    '''
    get a registry value and type, optionally checking for expected type,
    and formatting a nice error message if not found or wrong type
    '''
    hkey = None
    try:
        try:
            hkey = win32api.RegOpenKey( rootkey, subkeypath, RESERVED, win32con.KEY_QUERY_VALUE )
        except win32api.error, err:
            if err[0] == winerror.ERROR_FILE_NOT_FOUND:
                raise KeyError( 'registry key not found' )
            else:
                raise err
                
        try:
            v, t = win32api.RegQueryValueEx( hkey, valuename )
            if expectedtype and t != expectedtype:
                raise TypeError( 'wrong registry value type found: %d expected: %d' % ( t, expectedtype ) )
            return v
        except win32api.error, err:
            if err[0] == winerror.ERROR_FILE_NOT_FOUND:
                raise KeyError( 'registry value not found' )
            else:
                raise err
    except win32api.error, err:
        if err[0] == winerror.ERROR_ACCESS_DENIED: # show user friendly message when access is denied
            raise Exception( 'Registry access denied, please log in as an administrator and try again!' )
        else:
            raise



def GetOpts():
    argc = len( sys.argv )
    i = 1
    args = []
    while i < argc:
        arg = sys.argv[i]
        argUC = string.upper( arg )
        if argUC in [ '-?', '-H', '-HELP' ]:
            PrintUsageAndExit()
        else:
            args.append( sys.argv[ i ] )
        i = i + 1
    return args

class Db:
    def __init__( self ):
        user, password, alias = GetAccountInfo.OracleAccountInfo()
        oracleLogin = user + '/' + password + '@' + alias
        self.dbConnection = DCOracle.Connect( oracleLogin )
        self.dbCursor = self.dbConnection.cursor()
        self.procedures = self.dbConnection.procedures

    def Query( self, query ):
        self.dbCursor.execute( query )
        return self.dbCursor.fetchall()

    def Execute( self, command ):
        self.dbCursor.execute( command )

    def FetchOne( self ):
        return self.dbCursor.fetchone()

    def Commit( self ):
        self.dbConnection.commit()

    def Close( self ):
        self.dbConnection.close()

def PrintUsageAndExit( errmsg='', exitcode=EXIT_SUCCESS ):
    if errmsg:
        print errmsg
        print
    print USAGE_TEXT
    sys.exit( exitcode )

if __name__ == '__main__':
    main()

