# Version 4 of this script introduces argParse so that certain options can be fed in from command line

# ---------------------------------------------------------------------------------------------------------
# IMPORT LIBRARIES

# Import standard libraries
import os # Used to access files and directory info
import glob # Used to discover xml files with standard bash glob expressions
import time # Used for time functions like sleep (to slow down feedback)
from lxml import etree as ET # Used for xml object parsing
#from xml.etree import ElementTree as ET # TRYING TO NOT USE plain xml since it's slightly inferior
import argparse # used to define command-line arguments for script


# Import my supporting python scripts
import loadLocalizationInfo as locData
import bundleModify as bMod
import displayLayout as dispPrint



#--------------------------------------------------------------------------------------------------------------

# INITIAL DECLARATIONS

# A sessionData dictionary will be useful for tracking
sessionData={}
sessionData['runConfig']={}
sessionData['runConfig']['verbose']= True
# Call on loadLocalizationInfo to determine if available
sessionData['runConfig']['LFIavailable']=locData.module_exists('LocalFileInstaller')

testLib = __import__('xml')


sessionData['bundleChanges']={}
sessionData['substitutions']={}

displayAsTable=True # This flag will help determine whether to display bundles as table or simple text

# ---------------------------------------------------------------------------------------------------------
# UTILITY FUNCTION DEFINITIONS
verbose = False # global variable will trigger more detailed print statements in functions for 'debugging'

# CLEVER FUNCTION FOR PRINTING FEEDBACK only if set a "verbose" flag. SOURCE: stackoverflow.com/questions/5980042
if verbose:
    def verboseprint(*args):
        # Print each argument seperately so caller doesn't need to stuff everything to be printed into a single string
        for arg in args:
            print(arg, end=' ')
        print()
else:
    verboseprint = lambda *a: None # do-nothing function


# Set function for displaying line across terminal width
def printDivider(symbol='=',title=None): # Optional args include symbol and title
    # Leverage function in DisplayLayout for printing things which are the width of the terminal
    termWidth=dispPrint.getTerminalSize()
    # Construct primary divider
    divideLine = '{:{fillChar}{align}{width}}'.format('',fillChar=symbol,align='^',width=termWidth)
    print('') # EXTRA blank line before start
    print(divideLine)
    # If we have a title, then format it in the divier as well
    if title:
        titleLine='{:{fillChar}{align}{width}}'.format(title,fillChar='',align='^',width=termWidth)
        print(titleLine)
        print(divideLine)

# ---------------------------------------------------------------------------------------------------------


# WELCOME STATEMENTS HERE
print('>>> WELCOME to the python bundle editing script! <<<\n')

# ================================================================================================
# Set up argument parse
parser = argparse.ArgumentParser(description='Parse arguments')
parser.add_argument('--wfo', default=None, help='The three-letter identifier for your office (e.g. OAX)')
parser.add_argument('--bundle', default=None, help='Manually input the bundle location if not in current directory')
parser.add_argument('--autoGuess', default=False, action='store_true', help='If used, skips prompt for any ' \
    'memorized substitutions (very quick execution)')
parser.add_argument('--locFiles', default=None, help='If specified, uses provided path on local machine '\
    '(useful for W2B or testing). Otherwise assumes field cluster default')
parser.add_argument('--radars',default=None, nargs='+', help='[ADVANCED OPTION] Clockwise list of 4 radars, '\
    'separated by spaces, to use in 4-panel displays')

args=parser.parse_args()

printDivider(title='ARGUMENTS')




# Check if bundle arg provided and is valid
bFile = None
if args.bundle:
    if os.path.isfile(args.bundle):
        bFile=args.bundle
        print('SELECTED PROCEDURE FILE TO EDIT: {}\n'.format(bFile))
    else:
        print('File Error: Provided bundle ({}) is not valid file'.format(args.bundle))
# Try to find file in local directory if wasn't provided
if not bFile:
    print('Searching current directory for procedure files...')
    bCandidates = []
    xmlInDir = [f for f in glob.glob('*.xml'.format(os.getcwd()))]
    if len(xmlInDir)>0:
        for x in xmlInDir:
            with open(x) as myfile:
                if '<procedure>' in myfile.read():
                    bCandidates.append(x)
    if len(bCandidates)>0:
        print('The following procedure files were found in the current directory.')
        print(('\n'.join('{:2d}: {}'.format(i+1, s) for i, s in enumerate(bCandidates))))
        print('Enter a number to pick a procedure for editing...')
        choice = input('ENTER NUMBER:\n')
        # HANDLE INPUT (NEEDS WORK). Currently, depends on it being a number. But let's also allow it to be *. Also need bad entry handling
        if str(choice).isdigit() and int(choice) > 0 and int(choice) <= len(bCandidates):
            bFile = bCandidates[int(choice)-1]
            print(('You entered: {} ({})'.format(choice, bFile)))
        else:
            print('INVALID CHOICE. Exiting.')
            exit()
    else:
        print('No procedures found for editing! Provide path with --bundle argument, or change to directory containing bundles before executing.')
        print('Exiting! Goodbye')
        exit()
