/* ====================================================================
   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements.  See the NOTICE file distributed with
   this work for additional information regarding copyright ownership.
   The ASF licenses this file to You 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.apache.poi.hwpf.converter;

import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.poi.util.Beta;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;

@Beta
public class HtmlDocumentFacade
{
    protected final Element body;
    protected final Document document;
    protected final Element head;
    protected final Element html;

    /**
     * Map from tag name, to map linking known styles and css class names
     */
    private Map<String, Map<String, String>> stylesheet = new LinkedHashMap<String, Map<String, String>>();
    private Element stylesheetElement;

    protected Element title;
    protected Text titleText;

    public HtmlDocumentFacade( Document document )
    {
        this.document = document;

        html = document.createElement( "html" );
        document.appendChild( html );

        body = document.createElement( "body" );
        head = document.createElement( "head" );
        stylesheetElement = document.createElement( "style" );
        stylesheetElement.setAttribute( "type", "text/css" );

        html.appendChild( head );
        html.appendChild( body );
        head.appendChild( stylesheetElement );

        addStyleClass( body, "b", "white-space-collapsing:preserve;" );
    }

    public void addAuthor( String value )
    {
        addMeta( "author", value );
    }

    public void addDescription( String value )
    {
        addMeta( "description", value );
    }

    public void addKeywords( String value )
    {
        addMeta( "keywords", value );
    }

    public void addMeta( final String name, String value )
    {
        Element meta = document.createElement( "meta" );
        meta.setAttribute( "name", name );
        meta.setAttribute( "content", value );
        head.appendChild( meta );
    }

    public void addStyleClass( Element element, String classNamePrefix,
            String style )
    {
        String exising = element.getAttribute( "class" );
        String addition = getOrCreateCssClass( classNamePrefix, style );
        String newClassValue = WordToHtmlUtils.isEmpty( exising ) ? addition
                : ( exising + " " + addition );
        element.setAttribute( "class", newClassValue );
    }

    protected String buildStylesheet(
            final Map<String, Map<String, String>> prefixToMapOfStyles )
    {
        StringBuilder stringBuilder = new StringBuilder();
        for ( Map<String, String> byPrefix : prefixToMapOfStyles.values() )
        {
            for ( Map.Entry<String, String> byStyle : byPrefix.entrySet() )
            {
                String style = byStyle.getKey();
                String className = byStyle.getValue();

                stringBuilder.append( "." );
                stringBuilder.append( className );
                stringBuilder.append( "{" );
                stringBuilder.append( style );
                stringBuilder.append( "}\n" );
            }
        }
        final String stylesheetText = stringBuilder.toString();
        return stylesheetText;
    }

    public Element createBlock()
    {
        return document.createElement( "div" );
    }

    public Element createBookmark( String name )
    {
        final Element basicLink = document.createElement( "a" );
        basicLink.setAttribute( "name", name );
        return basicLink;
    }

    public Element createHeader1()
    {
        return document.createElement( "h1" );
    }

    public Element createHeader2()
    {
        return document.createElement( "h2" );
    }

    public Element createHyperlink( String internalDestination )
    {
        final Element basicLink = document.createElement( "a" );
        basicLink.setAttribute( "href", internalDestination );
        return basicLink;
    }

    public Element createImage( String src )
    {
        Element result = document.createElement( "img" );
        result.setAttribute( "src", src );
        return result;
    }

    public Element createLineBreak()
    {
        return document.createElement( "br" );
    }

    public Element createListItem()
    {
        return document.createElement( "li" );
    }

    public Element createOption( String value, boolean selected )
    {
        Element result = document.createElement( "option" );
        result.appendChild( createText( value ) );
        if ( selected )
        {
            result.setAttribute( "selected", "selected" );
        }
        return result;
    }

    public Element createParagraph()
    {
        return document.createElement( "p" );
    }

    public Element createSelect()
    {
        Element result = document.createElement( "select" );
        return result;
    }

    public Element createTable()
    {
        return document.createElement( "table" );
    }

    public Element createTableBody()
    {
        return document.createElement( "tbody" );
    }

    public Element createTableCell()
    {
        return document.createElement( "td" );
    }

    public Element createTableColumn()
    {
        return document.createElement( "col" );
    }

    public Element createTableColumnGroup()
    {
        return document.createElement( "colgroup" );
    }

    public Element createTableHeader()
    {
        return document.createElement( "thead" );
    }

    public Element createTableHeaderCell()
    {
        return document.createElement( "th" );
    }

    public Element createTableRow()
    {
        return document.createElement( "tr" );
    }

    public Text createText( String data )
    {
        return document.createTextNode( data );
    }

    public Element createUnorderedList()
    {
        return document.createElement( "ul" );
    }

    public Element getBody()
    {
        return body;
    }

    public Document getDocument()
    {
        return document;
    }

    public Element getHead()
    {
        return head;
    }

    public String getOrCreateCssClass( String classNamePrefix, String style )
    {
        if ( !stylesheet.containsKey( classNamePrefix ) )
            stylesheet.put( classNamePrefix, new LinkedHashMap<String, String>(
                    1 ) );

        Map<String, String> styleToClassName = stylesheet.get( classNamePrefix );
        String knownClass = styleToClassName.get( style );
        if ( knownClass != null )
            return knownClass;

        String newClassName = classNamePrefix + ( styleToClassName.size() + 1 );
        styleToClassName.put( style, newClassName );
        return newClassName;
    }

    public String getTitle()
    {
        if ( title == null )
            return null;

        return titleText.getTextContent();
    }

    public void setTitle( String titleText )
    {
        if ( WordToHtmlUtils.isEmpty( titleText ) && this.title != null )
        {
            this.head.removeChild( this.title );
            this.title = null;
            this.titleText = null;
        }

        if ( this.title == null )
        {
            this.title = document.createElement( "title" );
            this.titleText = document.createTextNode( titleText );
            this.title.appendChild( this.titleText );
            this.head.appendChild( title );
        }

        this.titleText.setData( titleText );
    }

    public void updateStylesheet()
    {
        stylesheetElement.setTextContent( buildStylesheet( stylesheet ) );
    }
}
