'''
Name: srd_misc.py

Overview:
    Some misc functions that are commonly used.

Description:
    
    
Notes:

Author: Doug Crane
        May, 2012

Modifications:

'''

__author__ = 'Doug Crane'
__version__ = '1.0'

import sys
import os
import traceback
import pdb
import ctypes
from ctypes.wintypes import MAX_PATH
import tempfile
import errno

import arcpy

from srd_exception import *
import srd_logging

VERBOSE=1

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def setDefaultWorkspaceFGDB(scratchDirPath, recreateFGDB=False, scratchFgdbName='scratch.gdb'):
    '''
    Sets the arcpy.env.workspace variable to an FGDB where geoprocessing data will
    be by default written. The default name for the resulting FGDB is scratch.gdb
    and will be located in the folder pointed to by the scratchDIrPath argument.
    If this folder does not exist or is not writable then the FGDB assigned will
    be taken from the arcpy.env.scratchGDB environment variable.
    
    Parameters:
        scratchDirPath - directory where the scratch FGDB will be created. This
                         does not need to be a folder but can be some other data
                         type such as feature class or FGDB. In this case it will
                         use the folder that the data element you passed is located
                         in to create the scratch FGDB.

        recreateFGDB - if True then if the scratch.gdb already exists it
                       will be deleted and recreated. Default is False.

        scratchFgdbName - name of scratch FGDB to create default: scratch.gdb

    '''

    logChn = srd_logging.SRD_Log()

    # Default to environment scratch FGDB if one passed does not exist.
    if not arcpy.Exists(scratchDirPath):
        arcpy.env.workspace = arcpy.env.scratchGDB
        logChn.logError('\n***\n*** Scratch Folder: %s does not exist or is not writable, defaulting to:\n %s\n***' % (scratchDirPath, arcpy.env.scratchGDB))
        return None
    
    # You may pass a feature class or other arcpy data element type
    # If the data element is not a folder then it will try to find
    # the folder the element is located in.
    desc = arcpy.Describe(scratchDirPath)
    deType = desc.dataElementType
    while deType and deType.lower() != 'defolder':
        scratchDirPath = os.path.dirname(scratchDirPath)
        desc = arcpy.Describe(scratchDirPath)
        deType = desc.dataElementType

    # The folder where the FGDB is to be created must exist.
    if os.path.exists(scratchDirPath) and os.path.isdir(scratchDirPath) and isWritable(scratchDirPath):

        # Make sure there is GDB file extension on FGDB.
        fgdbBaseName,ext = os.path.splitext(scratchFgdbName)
        scratchFgdbName = '%s.gdb' % fgdbBaseName
        
        fgdbPath = os.path.join(scratchDirPath, scratchFgdbName)

        # May need to delete existing FGDB if user wishes to recreate it.
        if arcpy.Exists(fgdbPath) and recreateFGDB:
            if VERBOSE:
                logChn.logMsg('Deleting: %s' % fgdbPath)
                try:
                    # Delete may fail is schema lock on FGDB. It may not delete
                    # the FGDB so the old one is used.
                    RC = arcpy.Delete_management(fgdbPath)
                except arcpy.ExecuteError:
                    logChn.logError('\n***\n*** Cannot delete scratch FGDB: %s\n***' % fgdbPath)
                
        # Create the FGDB if it has been deleted or does not exist.
        if not arcpy.Exists(fgdbPath):
            if VERBOSE:
                logChn.logMsg('Creating: %s' % fgdbPath)
            arcpy.CreateFileGDB_management(scratchDirPath, scratchFgdbName)

        # Set the default workspace to our scratch FGDB.
        arcpy.env.scratchWorkspace = fgdbPath
        arcpy.env.workspace = fgdbPath
        logChn.logMsg('\n***\n*** arcpy.env.workspace has been set to:\n    %s\n***' % fgdbPath)
    else:
        arcpy.env.workspace = arcpy.env.scratchGDB
        logChn.logError('\n***\n*** Scratch Folder: %s does not exist or is not writable, defaulting to:\n %s\n***' % (scratchDirPath, arcpy.env.scratchGDB))

    return arcpy.env.workspace

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def getTempFGDB(tmpFolder=arcpy.env.scratchFolder, fgdbPrefix='tmp_ws'):
    '''
    Creates a tempoary FGDB used to write geoprocessing data to. The name of
    the FGDB will be unique using a prefix for the name followed by an integer
    value that will ensure its uniqueness. The default location for the resulting
    FGDB will be the folder pointed to by the arcpy.env.scratchFolder environment
    variable.

    Parameters
        tmpFolder - the folder you wish the temp FGDB to be created in

        fgdbPrefix - prefix to FGDB name, default is tmp_ws
    '''

    logChn = srd_logging.SRD_Log()

    # Make sure the temp folder exists and can be written to.
    if os.path.exists(tmpFolder) and os.path.isdir(tmpFolder) and isWritable(tmpFolder):
        pass
    else:
        logChn.logWarning('\n*** Cannot use: %s, defaulting to: %s\n***' % (tmpFolder, arcpy.env.scratchFolder))
        tmpFolder = arcpy.env.scratchFolder

    # Create the temp FGDB using the prefix and appending an integer value to name
    # to make it unique in the folder it is to be ctreated in
    tmpGDBPath = arcpy.CreateUniqueName('%s.gdb' % fgdbPrefix, tmpFolder)
    tmpGDBName = os.path.basename(tmpGDBPath)
    arcpy.CreateFileGDB_management(tmpFolder, tmpGDBName)

    logChn.logMsg('\n***\n*** Temp FGDB: %s\n***' % tmpGDBPath)
     
    return tmpGDBPath
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def isWritable(path):
    '''
    Determines if you can write to a folder.

    Parameters:
        path = folder path to test
    '''

    
    try:
        testfile = tempfile.TemporaryFile(dir = path)
        testfile.close()
    except OSError as e:
        if e.errno == errno.EACCES:
            return False
    except IOError as e:
        if e.errno == errno.EEXIST:
            return False
        e.filename = path
        raise
    
    return True
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def setDefaultWorkspaceEnv(defaultPath=None, recreateFGDB=False, scratchFgdbName='scratch.gdb'):
    '''
    Set the default workspace environement for the script.
    This is normally set in the initialization method of
    the class.

    Its main purpose is to set a workspace envrionement that
    will allow creation of scratch feature classes in a given
    FGDB. By setting the arcpy.env.workspace variable you can
    then use simple feature class names that will be created in
    the FGDB workspace that was assigned.

    In addition to setting the default workspace it will also
    set the arcpy.env.scratchWorkspace variable to the system
    temp directory pointed to by tempfile.gettempdir(). This is
    normally c:\temp.

    Parameters:
        defaultPath - the path to the folder where the default
                      scratch workspace will be created. Default
                      will be c:\_LOCALData. If this folder does
                      not exist then it is assigned the temp file
                      directory pointed to by tempfile.gettempdir()
                      which is normally c:\temp

        recreateFGDB - if set to True then any existing scratch
                       FGDB will be deleted and recreated. Default
                       value is False.

    Example:
        # Path to where all scratch work will be done.
        # The FGDB scratch.gdb will be created in this
        # folder and the variable arcpy.env.workspace
        # will be set to point to it.
        self.homePath = r'E:\old_X\nfi\Protection_2018'

        # Set default FGDB and workspace env.
        srd_misc.setDefaultWorkspaceEnv(self.homePath)
    '''
                        
    # If not default folder has been indicated then use _LOCALdata if possible.
    if not defaultPath:
        defaultPath = r'c:\_LOCALdata'
        if not (os.path.exists(defaultPath) and os.path.isdir(defaultPath)):
            tempPath = tempfile.gettempdir()
            if os.path.exists(tempPath) and os.path.isdir(tempPath) and os.access(tempPath, os.W_OK) :
                defaultPath = tempPath

    # Set the default workspace as the scratch workspace.
    setDefaultWorkspaceFGDB(defaultPath, recreateFGDB, scratchFgdbName)
        
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def exceptionDetails(tb):
    '''
    Generates an error message detailing what failed and where.

    Parameters:
        tb - traceback object (sys.exc_info()[2])
    '''

    # tbinfo contains the line number that the code failed on and the code from that line
    tbinfo = traceback.format_tb(tb)[0]
    
    # concatenate information together concerning the error into a message string
    pymsg = 'PYTHON ERRORS:\n %s \n' % tbinfo
    pymsg += 'Error Info:\n %s \n %s' %  (str(sys.exc_type), str(sys.exc_value))

    pymsg += '\nTRACEBACK:\n'
    stackTraceEntries = traceback.extract_stack(tb.tb_frame.f_back) + traceback.extract_tb(tb)
    for entry in traceback.format_list(stackTraceEntries):
        pymsg += entry

    # generate a message string for any geoprocessing tool errors
    gpmsg = '\nArcGIS ERRORS:\n %s' % arcpy.GetMessages()

    return pymsg + gpmsg

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def getLogFilePath(pyScriptPath):
    '''
    Returns a log file path derived from the name of the script.
    The log file will be located in the directory defined by the
    users TEMP environment variable. If this value cannot be obtained
    then an empty string is returned for the log file path.

    Parameters:
        pyScriptPath - name of python script being run (sys.argv[0])
    '''

    # Obtain the location for the My Documents folder and
    # use this as the default location for log files. If
    # this cannot be obtained then use the directory pointed
    # to by the TEMP environment variable.
    dll = ctypes.windll.shell32
    buf = ctypes.create_unicode_buffer(MAX_PATH + 1)
    if dll.SHGetSpecialFolderPathW(None, buf, 0x0005, False):
        tempDir = buf.value
    else:
        tempDir = arcpy.GetSystemEnvironment('TEMP')

    if tempDir:
        baseName = os.path.basename(pyScriptPath)
        scriptName,ext = os.path.splitext(baseName)
        logPath = os.path.join(tempDir, '%s.log' % scriptName)
    else:
        logPath = ''

    return logPath
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def wordWrap(string, width=80, ind1=0, ind2=0, prefix=''):
    '''
    Provides word wrapping function.

    Parameters:
        string: the string to wrap
        
        width: the column number to wrap at
        
        prefix: prefix each line with this string (goes before any indentation)
        
        ind1: number of characters to indent the first line
        
        ind2: number of characters to indent the rest of the lines

    Examples:
    
    '''
    
    string = prefix + ind1*" " + string
    newstring = ""
    if len(string) > width:
        while True:
            # find position of nearest whitespace char to the left of "width"
            marker = width-1
            while not string[marker].isspace():
                marker = marker - 1

            # remove line from original string and add it to the new string
            newline = string[0:marker] + "\n"
            newstring = newstring + newline
            string = prefix + ind2*" " + string[marker+1:]

            # break out of loop when finished
            if len(string) <= width:
                break
    
    return newstring + string
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def printMsg(msg):
    print(msg)
    arcpy.AddMessage(msg)

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~        
if __name__ == '__main__':

