import org.springframework.orm.hibernate3.SessionHolder
import org.springframework.orm.hibernate3.SessionFactoryUtils
import org.springframework.transaction.support.TransactionSynchronizationManager
import org.codehaus.groovy.grails.commons.ConfigurationHolder
import org.codehaus.groovy.grails.web.context.ServletContextHolder
import grails.util.*

grailsHome = Ant.project.properties."environment.GRAILS_HOME"

includeTargets << new File("${grailsHome}/scripts/Bootstrap.groovy")

target('default': "Translates all Context objects that represent Plugins into Plugin objects") {
    packageApp()
    classLoader = new URLClassLoader([classesDir.toURL()] as URL[], rootLoader)
    Thread.currentThread().setContextClassLoader(classLoader)
    loadApp()
    configureApp()

    sessionFactory = grailsApp.mainContext.getBean("sessionFactory")
    session = SessionFactoryUtils.getSession(sessionFactory, true)
    TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session))

    translateContextToPlugin()
}

target(translateContextToPlugin: "The implementation task") {

    // getting the plugin listing wiki page to parse
    def wikiClass = grailsApp.getDomainClass("org.grails.wiki.WikiPage").clazz
    def id = wikiClass.executeQuery("select w.id from WikiPage w where w.title = 'Plugins' order by w.lastUpdated")[0]
    def pluginList = wikiClass.get(525)

    def pluginCategories
    pluginList.body.eachLine { line ->
        if (line.contains('h2.')) {
            pluginCategories = (line - 'h2.').trim().trim().split('/')
            pluginCategories = pluginCategories.collect { cat ->
                def result = cat - 'plugins'
                result = result - 'plugin'
                result = result - 'Plugins'
                result = result - 'Plugin'
                result.trim()
            }
            println ">> plugin categories: $pluginCategories"
        } else {
            matcher = line =~ /\* \[([^\[\]]*)\]/ // matches "* [Plugin name]"
            matcher.each {match ->
                def title = match[1].split(/\|/)[0]
                println ">>>> ${title} in ${pluginCategories}"
                def pluginContent = wikiClass.findByTitle(title)

                if (!pluginContent) {
                    println " ! There was no Wiki Page with title '${title}'"
                    def url = match[1].split(/\|/)[1]
                    println " ->Trying by url: ${url}"
                    pluginContent = wikiClass.findByTitle(url)
                }

                if (!pluginContent) {
                    println "X: ${title} has no docs on grails.org, see: ${match[1].split(/\|/)[1]}"
                } else {
                    contentToPlugin pluginContent, pluginCategories
                }
            }
        }
    }

}

