/*
* Licensed Materials - Property of IBM Corp.
* IBM UrbanCode Build
* (c) Copyright IBM Corporation 2012, 2014. All Rights Reserved.
*
* U.S. Government Users Restricted Rights - Use, duplication or disclosure restricted by
* GSA ADP Schedule Contract with IBM Corp.
*/
package com.urbancode.air.plugin.scm

import com.urbancode.air.CommandHelper
import com.urbancode.air.ExitCodeException

public class SCMCheckout extends SCMStep {
    
    def srcName
    
    public def execute() {
        def props = null;
        
        // try to get the components of the workspace, if we fail we need to create it, if not we need to update it if there
        // there are changes to the components/baselines/snapshots
        def wsInfo = getWorkspaceInfo()
        if (wsInfo.exitCode != 0 && error.length() > 0) {
            if (error.indexOf('Ambiguous') != -1) {
                // this is the case of multiple workspaces with the same name if name and not alias was used
                throw new Exception("More than one workspace exists for name ${workspace}, please remove duplicate workspaces.")
            }
            else {
                // error was due to a missing workspace
                createWorkspace()
                wsInfo = getWorkspaceInfo()
                updateWorkspaceComponents(wsInfo.componentList)
                props = createSnapshot()
                changeWorkspaceBaseline()
                cleanup(null)
                loadWorkspace()
            }
        }
        else {
            changeWorkspaceFlowStream(wsInfo.workspaceStreamName)
            if (snapshot) {
                changeWorkspaceSnapshot()
            }
            updateWorkspaceComponents(wsInfo.componentList)
            changeWorkspaceBaseline()
            acceptChanges()
            try {
                repair()
                undo()
            }
            catch (Exception e) {
                println "Workspace Repair failed, doing a complete load!"
                cleanup()
                loadWorkspace()
            }
            
            props = createSnapshot()
        }
        
        return props
    }

    private void updateWorkspaceComponents(currentComponentList) {
        if (!currentComponentList) {
            currentComponentList = []
        }
        
        def removeComponentList = []
        def addComponentList = []

        if (components) {
            // user provided a list of components
            def componentList = components.split(',')
            componentList.each { component ->
                if (!currentComponentList.contains(component)) {
                    addComponentList << component
                }
            }
            currentComponentList.each { component ->
                if (!componentList.contains(component)) {
                    removeComponentList << component
                }
            }
        }
        else {
            // user did not provide component list, assuming all
            def targetList = getStreamOrSnapshotComponents()
            targetList.each { component ->
                println "Target component: ${component}"
                if (!currentComponentList.contains(component)) {
                    addComponentList << component
                }
            }
            currentComponentList.each { component ->
                if (!targetList.contains(component)) {
                    removeComponentList << component
                }
            }
        }

        if (removeComponentList.size() > 0) {
            removeComponents(removeComponentList)
        }
        if (addComponentList.size() > 0) {
            addComponents(addComponentList)
        }
    }

    private void addComponents(componentList) {
        def cmd = [command, '-a', 'n', '--non-interactive', 'workspace', 'add-components',
                '-r', serverUrl,
                '-u', username,
                '-P', password,
                workspace]
        componentList.each {
            cmd << it
        }
        runCommand('Add Workspace components', cmd)
    }

    private void removeComponents(componentList) {
        def cmd = [command, '-a', 'n', '--non-interactive', 'workspace', 'remove-components',
                '-r', serverUrl,
                '-u', username,
                '-P', password,
                workspace]
        componentList.each {
            cmd << it
        }
        runCommand('Remove Workspace components', cmd)
    }
    
    private void changeWorkspaceBaseline() {
        if (baseline) {
            runCommand('Change Workspace Baseline',
                    [command, '-a', 'n', '--non-interactive', 'workspace', 'rplc',
                    '-o',
                    '-b', baseline,
                    '--all',
                    '-r', serverUrl,
                    '-u', username,
                    '-P', password,
                    workspace,
                    'stream',
                    stream])
        }
    }

    public List<String> getStreamOrSnapshotComponents() {
        def componentText
        def result = []
        def cmdArgs = [command, '-a', 'n', '--non-interactive', 'list', 'components']
        if (snapshot) {
            cmdArgs << '-s'
        }
        cmdArgs << '-r'
        cmdArgs << serverUrl
        cmdArgs << '-u'
        cmdArgs << username
        cmdArgs << '-P'
        cmdArgs << password

        if (snapshot) {
            cmdArgs << snapshot
        }
        else {
            cmdArgs << stream
        }

        CommandHelper cmdHelper = getCmdHelper()
        cmdHelper.ignoreExitValue = true
        def exitCode = runCommand('List stream components', cmdArgs) {
            println it
            componentText = it
        }
        cmdHelper.ignoreExitValue = false
        if (exitCode == 0) {
            componentText?.eachLine {
                def line = it.trim()
                if (snapshot && line.startsWith("(")) {
                    line.find('(.+)') { match, component ->
                        result << stripQuotes(component)
                    }
                }
                else if (line.startsWith('Component') && line[-1] != ')') {
                    line.find('Component: (.+)') { match, component ->
                        result << stripQuotes(component)
                    }
                }
            }
        }
        return result
    }

