//////////////////////////////////////////////////////////////////////////////
// 
//                    Copyright 2012, Cornutum Project
//                             www.cornutum.org
//
//////////////////////////////////////////////////////////////////////////////

package org.cornutum.tcases.io;

import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.text.translate.NumericEntityEscaper;

import java.io.OutputStream;
import java.io.Writer;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
  
/**
 * Supports creation of an XML document stream.
 *
 */
public class XmlWriter extends IndentedWriter
  {
  /**
   * Creates a new XmlWriter object.
   */
  public XmlWriter( OutputStream output)
    {
    super( output);
    }

  /**
   * Creates a new XmlWriter object.
   */
  public XmlWriter( Writer writer)
    {
    super( writer);
    }

  /**
   * Writes the standard XML document declaration
   */
  public void writeDeclaration( String encoding)
    {
    print( "<?xml version=\"1.0\"");
    if( encoding != null)
      {
      writeAttribute( "encoding", encoding);
      }
    println( "?>");
    }

  /**
   * Writes the standard XML document declaration
   */
  public void writeDeclaration()
    {
    writeDeclaration( null);
    }

  /**
   * Begins an element start tag.
   */
  protected void writeTagStart( String tag)
    {
    startLine();
    print( "<");
    print( tag);
    }

  /**
   * Completes an element start tag.
   */
  protected void writeTagEnd()
    {
    print( ">");
    println();
    }

  /**
   * Writes an element end tag.
   */
  protected void writeElementEnd( String tag)
    {
    startLine();
    print( "</");
    print( tag);
    print( ">");
    println();
    }

  /**
   * Writes the end of an empty element.
   */
  protected void writeEmptyElementEnd()
    {
    print( "/>");
    println();
    }

  /**
   * Writes an attribute definition.
   */
  protected void writeAttribute( String name, String value)
    {
    print( " ");
    print( name);
    print( "=\"");
    // StringEscapeUtils escapes symbols ', < >, &, ", and some control characters
    // NumericEntityEscaper translates additional control characters \n, \t, ...
    print( NumericEntityEscaper.below(0x20).translate(StringEscapeUtils.escapeXml11(value)));
    print( "\"");
    }

  /**
   * Creates a new ElementWriter with the given tag.
   */
  public ElementWriter element( String tag)
    {
    return new ElementWriter().tag( tag);
    }

  public class ElementWriter
    {
    public ElementWriter()
      {
      tag( null);
      attributes( new LinkedHashMap<String,String>());
      content( (String) null);
      }

    public ElementWriter tag( String tag)
      {
      tag_ = tag;
      return this;
      }

    public ElementWriter attributes( Map<String,String> attributes)
      {
      attributes_ = attributes;
      return this;
      }

    public ElementWriter attribute( String name, String value)
      {
      attributes_.put( name, value);
      return this;
      }

    public ElementWriter attributeIf( boolean condition, String name, String value)
      {
      if( condition)
        {
        attribute( name, value);
        }
      return this;
      }

    public ElementWriter attributeIf( String name, Optional<String> value)
      {
      if( value.isPresent())
        {
        attribute( name, value.get());
        }
      return this;
      }

    public ElementWriter content( String content)
      {
      content_ = content;
      contentWriter_ = null;
      return this;
      }

    public ElementWriter content( Runnable contentWriter)
      {
      content_ = null;
      contentWriter_ = contentWriter;
      return this;
      }

    public ElementWriter contentIf( boolean condition, Runnable contentWriter)
      {
      if( condition)
        {
        content( contentWriter);
        }
      return this;
      }

    public void write()
      {
      if( content_ != null)
        {
        startLine();
        print( "<"); print( tag_); print( ">");
        print( content_);
        print( "</"); print( tag_); print( ">");
        println();
        }
      else
        {
        writeTagStart( tag_);
        attributes_.forEach( (k,v) -> writeAttribute( k, v));

        if( contentWriter_ != null)
          {
          writeTagEnd();
          indent();
          contentWriter_.run();
          unindent();
          writeElementEnd( tag_);
          }
        else
          {
          writeEmptyElementEnd();
          }
        }
      }
    
    private String tag_;
    private Map<String,String> attributes_;
    private String content_;
    private Runnable contentWriter_;
    } 
  }