"""
    @author: ArcGIS for Utilities
    @contact: ArcGISTeamUtilities@esri.com
    @company: Esri
    @version: 1.0
    @description: Used to prep data for reporting and other geoprocessing task by copying datasets from enterprise to
                  local geodatabases.
    @requirements: Python 2.7.x, ArcGIS 10.2
    @copyright: Esri, 2015
    @Usage: temp = DataPrep.DataPrep(configFilePath="path to config file") to initialize
            (each of the below can be called independently)
            temp.CopyData()
            This can also be called from a different application.  Just need to pass in the config as a DICT object
"""
import arcpy
import os
import sys
import common as Common
import subprocess

class DataPrepError(Exception):
    """ raised when error occurs in utility module functions """
    pass
#----------------------------------------------------------------------
def trace():
    """
        trace finds the line, the filename
        and error message and returns it
        to the user
    """
    import traceback, inspect
    tb = sys.exc_info()[2]
    tbinfo = traceback.format_tb(tb)[0]
    filename = inspect.getfile(inspect.currentframe())
    # script name + line number
    line = tbinfo.split(", ")[1]
    # Get Python syntax error
    #
    synerror = traceback.format_exc().splitlines()[-1]
    return line, filename, synerror
class DataPrep:

    overWrite = None
    databases = None
    start_db =  None
    end_db = None
    datasetsToInclude = None       
    standaloneFeatures = None
    calledFromApp = None
    postExtractGP = None

    def __init__(self,configFilePath=""):
    
        # This checks to see whether this Data Prep is ran as a standalone or as a sub process in another application
        if configFilePath and configFilePath != "":
            if type(configFilePath) is dict:
                configParams = configFilePath
                self.calledFromApp = True
            else:
                configParams = Common.init_config_json(config_file=configFilePath)
                self.calledFromApp = False
        
            if "Databases" in configParams:
                self.databases = configParams["Databases"]
                
            return None
        else:
            print "Error, no config file path specified."
            return False
 
    def CopyData(self):
        try:
            print "************ BEGIN Data Copy****************"
            # Read the config values and store in local variables then start extraction
            # It all depends on if it can create a GDB, if not, all other processes are bypassed.
            for database in self.databases:
                self.overWrite = None
                self.databases = None
                self.start_db =  None
                self.end_db = None
                self.datasetsToInclude = None       
                self.standaloneFeatures = None
                self.postExtractGP = None
                retVal = True
                             
                if "GDBPath" in database and "SDEPath" in database:
                    
                    #workspaceProp = arcpy.Describe(database["GDBPath"])
                    if (database["GDBPath"].lower()).find(".sde") == -1:
                    #if (workspaceProp.workspaceType == "LocalDatabase"):
                        self.start_db = database["SDEPath"]
                        self.end_db = database["GDBPath"]
                        self.overWrite = database["Overwrite"]
                        if self._CheckCreateGDBProcess():
                            if "DataSets" in database:
                                if database["DataSets"]:
                                    self.datasetsToInclude = database["DataSets"]
                                    retVal = self._CopyDatasetsProcess()
                            if "FeatureClasses" in database:
                                if database["FeatureClasses"]:
                                    self.standaloneFeatures = database["FeatureClasses"]
                                    retVal = self._CopyDataTypeProcess(type="FeatureClasses") 
                            if "Tables" in database:
                                if database["Tables"]:
                                    self.standaloneFeatures = database["Tables"]
                                    retVal = self._CopyDataTypeProcess(type="Tables")  
                    else:
                        print "The output geodatabase must be a file geodatabase" 
                        retVal = False  
                if "PostProcesses" in database:
                    if database["PostProcesses"]:
                        self.postExtractGP = database["PostProcesses"]
                        retVal = self._executePostProcess()                                          
            print "************ END Data Copy ****************"
            return retVal
        except arcpy.ExecuteError:
            line, filename, synerror = trace()
            raise DataPrepError({
                "function": "CopyData",
                "line": line,
                "filename":  filename,
                "synerror": synerror,
                "arcpyError": arcpy.GetMessages(2),
            })
        except (DataPrepError),e:
            raise e
        except:
            line, filename, synerror = trace()
            raise DataPrepError({
                "function": "CopyData",
                "line": line,
                "filename":  filename,
                "synerror": synerror,
            })        
    def _CopyDatasetsProcess(self): 
        try: 
            if self.datasetsToInclude: 
                #Set workspaces
                arcpy.env.workspace = self.start_db
                wk2 = self.end_db
                datasetList = arcpy.ListDatasets()
                
                #Check GDB if not created already, create it now
                if self._CheckCreateGDBProcess():
                
                    for dataset in datasetList:
                        if arcpy.Exists(dataset = dataset):
                            
                            name = arcpy.Describe(dataset)
                            new_data=name.name.split('.')[-1]
                            
                            # if user specified *, then user wants all datasets and child objects copied
                            if "*" in self.datasetsToInclude and len(self.datasetsToInclude) == 1:
                                #print "Reading: {0}".format(dataset)
                                if arcpy.Exists(wk2 + os.sep + new_data)==False:      
                                    arcpy.Copy_management(dataset, wk2 + os.sep + new_data)
                                    print "Created Dataset {0} and all childs in local gdb".format(new_data)
                            else:
                            # If a list of dataset names is stated, check to see if it iterating dataset is in that list
                                for checkDS in self.datasetsToInclude:                               
                                    if new_data == checkDS["Name"]:
                                        print "Reading: {0}".format(dataset)
                                        if arcpy.Exists(wk2 + os.sep + new_data)==False:
                                            
                                            if "*" in checkDS["FeatureClasses"] and len(checkDS["FeatureClasses"]) == 1:
                                                arcpy.Copy_management(dataset, wk2 + os.sep + new_data)
                                                print "Created Dataset {0} and all childs in local gdb".format(new_data)                                           
                                            else:
                                                #Create the dataset envelope. Creating and not copying because user might not want all
                                                #features copied into this dataset
                                                arcpy.CreateFeatureDataset_management(self.end_db, new_data, dataset)
                                                print "Created Dataset {0} in local gdb".format(new_data)
                                                
                                                #Handles child features of the datset.  Can either copy all or user defined features
                                                if name.children:
                                                    self._CheckChildFeatures(ds=name.name,childList=name.children,checkList=checkDS["FeatureClasses"])
                                        else:                            
                                            #Handles child features of the datset if dataset already exist.  Only copy new ones
                                            if name.children:
                                                self._CheckChildFeatures(ds=name.name,childList=name.children,checkList=checkDS["FeatureClasses"])                            
                                            print "Dataset {0} already exists in the end_db checking for childs".format(new_data) 
                        else:
                            raise DataPrepError({
                                "function": "_CopyDatasetsProcess",
                                "line": 125,
                                "filename":  'dataprep',
                                "synerror": "%s does not exist" % dataset
                            }   )                                     
                    #Clear memory
                    del dataset
            return True
        except arcpy.ExecuteError:
            line, filename, synerror = trace()
            raise DataPrepError({
                "function": "_CopyDatasetsProcess",
                "line": line,
                "filename":  filename,
                "synerror": synerror,
                "arcpyError": arcpy.GetMessages(2),
            }
                            )
        except:
            line, filename, synerror = trace()
            raise DataPrepError({
                "function": "_CopyDatasetsProcess",
                "line": line,
                "filename":  filename,
                "synerror": synerror,
            }
                                )     
    
    def _CopyDataTypeProcess(self,type="FeatureClasses",ds="",fc=""):
        try:   
            #Set workspaces
            arcpy.env.workspace = self.start_db
            wk2 = self.end_db
            result = {}
            if(self.calledFromApp):
                if isinstance(self.standaloneFeatures,dict): 
                    for key,featClass in self.standaloneFeatures.items():
                        if arcpy.Exists(dataset=featClass):
                            
                            fcName = os.path.basename(featClass)
                            if '.' in fcName:
                                fcSplit = fcName.split('.')
                                fcName = fcSplit[len(fcSplit) - 1]
                            
                            #fcDes = arcpy.Describe(featClass)
                            #workspace =featClass.replace(featClassBase,"")
                            #fullName = arcpy.ParseTableName(name=featClassBase,workspace=fcDes.workspace)
                            #nameList = fullName.split(",")
                            #databaseName = str(nameList[0].encode('utf-8')).strip()
                            #ownerName = str(nameList[1].encode('utf-8')).strip()
                            #fcName = str(nameList[2].encode('utf-8')).strip()
                           
                            
                            fcRes = arcpy.FeatureClassToFeatureClass_conversion(featClass,wk2,fcName)
                            result[key] = str(fcRes)
                                
                            print "Completed copy on {0}".format(fcName)                             
                        else:
                            result[key] = featClass
                        
                else:
                    for featClass in self.standaloneFeatures:
                        if featClass.upper().find(".SDE") != -1:
                            featName = featClass.split('.')[-1]
                        else:
                            featName = featClass.split('/')[-1]
                        if arcpy.Exists(dataset=featClass):
                            arcpy.FeatureClassToFeatureClass_conversion(featClass,wk2,featName)
                        print "Completed copy on {0}".format(featName)                     
            else:
            
                # if ds passed value exist then this call came from a copy dataset child object request.
                if ds != "":
                    if arcpy.Exists(wk2 + os.sep + ds.split('.')[-1] + os.sep + fc.split('.')[-1])==False:
                        if type == "FeatureClasses":
                            arcpy.FeatureClassToFeatureClass_conversion(self.start_db + os.sep + ds + os.sep + fc,wk2 + os.sep + ds.split('.')[-1],fc.split('.')[-1])
                            #arcpy.Copy_management(self.start_db + os.sep + ds + os.sep + fc, wk2 + os.sep + ds.split('.')[-1] + os.sep + fc.split('.')[-1])
                            print "Completed copy on {0}".format(fc)
                else:
                # This function was called independently        
                    #Check GDB if not created already, create it now
                    if self._CheckCreateGDBProcess():
                    #Determine the object type and List out
                        if type == "Tables":
                            dataTypeList = arcpy.ListTables()
                        else:
                            dataTypeList = arcpy.ListFeatureClasses()
                            
                        for dtl in dataTypeList:
                            name = arcpy.Describe(dtl)
                            new_data=name.name.split('.')[-1]
                            
                            # Checks to see if user wants to copy all features or just the ones that match the supplied list.
                            if "*" in self.standaloneFeatures and len(self.standaloneFeatures) == 1:
                                #print "Reading: {0}".format(dtl)
                                if arcpy.Exists(wk2 + os.sep + new_data)==False:
                                    if type == "Tables":
                                        arcpy.TableToTable_conversion(dtl,wk2,new_data)
                                    else:
                                        arcpy.FeatureClassToFeatureClass_conversion(dtl,wk2,new_data)                           
                                    print "Completed copy on {0}".format(new_data)
                            else:
                                if new_data in self.standaloneFeatures:
                                    print "Reading here: {0}".format(dtl)
                                    if arcpy.Exists(wk2 + os.sep + new_data)==False:                     
                                        if type == "Tables":
                                            arcpy.TableToTable_conversion(dtl,wk2,new_data)
                                        else:
                                            arcpy.FeatureClassToFeatureClass_conversion(dtl,wk2,new_data)
                                        print "Completed copy on {0}".format(new_data)                   
                                    else:
                                        print "Feature class {0} already exists in the end_db so skipping".format(new_data)
                    #Clear memory
                    del dtl
            return True
        except arcpy.ExecuteError:
            line, filename, synerror = trace()
            raise DataPrepError({
                "function": "CopyData",
                "line": line,
                "filename":  filename,
                "synerror": synerror,
                "arcpyError": arcpy.GetMessages(2),
            })
        except:
            line, filename, synerror = trace()
            raise DataPrepError({
                "function": "CopyData",
                "line": line,
                "filename":  filename,
                "synerror": synerror,
        })        
 
    def _CheckChildFeatures(self,ds="",childList="",checkList=""):
         #Handles child features of the datset.  Can either copy all or user defined features
        try:         
            if checkList:
                children = childList
                for child in children:
                    #Determines if all features will be copied.  Sending to copy feature class function
                    #to keep the handling of different data types separate.
                    if "*" in checkList and len(checkList) == 1:
                        self._CopyDataTypeProcess(ds=ds,fc=child.name)
                    else:
                        if child.name.split('.')[-1] in checkList:
                            self._CopyDataTypeProcess(ds=ds,fc=child.name)
        except:
            print "Unexpected error checking for child features:", sys.exc_info()[0]
            return False                            
    
    def _CheckCreateGDBProcess(self):
        try:
            # If user param is to overwrite GDB, then delete it first
            if self.overWrite.upper() == "YES":
                if arcpy.Exists(self.end_db)==True:
                    arcpy.Delete_management(self.end_db)
                    self.overWrite = None
                    print "Deleted previous GDB {0}".format(self.end_db)
             
            # if the local gdb doesn't exist, then create it using the path and name given in the end_db string
            if arcpy.Exists(self.end_db)==False:
                if self.end_db.rfind("\\") != -1:    
                    lastSlash = self.end_db.rfind("\\")
                else:
                    lastSlash = self.end_db.rfind("/")           
                arcpy.CreateFileGDB_management(self.end_db[:lastSlash], self.end_db[lastSlash+1:])
                self.overWrite = None
                print "Created geodatabase {0}".format(self.end_db[lastSlash+1:])
            else:
                self.overWrite = None
                #print "Geodatabase already exists" 
            return True 
        except:
            print "Unexpected error create geodatabase:", sys.exc_info()[0]
            return False
        
   
    def _executePostProcess(self):
        try:
            print "Running post process GP"
            for process in self.postExtractGP:
                if process["ToolType"].upper() == "MODEL":
                    arcpy.ImportToolbox(process["ToolPath"])
                    arcpy.gp.toolbox = process["ToolPath"]
                    tools = arcpy.ListTools()
                    for tool in process["Tools"]:
                        if tool in tools:
                            customCode = "arcpy." + tool + "()"
                            print eval(customCode)
                            print "Finished executing model {0}".format(tool)
                elif process["ToolType"].upper() == "SCRIPT":
                    for tool in process["Tools"]:
                        scriptPath = process["ToolPath"] + "/" + tool
                        subprocess.call([sys.executable, os.path.join(scriptPath)])
                        print "Finished executing script {0}".format(tool)
                else:
                    print "Sorry, not a valid tool"
            return True
        except arcpy.ExecuteError:
            line, filename, synerror = trace()
            raise DataPrepError({
                "function": "CopyData",
                "line": line,
                "filename":  filename,
                "synerror": synerror,
                "arcpyError": arcpy.GetMessages(2),
            })
        except:
            line, filename, synerror = trace()
            raise DataPrepError({
                "function": "CopyData",
                "line": line,
                "filename":  filename,
                "synerror": synerror,
            })