class AssetTreeService {

    boolean transactional = false

    def js = new JsUtilService()

    def g = new org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib()

    /** General */
    def nameDescriptionSeperator = ' -- '

    /** Html class and id settings */
    def buttonHtmlClass = 'tree_button'
    def paneHtmlClass = 'overlayPane'
    def paneHtmlId = 'assetTreePane'
    def paneCloseHtmlClass = 'pane_close'
    def tableDivHtmlClass = 'tree'
    def tableHtmlId = 'assetTreeTable'
    def tableLoadingImgId = 'assetTreeLoadingImg'

    /** Imgs */
    def treeRootImg = ''
    def addImg = ''
    def copyImg = ''
    def bulletTreePlusImg = ''
    def bulletTreeMinusImg = ''
    def dashImg = ''
    def closeImg = ''

    /** Urls */
    def assetTreeActionUrl = ''
    def saveAssetTreeStatusActionUrl = ''

    /** Links */
    def siteCreateBaseLink = ''
    def siteShowBaseLink = ''
    def siteEditBaseLink = ''
    def sectionCreateBaseLink = ''
    def sectionShowBaseLink = ''
    def sectionEditBaseLink = ''
    def assetCreateBaseLink = ''
    def assetShowBaseLink = ''
    def assetEditBaseLink = ''
    def assetCopyBaseLink = ''
    def assetSubItemCreateBaseLink = ''
    def assetSubItemCreateWithParentBaseLink = ''
    def assetSubItemShowBaseLink = ''
    def assetSubItemEditBaseLink = ''

    /**
    * Initialise some class wide variables.
    * This has been done to optimise since g.link and g.resource calls are expensive.
    * This can't be done by class construction since some of the metaclass stuff is not available yet, e.g. 'out' etc.
    * Initialise can't be called from BootStrap.
    */
    def initialise() {
        log.debug "Initialise asset tree variables."

        // Imgs.
        treeRootImg = g.resource(dir:'images/skin',file:'chart_organisation.png')
        addImg = g.resource(dir:'images/skin',file:'database_add.png')
        copyImg = g.resource(dir:'images/skin',file:'page_copy.png')
        bulletTreePlusImg = g.resource(dir:'images/skin',file:'bullet_tree_plus.png')
        bulletTreeMinusImg = g.resource(dir:'images/skin',file:'bullet_tree_minus.png')
        dashImg = g.resource(dir:'images/skin',file:'hline_short.png')
        closeImg = g.resource(dir:'images/skin',file:'cross.png')

        // Urls.
        assetTreeActionUrl = g.createLink(controller: 'assetDetailed', action: 'assetTree')
        saveAssetTreeStatusActionUrl = g.createLink(controller: 'assetDetailed', action: 'saveAssetTreeStatus')

        // Links
        siteCreateBaseLink = g.createLink(controller: 'siteDetailed', action: 'create').toString()
        siteShowBaseLink = g.createLink(controller: 'siteDetailed', action: 'show').toString()
        siteEditBaseLink = g.createLink(controller: 'siteDetailed', action: 'edit').toString()
        sectionCreateBaseLink = g.createLink(controller: 'sectionDetailed', action: 'create').toString()
        sectionShowBaseLink = g.createLink(controller: 'sectionDetailed', action: 'show').toString()
        sectionEditBaseLink = g.createLink(controller: 'sectionDetailed', action: 'edit').toString()
        assetCreateBaseLink = g.createLink(controller: 'assetDetailed', action: 'create').toString()
        assetShowBaseLink = g.createLink(controller: 'assetDetailed', action: 'show').toString()
        assetEditBaseLink = g.createLink(controller: 'assetDetailed', action: 'edit').toString()
        assetCopyBaseLink = g.createLink(controller: 'assetDetailed', action: 'copy').toString()
        assetSubItemCreateBaseLink = g.createLink(controller: 'assetSubItemDetailed', action: 'create').toString()
        assetSubItemCreateWithParentBaseLink = g.createLink(controller: 'assetSubItemDetailed', action: 'create').toString()
        assetSubItemShowBaseLink = g.createLink(controller: 'assetSubItemDetailed', action: 'show').toString()
        assetSubItemEditBaseLink = g.createLink(controller: 'assetSubItemDetailed', action: 'edit').toString()
        // Success.
        return true
    }

