package com.urbancode.air.plugin.scm

import com.urbancode.air.*
import com.urbancode.air.plugin.scm.changelog.*
import java.util.regex.Matcher

public class SCMQuietPeriod extends SCMChangelog {
    
    //**************************************************************************
    // CLASS
    //**************************************************************************

    //**************************************************************************
    // INSTANCE
    //**************************************************************************
    
    // Clientspec Creation Variables
    boolean createNewClientSpec
    boolean isUsingTemplate
    String templateName
    String clientspec
    
    // Changeset Variables
    final def PERFORCE_CHANGELOG_OUT_DATE = new java.text.SimpleDateFormat("yyyy/MM/dd HH:mm:ss")
    final def CHANGE_SUMMARY_PATTERN = ~'(?m)^Change \\d+ by (\\S+)@\\S+ on (\\d+/\\d+/\\d+ \\d+:\\d+:\\d+)'
    
    public def execute() {
        this.login()
        
        if (createNewClientSpec && isUsingTemplate) {
            createClientSpec()
        }
        
        getClientspecInfo()
        
        def logOutput = super.runLogCommand()
        Date latestDate = parseLogIntoChangesets(logOutput)
        
        if (createNewClientSpec && usUsingTemplate) {
            deleteClientSpec()
        }
        
        return latestDate
    }
    
    public void createClientSpec() {
        if (isUsingTemplate) {
            def command = this.createP4BaseCommand()
            command << 'client' << '-o' << '-t' << templateName << client
            ch.runCommand("Getting Clientspec Template", command) { proc ->
                proc.out.close() // close stdin
                proc.consumeProcessErrorStream(out)
                clientspec = proc.text
            }
        }
        
        // modify the root in the clientspec config to reflect the directoryOffset if
        // one was specified in the command
        clientspec = modifyClientSpecConfigRoot(clientspec, dirOffset)
        
        // run the process
        def createClientCommand = createP4BaseCommand()
        createClientCommand << 'client' << '-i'
        runCommand('Create Clientspec', createClientCommand) { proc ->
            proc.consumeProcessOutput(out, out)
            proc.withWriter{ it << clientspec} // pipe clientspec to child process stdIn and close the stream afterwards
            proc.out.close()
        }
    }
    
    public def parseLogIntoChangesets(String logOutput) {
        Date latestChangeDate
        
        logOutput.eachLine{ line ->
            def matcher = ( line =~ /(?m)^Change (\d+)/ ) // Change X on yyyy/MM/dd by user@client 'comment'
            if (matcher) { // find pattern
                int changeId = matcher.group(1).toInteger()
                def summary = ''
        
                def descCommand = createP4BaseCommand()
                descCommand << 'describe' << '-s' << changeId
                ch.runCommand("Describe Change " + changeId, descCommand) { proc ->
                    proc.consumeProcessErrorStream(System.out)
                    summary = proc.text // get the stdout of the process
                    proc.in.close() // close the stdin of the process
                }
        
                // first line is "Change X by user@client on yyyy/MM/dd HH:mm:ss"
                def summaryMatcher = CHANGE_SUMMARY_PATTERN.matcher(summary)
                if (summaryMatcher) {
                    
                    def user  = summaryMatcher.group(1)
                    def date = PERFORCE_CHANGELOG_OUT_DATE.parse(summaryMatcher.group(2))
                    
                    ChangeSet changeSet = new ChangeSet()
                    
                    changeSet.id = changeId
                    changeSet.user = user
                    changeSet.date = date
                    // Eventually there is a line "Affected files ..."
                    // followed by lines of "... //depot/file#3 edit"
                    summary.eachMatch(/(?m)^\.\.\. (.+)#\d+ (\S+)/){entry, file, operation ->
                        ChangeSetFileEntry csEntry = new ChangeSetFileEntry()
                        csEntry.path = file
                        changeSet.fileSet << csEntry
                    }
                    
                    boolean hasAllowedFile = changeSet.hasAllowedPath(changeSetFilter);
                    boolean hasAllowedAuthor = changeSet.hasAllowedAuthor(changeSetFilter)
                    if (hasAllowedAuthor && hasAllowedFile) {
                        latestChangeDate = changeSet.date
                    }
                    else {
                        def message = new StringBuilder("Changeset ${changeSet.id} skipped because ")
                        if (!hasAllowedAuthor) {
                            message << "it has excluded author ${changeSet.user}"
                        }
                        if (!hasAllowedAuthor && !hasAllowedFile) {
                            message << " and "
                        }
                        if (!hasAllowedFile) {
                            message << "it contains only excluded file paths (${changeSet.fileSet.collect{it.path}})"
                        }
                        println message
                    }
                }
            }
        }
        
        return latestChangeDate
    }
    
    public void deleteClientSpec() {
        def deleteClientCommand = createP4BaseCommand()
        
        deleteClientCommand << 'client' << '-d'
        if (isForceDelete) {
            deleteClientCommand << '-f'
        }
        deleteClientCommand << client
        ch.runCommand('P4 Delete ClientSpec', deleteClientCommand)
    }
    
    /**
    * replace path in a configspec with given path
    */
    def modifyClientSpecConfigRoot = {def config, def newRoot ->
        if (!config) {
            return config
        }
        else if (!newRoot) {
           return config.replaceAll(/(?mi)Root:.*$/, Matcher.quoteReplacement("Root: ${new File('.').canonicalPath}"))
        }
        return config.replaceAll(/(?mi)Root:.*$/, Matcher.quoteReplacement("Root: ${new File('.', newRoot).canonicalPath}"))
    }
}