private contentToPlugin(c, tagNames) {
    println "--> Translating $c.title to plugin..."
    def adminUser = grailsApp.getDomainClass("org.grails.auth.User").clazz.findByLogin('admin')
    def pluginClass = grailsApp.getDomainClass("org.grails.plugin.Plugin").clazz
    def wikiClass = grailsApp.getDomainClass("org.grails.wiki.WikiPage").clazz
    def tagClass = grailsApp.getDomainClass("org.grails.taggable.Tag").clazz
    def tagLinkClass = grailsApp.getDomainClass("org.grails.taggable.TagLink").clazz
    def p = pluginClass.newInstance()
    def authors = c.versions*.author
    def mostEditedCount = 0
    def author = (authors as Set).inject(null) {mostEdited, it ->
        if (!mostEdited) {
            mostEditedCount++
            return it
        }
        if (mostEditedCount < authors.count(it)) return it
        mostEdited
    }

    // there will not be a name, so we'll try to guess one
    matcher = c.body =~ /(?<=\binstall-plugin\s)(\w+)(-)?(\w+)[ |\n|\}|\']/
    def name = matcher ? matcher[0][0].toString() : null
    if (name && !pluginClass.executeQuery("from Plugin p where p.name = '${name[0..-2]}'")) {
        // strip off the last char, as the regex will leave it
        p.name = name[0..-2]
        println "--> I think ${c.title} is '$p.name'..."
    } else {
        def newName = 'fix-this-' + c.title.toLowerCase().replaceAll(/\s+/, '-')
        println "!! Cannot procure a plugin name for \"${c.title}\" !!"
        println "\t\"${c.title}\" will be called ${newName} for now."
        p.name = newName
    }

    // check to see if there is already something in the database to remove first
    def existing = pluginClass.executeQuery("from Plugin p where p.title = '${c.title}'")
    existing.each { it
        println "Removing existing plugins before saving '$p.name'"
        it.comments.each { existing.removeComment(it) }
        it.description?.delete()
        it.delete(flush:true)
    }

    p.title = c.title
    p.author = author.login
    p.authorEmail = author.email
    p.documentationUrl = 'not provided'
    p.downloadUrl = 'not provided'
    p.currentRelease = 'not provided'

    if (!p.validate()) {
        println "!! ERROR saving plugins ${p}!!"
        p.errors.allErrors.each { println it }
    }

    assert p.save(flush:true)

    // needed to save the plugin before adding the description wiki page, because we need to identify the plugin by id
    // within the wiki description to ensure it is unique
    def descWiki = wikiClass.newInstance(title:"description-${p.id}", body: c.body)
    p.description = descWiki
    if (!p.description.validate()) {
        println "!! ERROR saving pluging description wiki for ${p}!!"
        p.description.errors.allErrors.each { println it }
    }
    assert p.description.save()
    p.installation = wikiClass.newInstance(title:"installation-${p.id}", body: '')
    assert p.installation.save()
    p.faq = wikiClass.newInstance(title:"faq-${p.id}", body: '')
    assert p.faq.save()
    p.screenshots = wikiClass.newInstance(title:"screenshots-${p.id}", body: '')
    assert p.screenshots.save()

    def v = p.description.createVersion()
    v.author = adminUser
    try {
        v.save(flush:true)
    } catch (Exception e) {
        println "WARNING: Can't save version ${v.title} (${v.number})"
    }   
    p.save(flush:true)

    // working with the tag names provided to add tags to new plugins appropriately
    tagNames.each { tagName ->

        def tag = tagClass.executeQuery("from Tag t where t.name = '${tagName.toLowerCase()}'")[0]
        if (!tag) {
            tag = tagClass.newInstance(name:tagName)
            assert tag.save(flush:true)
            println " * created new tag $tag ($tag.id) * "
        }
        def tagLink = tagLinkClass.newInstance(tag:tag, tagRef:p.id, type:'plugin')
        assert tagLink.save(flush:true)
        println "Added tag $tag to $p"
    }
    if (!p.save(flush:true)) {
        p.errors.allErrors.each { println it }
    }
/*
    println "handling comments..."
    // adding a comment to the plugin about this move
    def text = """This Plugin page was automatically generated.  To see the old Wiki version, go to [${p.title}]. \
This will only be available for a limited time in order for plugin authors to make adjustments during this \
transition."""
    addComment(text, p, adminUser)

    // add a comment to the original content object before locking it
    text = """This wiki page has been locked because another page outdates it.  Please see the new plugin page \
for "${c.title}" [here|${ConfigurationHolder.config.grails.serverURL}/plugin/${p.name}]."""
    addComment(text, c, adminUser)
*/
    println "handling images..."
    // handle any image references
    def context = org.codehaus.groovy.grails.commons.ApplicationHolder.application.parentContext.servletContext
    def imageMatcher = p.description.body =~ /\![a-zA-Z0-9_+\/\.\-\%]+\!/
    imageMatcher.each { match ->
        println "\tFound image reference: $match"
        def relPath = match.toString().replaceAll('!','')
        def existingFile = new File(context.getRealPath("/images/${relPath}"))
        println "\t\tI think I found that image here: ${existingFile.absolutePath}"
        if (!existingFile.exists()) {
            println "\t\tWARNING: ${existingFile.absolutePath} does not exist... trying another place..."
            // try looking in the wiki page name directory
            relPath = "${c.title.encodeAsURL()}/${relPath}"
            existingFile = new File(context.getRealPath("/images/${relPath}"))
            if (!existingFile.exists()) {
                println "\t\tWARNING: ${existingFile.absolutePath} does not exist... I GIVE UP!!"
                return
            }
        }
        def newFileName = relPath.split('/')[-1]
        def newPath = context.getRealPath("/images/${p.description.title.encodeAsURL()}/${newFileName}")
        def newFile = new File("$newPath")
        println "\t\tI'm going to put the new one here: ${newFile.absolutePath}"
        if(!newFile.parentFile.exists()) newFile.parentFile.mkdirs()
        try {
            copyFile(existingFile, newFile)
        } catch (IOException e) {
            println "\t\tERROR: $e"
            return
        }
        // if the image file copy was make properly, we'll need to update the wiki page with the new reference
        def newRelPath = relPath.replace(c.title.encodeAsURL(), p.description.title.encodeAsURL())
        println "\t\tREPLACING ${match.toString()} with !${newRelPath}!"
        p.description.body = p.description.body.replace(match.toString(), "!${newRelPath}!")
    }
    assert p.description.save()
}

private void addComment(text, instance, poster) {
    println "Adding comment..."
    def comment = grailsApp.getDomainClass("org.grails.comments.Comment").clazz.newInstance(body:text, posterId: poster.id, posterClass: poster.class.name)
    println comment
    if (!comment.save()) {
        comment.errors.allErrors.each { println it }
    }
    def link = grailsApp.getDomainClass("org.grails.comments.CommentLink").clazz.newInstance(comment:comment, commentRef:instance.id, type:GrailsNameUtils.getPropertyName(instance.class))
    println link
    if (!link.save()) {
        link.errors.allErrors.each { println it }   
    }
}

private void copyFile(File source, File destination) {
   def reader = source.newReader()
   destination.withWriter { writer ->
       writer << reader
   }
   reader.close()
}