package net.liftweb.util

/*
 * Copyright 2006-2008 WorldWide Conferencing, LLC
 *
 * 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.
 */


import _root_.scala.xml._
import _root_.scala.xml.transform._

import Helpers._

/**
 * This object provides functions to setup the head section of html documents.</p>
 */
object HeadHelper {
  /**
   * This method returns its parameter unmodified.
   */
  def identity(xml: NodeSeq) : NodeSeq = xml

  /**
   * This method finds all <head> tags that are descendants of
   * <body> tags in the specified NodeSequence and merges
   * the contents of those tags into the <head> tag closest
   * to the root of the XML tree.
   */
  def mergeToHtmlHead(xhtml: NodeSeq) : NodeSeq = {
    def trimText(in: NodeSeq): NodeSeq = in flatMap {
      case e: Elem =>
        Elem(e.prefix, e.label, e.attributes, e.scope, trimText(e.child) :_*)

      case g: Group =>
        trimText(g.child)

      case t: Text =>
        val s = t.text.trim
        if (s.length == 0) NodeSeq.Empty
        Text(s)

      case x => x
    }

    val headInBody: NodeSeq =
    (for (body <- xhtml \ "body";
          head <- findElems(body)(_.label == "head")) yield trimText(head.child)).
    toList.removeDuplicates.flatMap(a => a)

    if (headInBody.isEmpty) xhtml
    else {
      def xform(in: NodeSeq, inBody: Boolean): NodeSeq = in flatMap {
        case e: Elem if !inBody && e.label == "body" =>
          Elem(e.prefix, e.label, e.attributes, e.scope, xform(e.child, true) :_*)

        case e: Elem if inBody && e.label == "head" => NodeSeq.Empty

        case e: Elem if e.label == "head" =>
          Elem(e.prefix, e.label, e.attributes,
               e.scope, e.child ++ headInBody :_*)

        case e: Elem =>
          Elem(e.prefix, e.label, e.attributes, e.scope, xform(e.child, inBody) :_*)

        case g: Group =>
          xform(g.child, inBody)

        case x => x
      }

      xform(xhtml, false)
    }
  }
}