#!/usr/bin/env python
import os
import sys
import argparse
import subprocess
import fnmatch
import json
import datetime
import string
import types
import smtplib
from email.mime.text import MIMEText
import pwd
from wizardPy import Wizard, TemplateWithValuesWizardStep, WizardStep, WizardContext, CallableWizardStep, TemplateWithValues

import globals
import toolconfig
import pushRelease
import upgradeRelease
import multi_facility_process_helper_client

def loadIOCNames(folder):
    '''Load ioc names from specified folder using *ioc* syntax'''
    iocs = []
    if not os.path.exists(folder):
        return []
    for file in os.listdir(folder):
        if fnmatch.fnmatch(file, '*ioc*') and os.path.isdir(os.path.join(folder, file)):
            iocs.append(file)
    return sorted(iocs)

def createFolderIfItDoesNotExist(facility, folder, mode):
    '''Create the named folder on the other side with mode and optionally make if owned by IOC owner
    Return True if the folder was created.
    '''
    commandsAndArgs = []
    commandsAndArgs.append(('fileinfo', [folder]))
    results = multi_facility_process_helper_client.executeCommandsOnFacility(facility, commandsAndArgs)
    folderInfo = results[0]
    if globals.verbose:
        print folderInfo
    if not folder in folderInfo:
        if globals.verbose:
            print "Creating the folder", folder
        commandsAndArgs = []
        options = {'name': folder, 'mode': mode}
        commandsAndArgs.append(('mkdirs', options))
        results = multi_facility_process_helper_client.executeCommandsOnFacility(facility, commandsAndArgs)
        if globals.verbose:
            print results
        return results[0]['justcreated']
    else:
        if globals.verbose:
            print "Folder {0} already exists.".format(folder)
        return False

def linkToFile(facility, link, target):
    '''
    Create a link to the target on facility.
    '''
    commandsAndArgs = []
    commandsAndArgs.append(('link', {
            'link' : link, 
            'target' : target
            }))
    results = multi_facility_process_helper_client.executeCommandsOnFacility(facility, commandsAndArgs)        
    if globals.verbose:
        print "Done creating link {} to target{} with results".format(link, target), results

    
def templateReplaceFile(facility, src, dest, templateVars):
    '''Template replace file on the other side'''
    commandsAndArgs = []
    commandsAndArgs.append(('templateReplace', {
        'source': src, 
        'dest': dest,
        'templateVars' :  templateVars
        }))
    results = multi_facility_process_helper_client.executeCommandsOnFacility(facility, commandsAndArgs)
    if globals.verbose:
        print results
    return results[0]['status']
    

class CheckReleaseFolderExists(WizardStep):
    def enter(self, wizardContext):
        facility = wizardContext.data['facility']
        facilityConfig = toolconfig.getConfigForFacility(facility)
        packageReleaseFolder = os.path.join(facilityConfig[wizardContext.appType]['releaseFolder'], wizardContext.appName)
        createFolderIfItDoesNotExist(facility, packageReleaseFolder, 0775)
        return True

class LinkToExistingRelease(WizardStep):
    def enter(self, wizardContext):
        if 'currentLinkExists' in wizardContext.data and wizardContext.data['currentLinkExists']:
            return True
        facility = wizardContext.data['facility']
        facilityConfig = toolconfig.getConfigForFacility(facility)
        upgradeToVersion = wizardContext.data['linkExistingRelease']
        if globals.verbose:
            print "Upgrading the MASTER release to {} in facility {}".format(upgradeToVersion, facility)
        upgradeRelease.upgradeReleaseOnFacility(wizardContext.appName, wizardContext.appType, facility, "MASTER", upgradeToVersion)
        return True

class NextIOCToInitialize(WizardStep):
    def enter(self, wizardContext):
        facility = wizardContext.data['facility']
        facilityConfig = toolconfig.getConfigForFacility(facility)
        if globals.verbose:
            print "Selecting next IOC to initialize"
        wizardContext.data['currIOCBeingInitialized'] = wizardContext.data['IOCsToInitialize'].pop(0)
        if globals.verbose:
            print "Initializing IOC", wizardContext.data['currIOCBeingInitialized']
            print "Remaining IOCS", wizardContext.data['IOCsToInitialize']
        return True

    