    private def createSnapshot() {
        def buildlifeSnapshot = buildSnapshot
        if (snapshot || baseline) {
            println "Building from ${snapshot ? 'snapshot' : 'baseline'} so no need to create a new snapshot!"
        }
        else {
            def createSnapshotCommand = [command, '-a', 'n', '--non-interactive', 'create', 'snapshot',
                                         '-n', buildSnapshot,
                                         '-d', buildSnapshotDescription,
                                         workspace ?: stream,
                                         '-r', serverUrl,
                                         '-u', username,
                                         '-P', password,
                                         '-f']

            int i = 1
            def currentSnapshotName = buildSnapshot
            def baseSnapshotName = buildSnapshot
            Closure renameSnapshot = { proc ->
                def text = processOutput(proc)
                // Increment the snapshot name just in case the snapshot command ends up getting retried multiple times
                // This gets run AFTER the creation attempt, so all changes to the snapshot name are taken into account
                // during the NEXT attempt
                buildlifeSnapshot = currentSnapshotName
                if (i <= DEFAULT_RETRIES) {
                    def index = createSnapshotCommand.findIndexOf { it in [currentSnapshotName] }
                    currentSnapshotName = "${baseSnapshotName}-${i++}"
                    createSnapshotCommand[index] = currentSnapshotName
                }
                return text
            }

            runCommand('Create snapshot', createSnapshotCommand, DEFAULT_RETRIES, renameSnapshot, null)
        }
        def newProps = [:]
        if (snapshot || buildSnapshot) {
            newProps["rtc.snapshot.${srcName}"] = snapshot ?: buildlifeSnapshot
        }
        else if (baseline) {
            newProps["rtc.baseline.${srcName}"] = baseline
        }
        
        return newProps
    }

    public void createWorkspace() {
        def cmd = [command, '-a', 'n', '--non-interactive', 'create', 'workspace',
                '-d', 'Created by IBM UrbanCode Build',
                '-r', serverUrl,
                '-u', username,
                '-P', password]
        if (snapshot) {
            cmd << '--snapshot' << snapshot
        }
        else if (baseline) {
            cmd << '-s' << stream
        }
        else {
            cmd << '-s' << stream
        }
        cmd << workspace

        runCommand('Create workspace', cmd, 0, null)
    }

    private void changeWorkspaceSnapshot() {
        runCommand('Change Workspace Snapshot',
                [command, '-a', 'n', '--non-interactive', 'workspace', 'rplc',
                '-o',
                '--all',
                '-r', serverUrl,
                '-u', username,
                '-P', password,
                workspace,
                'snapshot',
                snapshot ?: buildSnapshot])
    }

    private void changeWorkspaceFlowStream(currentStream) {
        if (stream && !stream.equalsIgnoreCase(currentStream)) {
            runCommand('Change Workspace Flow Stream',
                    [command, '-a', 'n', '--non-interactive', 'change-target', 'workspace',
                    '-r', serverUrl,
                    '-u', username,
                    '-P', password,
                    workspace,
                    stream])
        }
    }

    private void loadWorkspace() {
        def dir = directory.canonicalFile
        if (!dir.exists()) {
            dir.mkdirs()
        }
        def cmd = [command, '-a', 'n', '--non-interactive', 'load',
                '-d', dir.toString(),
                '-r', serverUrl,
                '-u', username,
                '-P', password]
        if (!components) {
            cmd << '--all'
        }
        
        if (isForce) {
            cmd << '-f'
        }
        
        if (isIncludeRoot) {
            cmd << '-i'
        }

        cmd << workspace

        if (components) {
            components.split(',').each {
                cmd << it
            }
        }
        
        runCommand('Load workspace', cmd)
    }

    private void acceptChanges() {
        def ignoreExitValue = getCmdHelper().ignoreExitValue
        getCmdHelper().ignoreExitValue = true
        try {
            def exitValue = runCommand('Accept Changes from Stream to Workspace',
                    [command, '-a', 'n', '--non-interactive', 'accept',
                    '-r', serverUrl,
                    '-u', username,
                    '-P', password,
                    '-t', workspace])
            if (exitValue != 0 && exitValue != 52) {
                throw new ExitCodeException("Command failed with exit code: " + exitValue)
            }
        }
        finally {
            getCmdHelper().ignoreExitValue = ignoreExitValue
        }
    }
}