    /** Name and Description Formatting */

    def name(obj) {
        obj.name.encodeAsHTML()
    }

    def description(obj) {
        def s =  obj.description.encodeAsHTML()
        s = s? (nameDescriptionSeperator + s) : ''
    }

    /**
    * Build and return the asset tree button.
    * Built here instead of directly in the taglib since we may need to initialise.
    */
    def buildAssetTreeButton(attrs) {

        // Self initialisation ;-)
        if(!treeRootImg)
            initialise()

        def sw = new StringWriter()
        def mkp = new groovy.xml.MarkupBuilder(sw)

        mkp.div(class: buttonHtmlClass) {
            a( href: hrefShowPane() ) {
                img(src: treeRootImg, alt: 'Asset Tree', title: 'Asset Tree')
            }
        } // mkp

        return sw.toString()
    }

    /**
    * Build and return the empty asset tree pane, ready for populating by ajax call to buildAssetTree.
    * Built here instead of directly in the taglib since we may need to initialise.
    */
    def buildAssetTreePane(attrs) {

        // Self initialisation ;-)
        if(!treeRootImg)
            initialise()

        def sw = new StringWriter()
        def mkp = new groovy.xml.MarkupBuilder(sw)

        mkp.div(class: paneHtmlClass, id: paneHtmlId, style: 'display:none;') {
            div(class: paneCloseHtmlClass) {
                a( href: js.toggle(paneHtmlId) ) {
                    img(src: closeImg)
                }
            }

            div(class: tableDivHtmlClass) {
                table(id: tableHtmlId) {
                    tr() {
                        td(valign: 'top', class: 'value') {
                            ul() {
                                img(src: treeRootImg, id: tableLoadingImgId, alt: 'TreeRoot')
                                li() {
                                } // li
                            } // ul
                        } // td
                    } // tr
                } // table
            } // div
        } // mkp

        return sw.toString()

    } // buildAssetPane