class IOCCommonInitialization(WizardStep):
    def enter(self, wizardContext):
        facility = wizardContext.data['facility']
        facilityConfig = toolconfig.getConfigForFacility(facility)
        if 'currIOCBeingInitialized' not in wizardContext.data:
            return True
        iocName = wizardContext.data['currIOCBeingInitialized']
        iocCommonFolder = os.path.join(facilityConfig[wizardContext.appType]['linkFolder'], iocName)
        createFolderIfItDoesNotExist(facility, iocCommonFolder, 0777)

        iocSpecificPath = os.path.join(iocCommonFolder, 'iocSpecificRelease')
        iocTopCurrentPath = os.path.join(facilityConfig[wizardContext.appType]['releaseFolder'], wizardContext.appName, 'current')

        commandsAndArgs = []
        commandsAndArgs.append(('fileinfo', [iocSpecificPath]))
        results = multi_facility_process_helper_client.executeCommandsOnFacility(facility, commandsAndArgs)        
        if globals.verbose:
            print results
        linkInfo = results[0]
        if iocSpecificPath in linkInfo:
            print "IOC {} seems to have a iocSpecificRelease".format(iocName)
        else:
            commandsAndArgs = []
            commandsAndArgs.append(('link', {'link' : iocSpecificPath, 'target' : iocTopCurrentPath}))
            results = multi_facility_process_helper_client.executeCommandsOnFacility(facility, commandsAndArgs)        
            if globals.verbose:
                print "Done creating iocSpecificRelease link", results

        templateReplaceFile(facility,
            "${EPICS_IOCS}/facility/screenrc",
            os.path.join(facilityConfig[wizardContext.appType]['linkFolder'], iocName, 'screenrc'),
            {}
            )
        return True
    
class IOCDataInitialization(WizardStep):
    def enter(self, wizardContext):
        facility = wizardContext.data['facility']
        facilityConfig = toolconfig.getConfigForFacility(facility)
        if 'currIOCBeingInitialized' not in wizardContext.data:
            return True
        iocName = wizardContext.data['currIOCBeingInitialized']
        iocDataFolder = os.path.join("${IOC_DATA}", iocName)
        createFolderIfItDoesNotExist(facility, iocDataFolder, 0776)
        createFolderIfItDoesNotExist(facility, os.path.join(iocDataFolder, 'autosave'), 0776)
        createFolderIfItDoesNotExist(facility, os.path.join(iocDataFolder, 'autosave-req'), 0776)
        createFolderIfItDoesNotExist(facility, os.path.join(iocDataFolder, 'iocInfo'), 0776)


        return True

class ScreenIOCsParameters(WizardStep):
    def enter(self, wizardContext):
        facility = wizardContext.data['facility']
        facilityConfig = toolconfig.getConfigForFacility(facility)
        if 'currIOCBeingInitialized' not in wizardContext.data:
            return True
        executableFiles = []
        for root, dirs, files in os.walk("bin"):
            for file in files:
                if os.access(os.path.join(root, file), os.X_OK):
                    executableFiles.append(os.path.relpath(os.path.join(root, file), "."))
        return TemplateWithValues("screeniocs.html", 
                {"userText" : "Please choose the <code>screeniocs</code> parameters for <b>{}</b>.".format(wizardContext.data['currIOCBeingInitialized']),
                 "executablePaths" : executableFiles
                 }, 
                wizardContext)