##    a = srd_logging.SRD_Log(r'd:\tmp\_tstLog.txt')
##    a.logMsg('This is from a')
##             
##    b = srd_logging.SRD_Log()
##    b.logMsg('test this is from b')
##    
##    a = None
##    b = None

    myPathArg = r'E:\old_X\nfi\Protection_2018'
    ##setDefaultWorkspaceEnv(defaultPath=myPathArg, recreateFGDB=True)
    ##setDefaultWorkspaceEnv(recreateFGDB=True)
    ##setDefaultWorkspaceEnv()

    ##isWritable(r'C:\Users\Default User')
    
    ##print(getTempFGDB(tmpFolder=r'C:\Users\Default User'))
    ##print(getTempFGDB())

    pathArg = r'E:\prjs\DataDirectives\_Audit_Tests\NORI_CCP_20181005\NORI_CCP_20181005\NORI_CCP_20181005.shp'
    pathArg = r'E:\prjs\DataDirectives\_Audit_Tests\NORI_CCP_20181005\NORI_CCP_20181005'
    pathArg = r'E:\prjs\DataDirectives\_Audit_Tests\NORI_CCP_20181005\NORI_CCP_yyyymmdd.gdb'
    pathArg = r'E:\prjs\DataDirectives\_Audit_Tests\NORI_CCP_20181005\NORI_CCP_yyyymmdd.gdb\FINAL_HARV_AREA'
    pathArg = r'E:\prjs\DataDirectives\_Audit_Tests\NORI_CCP_20181005\OWNERSHIP_UTM_12_Clip_sngl.lyr'
    pathArg = r'E:S'
    ##desc = arcpy.Describe(pathArg)
    ##print desc.dataElementType
    ##setDefaultWorkspaceFGDB(pathArg)
    
    ##pdb.set_trace()
    import pwd
    pathArg = r'E:\prjs\DataDirectives\_Audit_Tests\NORI_CCP_20181005\NORI_CCP_20181005\NORI_CCP_20181005.shp'
    st = os.stat(pathArg)
    print pwd.getpwuid(st.st_uid).pw_name
    pdb.set_trace()
