/*
 * Copyright 2007-2009 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.icescrum.web

import org.codehaus.groovy.grails.plugins.web.taglib.JavascriptProvider

/**
 * @author Sergey Nebolsin (nebolsin@prophotos.ru)
 * @author Finn Herpich (finn.herpich <at> marfinn-software <dot> de)
 */
class JQueryProvider implements JavascriptProvider {
    /**
     * doRemoteFunction creates a jQuery-AJAX-Call
     *
     * @param taglib
     * @param attrs
     * @param out
     *
     * @return the jQuery-like formatted code for an AJAX-request
     */
    def doRemoteFunction(taglib, attrs, out) {
        // Optional, onLoad
        if (attrs.onLoading) {
            out << "${attrs.onLoading};"
        }

        // Start ajax
        out << /jQuery.ajax({/

        // Method
        def method = (attrs.method ? attrs.remove('method') : 'POST')
        out << "type:'$method'"

        // Optional, synchron call
        if ("false" == attrs.asynchronous) {
            out << ",async:false"
            attrs.remove('asynchronous')
        }

        // Optional, dataType to use
        if (attrs.dataType) {
            out << ",dataType:'${attrs.remove('dataType')}'"
        }

        // Additional attributes
        if (attrs.params || attrs.jsParams) {
            if (!(attrs?.params instanceof Map)) {
                // tags like remoteField don't deliver a map
                out << ",data:${attrs.remove('params')}"
            } else {
                out << ",data:{"

                def hasParams = false

                if (attrs?.params instanceof Map) {
                    hasParams = true
                    out << attrs.remove('params').collect { k, v ->
                        "\'" +
                        "${k}".encodeAsJavaScript() +
                        "\': \'" +
                        "${v}".encodeAsJavaScript() +
                        "\'"
                    }.join(",")
                }

                if (attrs?.jsParams instanceof Map) {
                    if (hasParams) {
                        out << ","
                    }

                    out << attrs.remove('jsParams').collect { k, v ->
                        "\'" +
                        "${k}".encodeAsJavaScript() +
                        "\': \'" +
                        "${v}".encodeAsJavaScript() +
                        "\'"
                    }.join(",")
                }

                out << "}"
            }
        }

        // build url
        def url = attrs.url ? taglib.createLink(attrs.remove('url')) : taglib.createLink(attrs);
        out << ", url:'${url}'"

        // Add callback
        buildCallback(attrs, out)

        // find all onX callback events
        def callbacks = attrs.findAll { k, v ->
            k ==~ /on(\p{Upper}|\d){1}\w+/
        }

        // remove all onX callback events
        callbacks.each { k, v ->
            attrs.remove(k)
        }

        out << "});"

        // Yeah, I know, return is not needed, but I like it
        return out
    }

    /**
     *  Helper method to create callback object
     *
     * @param attrs Attributes to use for the callback
     * @param out Variable to attache the output
     */
    def buildCallback(attrs, out) {
        // TODO check for strlen
        if (out) {
            out << ','
        }

        //*** success
        out << 'success:function(data,textStatus){'

        if (attrs.onLoaded) {
            out << "${attrs.onLoaded};"
        }

        if (attrs.update instanceof Map) {
            if (attrs.update?.success) {
                out << "jQuery('#${attrs.update.success}').html(data);"
            }
        } else if (attrs.update) {
            out << "jQuery('#${attrs.update}').html(data);"
        }

        if (attrs.onSuccess) {
            out << "${attrs.onSuccess};"
        }

        out << '}'

        //*** failure
        out << ',error:function(XMLHttpRequest,textStatus,errorThrown){'

        if (attrs.update instanceof Map) {
            if (attrs.update?.failure) {
                // Applied to GRAILSPLUGINS-1919
                out << "jQuery('#${attrs.update?.failure}').html(XMLHttpRequest.responseText);"
            }
        }

        if (attrs.onFailure) {
            out << "${attrs.onFailure};"
        }

        out << '}'

        if (attrs.onComplete) {
            out << ",complete:function(XMLHttpRequest,textStatus){${attrs.onComplete}}"
        }
    }

    /**
     * Serializes the surrounding form.
     *
     * @param attrs attrs.params to serialize
     */
    def prepareAjaxForm(attrs) {
        // Fix for http://jira.codehaus.org/browse/GRAILSPLUGINS-1865
        if (attrs.forSubmitTag) {
            attrs.params = "jQuery(this).parents('form:first').serialize()".toString()
        } else {
            attrs.params = "jQuery(this).serialize()".toString()
        }
    }
}