class ScreenIOCsInitialization(WizardStep):
    def enter(self, wizardContext):
        facility = wizardContext.data['facility']
        facilityConfig = toolconfig.getConfigForFacility(facility)
        if 'currIOCBeingInitialized' not in wizardContext.data:
            return True

        iocName = wizardContext.data['currIOCBeingInitialized']
        iocType = wizardContext.data['iocType']
        iocCommonFolder = os.path.join(facilityConfig[wizardContext.appType]['linkFolder'], iocName)

        if iocType == 'SIOC': 
            startupCmdTemplate = os.path.join(os.path.dirname(__file__), 'templates', 'startup.cmd.soft')
        elif iocType == 'VIOC':
            pass
        elif iocType == 'HIOC':
            startupCmdTemplate = os.path.join(os.path.dirname(__file__), 'templates', 'startup.cmd.rtems')
        else:
            raise Exception("Unknown IOC type", iocType)

        if iocType in ['SIOC', 'HIOC']:
            startupCmdDict = self.generateStartupCmdDict(wizardContext)
            with open(startupCmdTemplate, 'r') as f:
                content = f.read()
            lines = string.Template(content).substitute(startupCmdDict)
            if globals.verbose:
                print "Template being sent over is"
                print lines
            commandsAndArgs = []
            commandsAndArgs.append(('templateReplace', {
                'content': lines, 
                'dest': os.path.join(iocCommonFolder, 'startup.cmd'),
                'templateVars' :  startupCmdDict
                }))
            results = multi_facility_process_helper_client.executeCommandsOnFacility(facility, commandsAndArgs)
            if globals.verbose:
                print results
            # For now create a softlink called st.cmd to the startup.cmd (until iocConsole is fixed)
            linkToFile(facility, os.path.join(iocCommonFolder, 'st.cmd'), os.path.join(iocCommonFolder, 'startup.cmd'))
        elif iocType == 'VIOC':
            commandsAndArgs = []
            commandsAndArgs.append(('link', {
                    'link' : os.path.join(iocCommonFolder, 'startup.cmd'), 
                    'target' : os.path.join(facilityConfig[wizardContext.appType]['linkFolder'], 'skel', 'startup_ioc.cmd')
                    }))
            results = multi_facility_process_helper_client.executeCommandsOnFacility(facility, commandsAndArgs)        
            if globals.verbose:
                print "Done creating startup.cmd link", results
        
        commandsAndArgs = []
        commandsAndArgs.append(('updateScreenIOCEntry', {
                'iocName' : iocName, 
                'screeniocsentry' : self.generateScreensIOCSLine(wizardContext, iocCommonFolder)
                }))
        results = multi_facility_process_helper_client.executeCommandsOnFacility(facility, commandsAndArgs)        
        if globals.verbose:
            print "Done creating startup.cmd link", results
        
        return True
    def generateStartupCmdDict(self, wizardContext):
        ''' Variables we have 
        ${IOC_NAME}
        ${IOC_EXECUTABLE}
        ${IOC_DESCRIPTION}
        ${DATE}
        ${USER}
        '''
        stCmdDict = {}
        stCmdDict['IOC_NAME'] = wizardContext.data['currIOCBeingInitialized']
        if wizardContext.data['iocType'] in ['SIOC', 'VIOC']:
            stCmdDict['IOC_EXECUTABLE'] = wizardContext.data['executable']
        stCmdDict['IOC_DESCRIPTION'] = wizardContext.data['description']
        stCmdDict['DATE'] = datetime.datetime.now().strftime("%Y/%b/%d %H:%M")
        userDetails = pwd.getpwuid(os.getuid())
        stCmdDict['USER'] = "{} ({})".format(userDetails.pw_name, userDetails.pw_gecos)
        return stCmdDict    
    def generateScreensIOCSLine(self, wizardContext, iocCommonFolder):
        iocName = wizardContext.data['currIOCBeingInitialized']
        iocType = wizardContext.data['iocType']
        description = wizardContext.data['description']
        if iocType == 'HIOC': 
            terminalServer = wizardContext.data['terminalServer']
            terminalPort = wizardContext.data['terminalPort']
            screenHost = wizardContext.data['screenHost']
            return "{:<25} {:<20} {:<8} {:<20}      #{}\n".format(iocName, terminalServer, terminalPort, screenHost, description)
        elif iocType == 'VIOC':
            CPU = wizardContext.data['CPU']
            user = wizardContext.data['user']
            executable = os.path.join(iocCommonFolder, 'iocSpecificRelease', wizardContext.data['executable'])
            return "{:<25} {:<20} {:<8} {}      #{}\n".format(iocName, CPU, user, executable, description)
        elif iocType == 'SIOC':
            executable = os.path.join(iocCommonFolder, 'iocSpecificRelease', wizardContext.data['executable'])
            screenHost = wizardContext.data['screenHost']
            return "{:<25} {}   sioc   {:<20}      #{}\n".format(iocName, executable, screenHost, description)