# [NEW MAY 2020] Add selected bundle info (e.g. path) to sessionData... this facilitates access to bundle file outside of main xml read process below
# --> Putting in 'runConfig' subsection of sessionData, since seems relevant to run arguments
sessionData['runConfig']['bundlePath']=bFile


# Prompt for localization if unknown
if args.wfo:
    sessionData['loc'] = args.wfo
else:
    sessionData['loc'] = input('Please type your three-letter localization bellow:\n')
print('YOU CHOSE WFO: {}\n'.format(sessionData['loc']))

# In one line, set autoGuess behavior based on args. True skips any prompts when guess is available, False keeps prompts
sessionData['runConfig']['autoAcceptGuesses'] = True if args.autoGuess else False

# In one line, set localization path if provided
if args.locFiles:
    print('LOCALIZATION: The LOCAL utility tree (at path below) will be used for finding localization settings.')
    print('\t{}\n'.format(os.path.abspath((args.locFiles))))
    sessionData['runConfig']['locFiles'] = args.locFiles

if args.radars:
    radarList=args.radars
    fixed_radarList=[] # new list to store any formatting fixes
    # CHECK if the user accidentally made a comma separated list
    for r in radarList:
        if ',' in r:
            splitValues=r.strip().split(',')
            for s in splitValues:
                if len(s)>0:
                    fixed_radarList.append(s.strip())
        else:
            fixed_radarList.append(r)
    radarList=fixed_radarList
    # VALIDATE that radars are 4-characters
    for r in radarList:
        if not len(r)==4:
            print('RADAR ARG ERROR: "{}" fails test that radar names MUST be 4 characters (e.g. ktlx).'.format(r))
            # Exit the script completely. Lets user know arguments must be fixed, even if prompts could have handled later
            print('EXITING SCRIPT!')
            exit()
    # Validate whether user entered 4 radars, as necessary for this to work
    if not isinstance(radarList,list) or not len(radarList)==4:
        print('RADAR ARG ERROR: radars argument MUST contain 4 radars.')
        # Exit the script completely. Lets user know arguments must be fixed, even if prompts could have handled later
        print('EXITING SCRIPT!')
        exit()
    print('YOU ENTERED {} RADARS: {}\n'.format(len(radarList),', '.join(radarList)))
    # Call getRadarSubs function to format radar arguments for substitution dictionary
    radarSubDict = locData.getRadarSubs(sessionData, radarList)
    # Put the radar substitution dictionary we got back into the appropriate sessionData substitution
    sessionData['substitutions']['radarResourceData'] = radarSubDict

# -------------------------------------------------------------------------------------

printDivider(title='IMPORTING LOCALIZATION INFO')

# Call the makeScaleDict, assign to sessionData
sessionData['scaleDict']=locData.buildScaleDict(sessionData)

# Try to get warning sites from localization, assign to sessionData
warningSites=locData.getWarningSites(sessionData)
# If succesful, assign AS SUBSTITUTION OPTION for 'wwaResourceData' resourceTypes, with special 'INITIAL' key
sessionData['substitutions']['wwaResourceData']={'INITIAL': warningSites}


# -------------------------------------------------------------------------------------
# Now present bundle choice list
printDivider(title='SELECT BUNDLES TO MODIFY')

procFile=bMod.xmlObjFromFile(bFile)
bundles=bMod.makeBundleList(procFile)

#~~~~~~~
# Retrive bundle to modify from list prompt
bundleToMod=bMod.chooseBundle(bundles)