    /**
    * Build and return the asset tree table.
    * To be used in conjunction with AssetTreeTagLib which inserts the wrapper div built by buildAssetTreePane().
    * This table replaces the contents of the wrapper div.
    * @returns The asset tree table as a String
    */
    def buildAssetTree(params, session) {

        def startedAt = System.currentTimeMillis()

        // Self initialisation ;-)
        if(!treeRootImg)
            initialise()

        def sites = Site.withCriteria {
            eq("isActive", true)
        }

        def visibleBranches = session.assetTreeVisibleBranches ? session.assetTreeVisibleBranches.tokenize(',') : []

        def branchStyle = { branchId ->
            if(visibleBranches.contains(branchId))
                ''
            else
                'display:none;'
        }

        def branchImg = { branchId ->
            if(visibleBranches.contains(branchId))
                bulletTreeMinusImg
            else
                bulletTreePlusImg
        }

        def divIdCount = 0
        def divId = ''
        def nextDivId = {
            divIdCount++
            divId = 'assetTreeBranch'+divIdCount
        }

        def sw = new StringWriter()
        def mkp = new groovy.xml.MarkupBuilder(sw)

        // Offer a site create link if no sites are found.
        if(!sites) {
            mkp.div(class: tableDivHtmlClass) {

                div(class: paneCloseHtmlClass) {
                    a( href: js.toggle(paneHtmlId) ) {
                        img(src: closeImg)
                    }
                }

                table(id: tableHtmlId) {
                    tr() {
                        td( valign: 'top', class: 'value') {
                            ul() {
                                img(src: treeRootImg, alt: 'TreeRoot')
                                li() {
                                    a(href: siteCreateBaseLink) {
                                        img(src: addImg, alt: 'Add', title: 'Add Site')
                                    }
                                } // li
                            } // ul
                        } // td
                    } // tr
                } // table

                div( class: 'buttons') {
                    span(class: 'button') {
                        input( type: 'button', value: g.message(code: 'default.close.text'), onclick: js.toggle(paneHtmlId, "onclick") )
                    }
                } // button div
            } // mkp
            return sw.toString()
        } // if(!sites)


        // The main populated table.
        /// @todo: use a loop for the subItem levels.
        mkp.div(class: tableDivHtmlClass) {

            div(class: paneCloseHtmlClass) {
                a( href: hrefHideAndSavePane() ) {
                    img(src: closeImg)
                }
            }

            table(id: tableHtmlId) {
                tr() {
                    td(valign: 'top', class: 'value') {
                        ul() {
                            img(src: treeRootImg, alt: 'TreeRoot')
                            for(site in sites.sort { p1, p2 -> p1.name.compareToIgnoreCase(p2.name) }) {
                                li() {
                                    if(site.sections) {
                                        a(href: toggleBranch(nextDivId()) ) {
                                            img( src: branchImg(divId), id: divId+'img' )
                                        }
                                    }
                                    else
                                        img(src: dashImg)
                                    a( href: siteShowLink(site.id), onclick: onclickHideAndSavePane() ) {
                                        yieldUnescaped( name(site)  )
                                    }
                                    yieldUnescaped( description(site)  )
                                    a(href: sectionCreateLink(site.id), onclick: onclickHideAndSavePane()) {
                                        img(src: addImg, alt: 'Add', title: 'Add Section')
                                    }
                                }
                                if(site.sections) {
                                    div( id: divId, style: branchStyle(divId) ) {
                                        ul() {
                                            for(section in site.sections.sort { p1, p2 -> p1.name.compareToIgnoreCase(p2.name) }) {
                                                li() {
                                                    if(section.assets) {
                                                        a( href: toggleBranch(nextDivId()) ) {
                                                            img(src: branchImg(divId), id: divId+'img' )
                                                        }
                                                    }
                                                    else
                                                        img(src: dashImg)
                                                    a( href: sectionShowLink(section.id), onclick: onclickHideAndSavePane() ) {
                                                        yieldUnescaped( name(section) )
                                                    }
                                                    yieldUnescaped( description(section)  )
                                                    a(href: assetCreateLink(section.id), onclick: onclickHideAndSavePane()) {
                                                        img(src: addImg, alt: 'Add', title: 'Add Asset')
                                                    }
                                                }

                                                if(section.assets) {
                                                    div( id: divId, style: branchStyle(divId) ) {
                                                        ul() {
                                                            for(asset in section.assets.sort { p1, p2 -> p1.name.compareToIgnoreCase(p2.name) }) {
                                                                li() {
                                                                    if(asset.assetSubItems) {
                                                                        a( href: toggleBranch(nextDivId()) ) {
                                                                            img(src: branchImg(divId), id: divId+'img' )
                                                                        }
                                                                    }
                                                                    else
                                                                        img(src: dashImg)
                                                                    a( href: assetShowLink(asset.id), onclick: onclickHideAndSavePane() ) {
                                                                        yieldUnescaped( name(asset) )
                                                                    }
                                                                    yieldUnescaped( description(asset) )
                                                                    a(href: assetSubItemCreateLink(asset.id), onclick: onclickHideAndSavePane()) {
                                                                        img(src: addImg, alt: 'Add', title: 'Add Sub Item')
                                                                    }
                                                                    a(href: assetCopyLink(asset.id), onclick: onclickHideAndSavePane()) {
                                                                        img(src: copyImg, alt: 'Add', title: 'Copy Asset')
                                                                    }
                                                                } // li

                                                                if(asset.assetSubItems) {
                                                                    div( id: divId, style: branchStyle(divId) ) {
                                                                        ul() {
                                                                            for(assetSubItemL1 in asset.assetSubItems.sort { p1, p2 -> p1.name.compareToIgnoreCase(p2.name) }) {
                                                                                li() {
                                                                                    if(assetSubItemL1.subItems) {
                                                                                        a( href: toggleBranch(nextDivId()) ) {
                                                                                            img(src: branchImg(divId), id: divId+'img' )
                                                                                        }
                                                                                    }
                                                                                    else
                                                                                        img(src: dashImg)
                                                                                    a( href: assetSubItemShowLink(assetSubItemL1.id), onclick: onclickHideAndSavePane() ) {
                                                                                        yieldUnescaped( name(assetSubItemL1) )
                                                                                    }
                                                                                    yieldUnescaped( description(assetSubItemL1) )
                                                                                    a(href: assetSubItemCreateWithParentLink(assetSubItemL1.id), onclick: onclickHideAndSavePane()) {
                                                                                        img(src: addImg, alt: 'Add', title: 'Add Sub Item')
                                                                                    }
                                                                                } // li

                                                                                if(assetSubItemL1.subItems) {
                                                                                    div( id: divId, style: branchStyle(divId) ) {
                                                                                        ul() {
                                                                                            for(assetSubItemL2 in assetSubItemL1.subItems.sort { p1, p2 -> p1.name.compareToIgnoreCase(p2.name) }) {
                                                                                                li() {
                                                                                                    if(assetSubItemL2.subItems) {
                                                                                                        a( href: toggleBranch(nextDivId()) ) {
                                                                                                            img( src: branchImg(divId), id: divId+'img' )
                                                                                                        }
                                                                                                    }
                                                                                                    else
                                                                                                        img(src: dashImg)
                                                                                                    a( href: assetSubItemShowLink(assetSubItemL2.id), onclick: onclickHideAndSavePane() ) {
                                                                                                        yieldUnescaped( name(assetSubItemL2) )
                                                                                                    }
                                                                                                    yieldUnescaped( description(assetSubItemL2) )
                                                                                                    a(href: assetSubItemCreateWithParentLink(assetSubItemL2.id), onclick: onclickHideAndSavePane()) {
                                                                                                        img(src: addImg, alt: 'Add', title: 'Add Sub Item')
                                                                                                    }
                                                                                                } // li

                                                                                                if(assetSubItemL2.subItems) {
                                                                                                    div( id: divId, style: branchStyle(divId) ) {
                                                                                                        ul() {
                                                                                                            for(assetSubItemL3 in assetSubItemL2.subItems.sort { p1, p2 -> p1.name.compareToIgnoreCase(p2.name) }) {
                                                                                                                li() {
                                                                                                                    if(assetSubItemL3.subItems) {
                                                                                                                        a( href: toggleBranch(nextDivId()) ) {
                                                                                                                            img( src: branchImg(divId), id: divId+'img' )
                                                                                                                        }
                                                                                                                    }
                                                                                                                    else
                                                                                                                        img(src: dashImg)
                                                                                                                    a( href: assetSubItemShowLink(assetSubItemL3.id), onclick: onclickHideAndSavePane() ) {
                                                                                                                        yieldUnescaped( name(assetSubItemL3) )
                                                                                                                    }
                                                                                                                    yieldUnescaped( description(assetSubItemL3) )
                                                                                                                    a(href: assetSubItemCreateWithParentLink(assetSubItemL3.id), onclick: onclickHideAndSavePane()) {
                                                                                                                        img(src: addImg, alt: 'Add', title: 'Add Sub Item')
                                                                                                                    }
                                                                                                                } // li

                                                                                                                if(assetSubItemL3.subItems) {
                                                                                                                    div( id: divId, style: branchStyle(divId) ) {
                                                                                                                        ul() {
                                                                                                                            for(assetSubItemL4 in assetSubItemL3.subItems.sort { p1, p2 -> p1.name.compareToIgnoreCase(p2.name) }) {
                                                                                                                                li() {
            //                                                                                                                         if(assetSubItemL4.subItems) {
            //                                                                                                                             a( href: toggleBranch(nextDivId()) ) {
            //                                                                                                                                 img( src: branchImg(divId), id: divId+'img' )
            //                                                                                                                             }
            //                                                                                                                         }
            //                                                                                                                         else
                                                                                                                                    img(src: dashImg)
                                                                                                                                    a( href: assetSubItemShowLink(assetSubItemL4.id), onclick: onclickHideAndSavePane() ) {
                                                                                                                                        yieldUnescaped( name(assetSubItemL4) )
                                                                                                                                    }
                                                                                                                                    yieldUnescaped( description(assetSubItemL4) )
            //                                                                                                                         a(href: assetSubItemCreateWithParentLink(assetSubItemL4.id), onclick: onclickHideAndSavePane()) {
            //                                                                                                                             img(src: addImg, alt: 'Add', title: 'Add Sub Item')
            //                                                                                                                         }
                                                                                                                                } // li

                                                                                                                            } // assetSubItemL4
                                                                                                                        } // ul
                                                                                                                    } // div
                                                                                                                } // if(assetSubItemL3.subItems)


                                                                                                            } // assetSubItemL3
                                                                                                        } // ul
                                                                                                    } // div
                                                                                                } // if(assetSubItemL2.subItems)

                                                                                            } // assetSubItemL2
                                                                                        } // ul
                                                                                    } // div
                                                                                } // if(assetSubItemL1.subItems)

                                                                            } // assetSubItemL1
                                                                        } // ul
                                                                    } // div
                                                                } // if(asset.assetSubItems)

                                                            } // assets
                                                        } // ul
                                                    } // div
                                                } // if(section.assets)

                                            } //sections
                                        } // ul
                                    } // div
                                } // if(site.sections)
                            } // sites
                        } // ul
                    } // td
                } // tr
            } // table

            div( class: 'buttons') {
                span(class: 'button') {
                    input( type: 'button', value: g.message(code: 'default.close.text'), onclick: onclickHideAndSavePane() )
                }
            }

        } // mkp

        def totalTime = (System.currentTimeMillis() - startedAt)/1000
        log.debug "Total time to build asset tree: " + totalTime + "sec."
        return sw.toString()

    } // buildAssetTree