class ScreenCPUsParameters(WizardStep):
    def enter(self, wizardContext):
        facility = wizardContext.data['facility']
        facilityConfig = toolconfig.getConfigForFacility(facility)
        if 'currIOCBeingInitialized' not in wizardContext.data:
            return True
        iocName = wizardContext.data['currIOCBeingInitialized']
        iocType = wizardContext.data['iocType']
        if iocType != 'VIOC':
            return True

        description = wizardContext.data['description']
        if iocType == 'VIOC':
            CPU = wizardContext.data['CPU']
            # See if the CPU has a screeniocs's entry
            commandsAndArgs = []
            commandsAndArgs.append(('getScreenIOCEntry', CPU))
            commandsAndArgs.append(('env', 'EPICS_CPUS'))
            results = multi_facility_process_helper_client.executeCommandsOnFacility(facility, commandsAndArgs)        
            if globals.verbose:
                print "Checking if CPU has screenIOC entry", results
            screeniocsentry = results[0]
            epicsCpusEnvVarExists = results[1]
            if epicsCpusEnvVarExists:
                if verbose:
                    print "Using EPICS_CPUS for the CPUS folder"
                wizardContext.data['useEPICSCPUs'] = True
            else:
                wizardContext.data['useEPICSCPUs'] = False
            if not screeniocsentry:
                wizardContext.data['updateScreenIOCsForCPU'] = True
                return TemplateWithValues("screeniocs.html", 
                    {"userText" : "Please choose the <code>screeniocs</code> parameters for CPU <b>{}</b>.".format(CPU),
                     "entriesForCPU" : True
                     }, 
                    wizardContext)
            else:
                wizardContext.data['updateScreenIOCsForCPU'] = False
                
        return True

class ScreenCPUsInitialization(WizardStep):
    def enter(self, wizardContext):
        facility = wizardContext.data['facility']
        facilityConfig = toolconfig.getConfigForFacility(facility)
        if 'currIOCBeingInitialized' not in wizardContext.data:
            return True
        iocType = wizardContext.data['iocType']
        if 'CPU' not in wizardContext.data:
            if globals.verbose:
                print "No CPU defined in context data; probably not a VIOC"
            return True
        cpuName = wizardContext.data['CPU']
        cpuRootFolder = "${EPICS_IOCS}"
        cpuCommonFolder = os.path.join("${EPICS_IOCS}", cpuName)
        if wizardContext.data['useEPICSCPUs']:
            cpuRootFolder = "${EPICS_CPUS}"
            cpuCommonFolder = os.path.join("${EPICS_CPUS}", cpuName)
        
        cpuCommonFolderCreated = createFolderIfItDoesNotExist(facility, cpuCommonFolder, 0774)
        createFolderIfItDoesNotExist(facility, os.path.join("${IOC_DATA}", cpuName), 0777)
        createFolderIfItDoesNotExist(facility, os.path.join("${IOC_DATA}", cpuName, "scanner"), 0777)
        
        if cpuCommonFolderCreated:
            linkToFile(facility, os.path.join(cpuCommonFolder, 'startup.cmd'), os.path.join(cpuRootFolder, 'skel', 'startup_cpu.cmd'))
            linkToFile(facility, os.path.join(cpuCommonFolder, 'screenrc'), os.path.join(cpuRootFolder, 'skel', 'screenrc'))
        
            kernelModulesCmdTemplate = os.path.join(os.path.dirname(__file__), 'templates', 'kernel-modules.cmd')
            with open(kernelModulesCmdTemplate, 'r') as f:
                content = f.read()

            commandsAndArgs = []
            commandsAndArgs.append(('templateReplace', {
                'content': content, 
                'dest': os.path.join(cpuCommonFolder, 'kernel-modules.cmd'),
                'templateVars' :  {}
                }))
            results = multi_facility_process_helper_client.executeCommandsOnFacility(facility, commandsAndArgs)
            if globals.verbose:
                print results

        if wizardContext.data['updateScreenIOCsForCPU']:
            commandsAndArgs = []
            commandsAndArgs.append(('updateScreenIOCEntry', {
                'iocName' : cpuName, 
                'screeniocsentry' : self.generateScreensIOCSLine(wizardContext, cpuName)
                }))
            results = multi_facility_process_helper_client.executeCommandsOnFacility(facility, commandsAndArgs)        
            if globals.verbose:
                print "Done updating screen iocs for CPU", results
        return True
    def generateScreensIOCSLine(self, wizardContext, cpuName):
        description = wizardContext.data['description']
        if 'cpuDescription' in wizardContext.data:
            # For silent installs, use the cpuDescription.
            description = wizardContext.data['cpuDescription']
        terminalServer = wizardContext.data['terminalServer']
        terminalPort = wizardContext.data['terminalPort']
        screenHost = wizardContext.data['screenHost']
        return "{:<25} {:<20} {:<8} {:<20}      #{}\n".format(cpuName, terminalServer, terminalPort, screenHost, description)