bundle_i=0
for bundle in bundleToMod:
    bundle_i+=1
    bundleName=bundle.get('name')
    dividerTitle='EDIT BUNDLE ({} of {}): {}'.format(bundle_i,len(bundleToMod),bundleName)
    printDivider(title=dividerTitle)
    #print('BUNDLE: '+bundle.get('name'))

    displays=bMod.getBundleDisplays(bundle)
    verboseprint('DISPLAY COUNT: '+str(len(displays)))

    displaySet,dispScale=bMod.getAllDisplaysInventory(displays)
    verboseprint(displaySet)


    # SHOW the current display. Check if user prefers simple or tabular format
    if displayAsTable:
        print(('BUNDLE: {} (SCALE: {})'.format(bundle.get('name'),dispScale)))
        dispPrint.printDisplayTabular(displaySet)
    else:
        bMod.printDisplaySimple(displays)

    # Check bundle for needed changes
    bMod.detectNeededChanges(bundle,sessionData)

    # CALL FUNCTINON to rename bundle
    print('FINISHED UPDATING BUNDLE!\nMOVING TO RENAMING...')
    bMod.renameBundle(bundle,sessionData)

    # ARBITRARY PAUSE
    time.sleep(0.1)




# Steps for saving file
time.sleep(1)
printDivider(title='FINISHED EDITING BUNDLES... PREPARE TO WRITE FILE')
outputDir=os.path.dirname(os.path.abspath(bFile)) # Use original path for now, but can modify
outputPrefix='MODIFIED_'
outputFName='{}{}'.format(outputPrefix,os.path.basename(bFile))

# Check with save name for bundle. Try using the function fro bundleModify.py which fills in a default answer:
try:
    savePrompt='>>\tCONFIRM SAVE NAME for procedure file\n\t[ENTER to accept]: '
    outputFName=bMod.raw_input_default(savePrompt,default=outputFName) or outputFName
except:
    outputFName=input(savePrompt) or outputFName

outputFPath='{}/{}'.format(outputDir,outputFName)

print('--> File will be saved to:\n\t{}'.format(os.path.abspath(outputFPath)))


# TRY WRITING OUT THE BUNDLE TO A NEW FILE
# In anticipation of this, we'll need to get the encoding info that was at the beginning of our file
# BASED ON https://stackoverflow.com/questions/12966488/preserving-original-doctype-and-declaration-of-an-lxml-etree-parsed-xml ... we can do this:
docinfo = procFile.docinfo # this gets docinfo from the xml, can use it later

# Based on https://stackoverflow.com/questions/2833185/write-xml-file-using-lxml-library-in-pyton, it looks like we can do somethign like this:

print('WRITING TO FILE ......')
#tree.write(fDir+output_fName, pretty_print=True, xml_declaration=True, encoding='utf-8')
procFile.write(outputFPath, pretty_print=True, xml_declaration=True, encoding=docinfo.encoding, standalone=docinfo.standalone)

# THIS WORKS for my file but may need a better way to grab EXACTLY the headers of the original file? since doing it this way with docinfo requires me to manually grab the elements of the header that I want when writing...

print('... done writing to file!')

# ------------------------------------------------
time.sleep(0.5)
printDivider(title='WRAP-UP')

# Provide final helpful information to
print('REMEMBER: Copy your new bundle to localization!')

print("\t* Follow instructions in ReadMe or VLab to copy the procedure file you wrote (path below) into CAVE")
print("\t--> PATH: {}\n".format(os.path.abspath(outputFPath)))

# print 'REMEMBER: Copy any needed colormaps to localization!'
#
# # Search for any cmap files in bundle directory
# bPath=os.path.dirname(bFile)
# availCmapFiles=[f for f in glob.glob('/'.join([bPath,'*.cmap']))]
# availCMapFiles_noExt = [os.path.basename(f).strip('.cmap') for f in availCmapFiles]
# # Do some analysis of the colormaps that will be needed
# procCmaps = bMod.getUniqueColormaps(procFile)
#
# # For each colormap used in procedure, try calling localization search to see if result can be found
# missingCmaps=[]
# for c in procCmaps:
#     if not locData.searchColorMaps(sessionData,c):
#         missingCmaps.append(c)
# # Print out list of missing colormaps
# if len(missingCmaps)>0:
#     print '\tNOTICE: {} (of {}) colormaps used by "{}" were not found in your localization:'\
#         .format(len(missingCmaps),len(procCmaps),os.path.basename(bFile))
#     for i,m in enumerate(sorted(missingCmaps)):
#         # TEST first to see if provided with bundle
#         extraText=''
#         for cFile in availCMapFiles_noExt:
#             if cFile in m:
#                 extraText='<--- PROVIDED in bundle directory! COPY to localization'
#                 break
#         print '\t\t{:2d}) {:50}{}'.format(i+1,m,extraText)
#     print "\t* Follow instructions in ReadMe or VLab to copy provided colormaps into CAVE"
# else:
#     '\t* All needed colormaps were found in your localization '
    # BUT MAYBE DO md5sum to compare?

time.sleep(0.5)
printDivider(title='EXITING... Goodbye!')