    /** js calls */

    def hrefShowPane() {
        'javascript: showAssetTreePane(\"assetTreePane\", \"assetTreeLoadingImg' +'\", \"' + assetTreeActionUrl + '\");'
    }

    def onclickHideAndSavePane() {
        'return hideAssetTreePane(\"assetTreePane\", \"assetTreeTable' + '\", \"' + saveAssetTreeStatusActionUrl + '\");'
    }

    def hrefHideAndSavePane() {
        'javascript: hideAssetTreePane(\"assetTreePane\", \"assetTreeTable' + '\", \"' + saveAssetTreeStatusActionUrl + '\");'
    }

    def toggleBranch(divId) {
        js.toggleWithImg(divId, divId + 'img', bulletTreeMinusImg, bulletTreePlusImg)
    }

    /** Links */

    def siteShowLink(id) {
        siteShowBaseLink + '/' + id
    }

    def siteEditLink(id) {
        siteEditBaseLink + '/' + id
    }

    def sectionCreateLink(siteId) {
        sectionCreateBaseLink + '?site.id=' + siteId
    }

    def sectionShowLink(id) {
        sectionShowBaseLink + '/' + id
    }

    def sectionEditLink(id) {
        sectionEditBaseLink + '/' + id
    }

    def assetCreateLink(sectionId) {
        assetCreateBaseLink + '?section.id=' + sectionId
    }

    def assetShowLink(id) {
        assetShowBaseLink + '/' + id
    }

    def assetEditLink(id) {
        assetEditBaseLink + '/' + id
    }

    def assetCopyLink(id) {
        assetCopyBaseLink + '?assetToCopy.id=' + id
    }

    def assetSubItemCreateLink(assetId) {
        assetSubItemCreateBaseLink + '?asset.id=' + assetId
    }

    def assetSubItemCreateWithParentLink(parentItemId) {
        assetSubItemCreateWithParentBaseLink + '?parentItem.id=' + parentItemId
    }

    def assetSubItemShowLink(id) {
        assetSubItemShowBaseLink + '/' + id
    }

    def assetSubItemEditLink(id) {
        assetSubItemEditBaseLink + '/' + id
    }

} // end class