class Ask_For_DHCP_Etc_Init_d(WizardStep):
    def enter(self, wizardContext):
        facility = wizardContext.data['facility']
        facilityConfig = toolconfig.getConfigForFacility(facility)
        if 'currIOCBeingInitialized' not in wizardContext.data:
            return True
        iocName = wizardContext.data['currIOCBeingInitialized']
        return TemplateWithValues("selectOne.html", {
                      "selections" : [{ 'label': "No, don't send an email just yet.", 'value': "No"}, {'label' : "Yes, please send an email now.", 'value': "Yes"}],
                      "stateName" : "sendEmail",
                      "userText" : "Do you want to send an email to the systems team to add <code>DHCP</code> or <code>/etc/init.d</code> entries for automatically starting <b>{}</b> in <b>{}</b>".format(iocName, facility) + "?"},
                      wizardContext)

class Initialize_DHCP_Etc_Init_d(WizardStep):
    def enter(self, wizardContext):
        facility = wizardContext.data['facility']
        facilityConfig = toolconfig.getConfigForFacility(facility)
        if 'currIOCBeingInitialized' not in wizardContext.data:
            return True

        iocName = wizardContext.data['currIOCBeingInitialized']
        iocType = wizardContext.data['iocType']
        msg = self.generateIOCMessage(iocName, iocType, facility, wizardContext)
        if wizardContext.data['sendEmail'] == 'Yes' and msg:
            self.sendEmail("Startup for IOC {}".format(iocName), msg)
        return True
    def sendEmail(self, subject, message):
        # Read config email.cfg
        emailConfig = []
        with open(os.path.join(os.path.dirname(__file__), 'email.cfg'), 'r') as f:
            emailConfig = json.load(f)
        userDetails = pwd.getpwuid(os.getuid())
        # , userDetails.pw_gecos)
        msg = MIMEText(message, 'html')
        msg['Subject'] = '' + subject
        fromEmailAddr = userDetails.pw_name + "@slac.stanford.edu" 
        msg['From'] = fromEmailAddr
        # For now send the email to the person running the program
        # msg['To'] = emailConfig['to'][0]
        msg['To'] = fromEmailAddr
        msg['Cc'] = fromEmailAddr
        s = smtplib.SMTP(emailConfig['smtphost'])
        s.sendmail(fromEmailAddr, emailConfig['to'], msg.as_string())
        s.quit()
    def generateIOCMessage(self, iocName, iocType, facility, wizardContext):
        userDetails = pwd.getpwuid(os.getuid())
        if iocType == 'HIOC':
            return "<div>Hi,</div><div>Please create DHCP entries for IOC <code>{0}</code> in facility <b>{1}</b>.</div><div>Thanks</div><div>{2}</div>".format(iocName, facility, userDetails.pw_gecos)
        elif iocType == 'SIOC':
            screenHost = wizardContext.data['screenHost']
            return "<div>Hi,</div><div>Please create an /etc/init.d entry for automatically starting SIOC <code>{0}</code> in facility <b>{1}</b>; the SIOC is targeted to run on the machine <code>{2}</code></div><div>Thanks</div><div>{3}</div>".format(iocName, facility, screenHost, userDetails.pw_gecos)
        elif iocType == 'VIOC':
            if wizardContext.data['updateScreenIOCsForCPU']:
                return "<div>Hi,</div><div>Please create DHCP entries for CPU <code>{0}</code> in facility <b>{1}</b></div><div>Thanks</div><div>{2}</div>".format(iocName, facility, userDetails.pw_gecos)
        return None


class AnyMoreIOCs(WizardStep):
    def enter(self, wizardContext):
        facility = wizardContext.data['facility']
        facilityConfig = toolconfig.getConfigForFacility(facility)
        wizardContext.data['IOCsInitDone'].append(wizardContext.data['currIOCBeingInitialized'])

        if globals.verbose:        
            print "AnyMoreIOCs: IOCsToInitialize", wizardContext.data['IOCsToInitialize'], "Num steps", len(wizardContext.steps)
        
        if len(wizardContext.data['IOCsToInitialize']) > 0:
            del wizardContext.data['currIOCBeingInitialized']
            wizardContext.gotToStep(wizardContext.data['iocInitializationStepId'])
        else:
            wizardContext.steps.insert(wizardContext.currentStep + 1, TemplateWithValuesWizardStep("message.html", {
                  "userText" : "<div>The following IOC's were initialized in facility <b>{}</b>.<ul>{}</ul></div><div>Thank you for using cram init</div>".format(facility, "\n".join(["<li>" + x + "</li>" for x in wizardContext.data['IOCsInitDone']])),
                  "finalStep" : True
                  }))
        return True
        

class ProcessIOCSelections(WizardStep):
    def enter(self, wizardContext):
        facility = wizardContext.data['facility']
        facilityConfig = toolconfig.getConfigForFacility(facility)
        if 'IOCsToInitialize' not in wizardContext.data or  not wizardContext.data['IOCsToInitialize'] or len(wizardContext.data['IOCsToInitialize']) <= 0:
            wizardContext.steps.insert(wizardContext.currentStep + 1, TemplateWithValuesWizardStep("message.html", {
                  "userText" : "<div>You did not choose to initialize any IOC's this time around in facility <b>{}</b>.</div><div>Please run cram init again if/when you need to this</div>".format(facility),
                  "finalStep" : True
                  }))
            return True;
        # We now transition into IOC initialization
        # We reset the wizard previous steps as now don't want to go back.
        wizardContext.resetPreviousSteps()
        wizardContext.data['iocInitializationStepId'] = wizardContext.currentStep
        if globals.verbose:
            print "Adding IOC initialization steps at", wizardContext.currentStep
        wizardContext.steps.insert(wizardContext.currentStep + 1, NextIOCToInitialize())
        wizardContext.steps.insert(wizardContext.currentStep + 2, IOCCommonInitialization())
        wizardContext.steps.insert(wizardContext.currentStep + 3, IOCDataInitialization())
        wizardContext.steps.insert(wizardContext.currentStep + 4, ScreenIOCsParameters())
        wizardContext.steps.insert(wizardContext.currentStep + 5, ScreenIOCsInitialization())
        wizardContext.steps.insert(wizardContext.currentStep + 6, ScreenCPUsParameters())
        wizardContext.steps.insert(wizardContext.currentStep + 7, ScreenCPUsInitialization())
        wizardContext.steps.insert(wizardContext.currentStep + 8, Ask_For_DHCP_Etc_Init_d())
        wizardContext.steps.insert(wizardContext.currentStep + 9, Initialize_DHCP_Etc_Init_d())
        wizardContext.steps.insert(wizardContext.currentStep + 10, AnyMoreIOCs())        
        
        return True



class FindUninitializedIOCs(WizardStep):
    def enter(self, wizardContext):
        facility = wizardContext.data['facility']
        facilityConfig = toolconfig.getConfigForFacility(facility)
        iocsInApp = loadIOCNames("iocBoot")
        commandsAndArgs = []
        arg = {}
        arg['name'] = wizardContext.appName
        arg['releaseFolder'] = toolconfig.determineReleaseFolder(facility, wizardContext.appName, wizardContext.appType)
        arg['linkFolder'] = toolconfig.determineLinkFolder(facility, wizardContext.appName, wizardContext.appType)
        commandsAndArgs.append(('determineIOCVersions', arg))
        results = multi_facility_process_helper_client.executeCommandsOnFacility(facility, commandsAndArgs)
        iocVersionResult = results[0]
        if globals.verbose:
            print "After remote execution, results is ", iocVersionResult
        if 'iocs' in iocVersionResult and iocVersionResult['iocs']:
            for iocInfo in iocVersionResult['iocs']:
                if globals.verbose:
                    print "{} is already pointing to release {}".format(iocInfo['ioc'], iocInfo['release'])
                iocsInApp.remove(iocInfo['ioc'])
        if iocsInApp:
            wizardContext.steps.insert(wizardContext.currentStep + 1, TemplateWithValuesWizardStep("selectMultiple.html", {
                "selections" : iocsInApp, 
                "stateName" : "IOCsToInitialize",
                "userText" : "Please choose the IOCs you want to initialize in <b>" + facility + "</b>"}))
            wizardContext.steps.insert(wizardContext.currentStep + 2, ProcessIOCSelections())
            wizardContext.data['IOCsInitDone'] = []
        else:
            wizardContext.steps.insert(wizardContext.currentStep + 1, TemplateWithValuesWizardStep("message.html", {
                  "userText" : "<div>All IOCs <i>(in iocBoot)</i> have been initialized in facility <b>{0}</b>.</div><div>Thank you for using cram init</div>".format(facility),
                  "finalStep" : True
                  }))

        return True



class PushCurrentFolderAndLink(WizardStep):
    def enter(self, wizardContext):
        if 'currentLinkExists' in wizardContext.data and wizardContext.data['currentLinkExists']:
            return True
        facility = wizardContext.data['facility']
        facilityConfig = toolconfig.getConfigForFacility(facility)
        if wizardContext.data['pushCurrentRelease'] == 'Yes':
            if globals.verbose:
                print "Pushing the current folder and using it as the 'current' release to facility", facility
            # No need to freshen the release as it does not exists if we are here.
            pushRelease.pushReleaseToFacility(wizardContext.appName, wizardContext.appType, facility, False)
            currentVersion = os.path.basename(os.getcwd())
            if globals.verbose:
                print "Upgrading the MASTER release to {} in facility {}".format(currentVersion, facility)
            upgradeRelease.upgradeReleaseOnFacility(wizardContext.appName, wizardContext.appType, facility, "MASTER", currentVersion)
        else:
            if globals.verbose:
                print "Creating a dummy UpgradeMe release in facility", facility
            commandsAndArgs = []
            packageReleaseFolder = os.path.join(facilityConfig[wizardContext.appType]['releaseFolder'], wizardContext.appName)
            commandsAndArgs.append(('mkdirs', {'name': os.path.join(packageReleaseFolder, 'UpgradeMe')}))
            results = multi_facility_process_helper_client.executeCommandsOnFacility(facility, commandsAndArgs)
            if globals.verbose:
                print results
            upgradeRelease.upgradeReleaseOnFacility(wizardContext.appName, wizardContext.appType, facility, "MASTER", "UpgradeMe")
        if wizardContext.appType in ['HIOC', 'SIOC']:
            iocsInApp = loadIOCNames("iocBoot")
            if iocsInApp:                
                wizardContext.steps.insert(wizardContext.currentStep + 1, FindUninitializedIOCs())
            else:            
                # All done for IOC without any IOC's in iocBoot
                wizardContext.steps.insert(wizardContext.currentStep + 1, TemplateWithValuesWizardStep("message.html", {
                  "userText" : "<div>No IOC's were found in iocBoot. Finished initializing {0} in <b>{1}</b>.</div><div>Thank you for using cram init</div>".format(wizardContext.appName, facility),
                  "finalStep" : True
                  }))
        else:
            # All done for HLA/Tools etc; if we want to run post init scripts; here's a place to do it.
            wizardContext.steps.insert(wizardContext.currentStep + 1, TemplateWithValuesWizardStep("message.html", {
              "userText" : "<div>Finished initializing {0} in facility <b>{1}</b>.</div><div>Thank you for using cram init</div>".format(wizardContext.appName, facility),
              "finalStep" : True
              }))

        return True

class CheckReleaseLinkExists(WizardStep):
    ''' Makes sure we have the softlink for HLA/Matlab apps and the "current" link for IOC apps'''
    def enter(self, wizardContext):
        facility = wizardContext.data['facility']
        facilityConfig = toolconfig.getConfigForFacility(facility)
        commandsAndArgs = []
        # See if we have any releases.
        packageReleaseFolder = os.path.join(facilityConfig[wizardContext.appType]['releaseFolder'], wizardContext.appName)
        if wizardContext.appType in ['SIOC', 'HIOC']:
            releaseLink = os.path.join(packageReleaseFolder, 'current')
        else:
            releaseLink = os.path.join(facilityConfig[wizardContext.appType]['linkFolder'], wizardContext.appName)
        commandsAndArgs.append(('fileinfo', [releaseLink]))
        commandsAndArgs.append(('ls', [packageReleaseFolder]))
        results = multi_facility_process_helper_client.executeCommandsOnFacility(facility, commandsAndArgs)
        
        linkInfo = results[0]
        releaseFolderContents = results[1]
        
        if releaseLink in linkInfo:
            if globals.verbose:
                print "The release link {} seems to exist in facility {} pointing to {}".format(releaseLink, facility, linkInfo[releaseLink]['pointingTo'] if 'pointingTo' in linkInfo[releaseLink] else 'N/A')
            wizardContext.data['currentLinkExists'] = True
            if wizardContext.appType in ['HIOC', 'SIOC']:
                iocsInApp = loadIOCNames("iocBoot")
                if iocsInApp:                
                    wizardContext.steps.insert(wizardContext.currentStep + 1, FindUninitializedIOCs())
                else:            
                    # All done for IOC without any IOC's in iocBoot
                    wizardContext.steps.insert(wizardContext.currentStep + 1, TemplateWithValuesWizardStep("message.html", {
                      "userText" : "<div>No IOC's found. Finished initializing {0} in facility <b>{1}</b></div><div>Thank you for using cram init</div>".format(wizardContext.appName, facility),
                      "finalStep" : True
                      }))
        else:
            # See if there any any releases available
            availableReleases = []
            for path in releaseFolderContents:
                if path['isFolder'] and not path['isLink']:
                    availableReleases.append(path['name'])
            if availableReleases:
                wizardContext.data['availableReleases'] = availableReleases
                wizardContext.steps.insert(wizardContext.currentStep + 1, TemplateWithValuesWizardStep("selectOne.html", {
                      # The | character in the label for the No option is used to specify both the value and the label 
                      "selections" : availableReleases,
                      "stateName" : "linkExistingRelease",
                      "userText" : "There are already some releases in facility " + wizardContext.data["facility"] + ". Please pick one of these release to use as the current release."}))
                wizardContext.steps.insert(wizardContext.currentStep + 2, LinkToExistingRelease())
            else:
                # Add an action to ask the user if we should push the current release.
                # Perhaps we should not do this if the current release is called MAIN_TRUNK
                wizardContext.steps.insert(wizardContext.currentStep + 1, TemplateWithValuesWizardStep("selectOne.html", {
                      # The | character in the label for the No option is used to specify both the value and the label 
                      "selections" : [{ 'label': "No, create a dummy <code>UpgradeMe</code> release instead (to remind me to upgrade later)", 'value': "No"}, {'label' : "Yes, push this folder and use it as the current release", 'value': "Yes"}],
                      "stateName" : "pushCurrentRelease",
                      "userText" : "Do you want to push the release in the current folder <code>" + os.path.basename(os.getcwd()) + "</code> to <b>" + wizardContext.data["facility"] + "</b>?"}))
                wizardContext.steps.insert(wizardContext.currentStep + 2, PushCurrentFolderAndLink())
        
        return True


def execute(args):
    parser = argparse.ArgumentParser(prog="cram install")
    parser.add_argument('-v', '--verbose', action='store_true', help='Enable verbose logging')
    parser.add_argument('-j', '--jsconsole', action='store_true', help='Send Javascript Console to stdout. However, QT will core dump on exit. Use for debugging only')
    parser.add_argument('-n', '--name', help='Specify the application name; we automatically pick this up from the package info if we are running this command in the folder containing the release')
    parser.add_argument('-t', '--type', help='Specifies the type of application from the deployment perspective. Possible values are ' + ", ".join(toolconfig.appTypes.keys()))
    parser.add_argument('-s', '--silent', help='Triggers a silent install; pass in a JSON file that contains all the answers needed for the silent install.', )
    
    args = parser.parse_args(args)
    globals.verbose = args.verbose
    packageInfo = toolconfig.determinePackageInfoFromArgs(args)
    appName = packageInfo['name']
    appType = packageInfo['type']
    
    if not appName or not appType:
        print "Cannot determine the name and type of application. Please run cram describe before attempting to install the package."
        sys.exit(-1)
        return
    if globals.verbose:
        print "Installing package {} of type {}".format(appName, appType)
    steps = [
        TemplateWithValuesWizardStep("selectOne.html", {
            "selections" : toolconfig.getListOfFacilities(), 
            "stateName" : "facility",
            "userText" : "Please choose the facility you want to install this package in"}),
        CheckReleaseFolderExists(),
        CheckReleaseLinkExists()
    ]
    wizard = Wizard("Initialize " + appName, os.path.dirname(__file__), steps, verbose=globals.verbose, appName=appName, appType=appType)
    # This is a convenience to send the javascript console to the terminal; however, QT seems to dump core when we do this. So, use only when debugging
    if args.jsconsole:
        wizard.showConsoleOnTerminal = True
    
    if 'silent' in args and args.silent:
        with open(args.silent, 'r') as f:
            dataForSilentInstall = json.load(f)
        wizard.context.silentInstall = True
        
        if type(dataForSilentInstall) == type(list()):
            # Make a copy of the initial steps to that we do not keep adding to the same list
            initialSteps = list(steps)
            # Silent install data file has a list of things to install
            for dataForIOC in dataForSilentInstall:
                print "Next item in silent install"
                wizard.context.resetForSilentInstall(list(initialSteps))
                wizard.context.data.update(dataForIOC)
                wizard.launchWizard()
        else:
            wizard.context.data.update(dataForSilentInstall)
            wizard.launchWizard()            
    else:    
        wizard.launchWizard()

if __name__ == "__main__":
    execute(sys.argv[1:])
