/**
 * Erzeugt ein neues XMLAttribute
 * @constructor
 * @param {String} name Der Name des Attributs
 * @param {String} value Der Wert des Attributs
 */
var XMLAttribute = function(name, value) {
	this.m_strName = name;
	this.m_strValue = value;
}

/**
 * Gibt den Namen dieses XMLAttributes zurück
 * @return {String} Der Name dieses XMLAttributes
 */
XMLAttribute.prototype.getName = function() {
	return this.m_strName;
}

/**
 * Gibt den Wert dieses XMLAttributes zurück
 * @return {String} Der Wert dieses XMLAttributes
 */
XMLAttribute.prototype.getValue = function() {
	return this.m_strValue;
}

/**
 * @constructor
 * @param {String} version, XML Versionsstring, "1.0" wenn nicht anders angegeben
 * @param {String} encoding XML Encoding, "UTF-8" wenn nicht anders angegeben 
 */
var XMLWriter = function(version, encoding) {
	
	this.m_xmlVersion = "1.0";
	this.m_xmlEncoding= "UTF-8";
	this.m_stack = new Array();
	this.m_strSrc = "";
	this.m_docWritten = false;
	this.m_headerWritten = false;
	this.m_DTDWritten = false;
	this.m_nStartedElemsCount = 0;
	
	if (typeof(version) == 'string') {
		this.m_xmlVersion = version;
	}
	if (typeof(encoding) == 'string') {
		this.m_xmlEncoding = version;
	}
}
/**
 * Konstante für DocType XHTML 1.1 
 */
XMLWriter.prototype.DTD_XHTML11 = 'html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"';

/**
 * Konstante für DocType XHTML 1.0 Transitional
 */
XMLWriter.prototype.DTD_XHTML10_TRANSITIONAL = 'html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "xhtml1-transitional.dtd"';

/**
 * Konstante für DocType XHTML 1.0 Strict
 */
XMLWriter.prototype.DTD_XHTML10_STRICT = 'html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"';

/**
 * Konstante für DocType XHTML 1.0 Frameset 
 */
XMLWriter.prototype.DTD_XHTML10_FRAMESET = 'html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"';
	
/**
 * Schreibt den XML Header (Methode überspringen, wenn kein XML Header gewünscht)
 */	
XMLWriter.prototype.writeStartDocument = function() {
	this.m_strSrc = '<?xml version="'+this.m_xmlVersion+'" encoding="'+this.m_xmlEncoding+'"?>\n';
	this.m_nStartedElemsCount = 0;
	this.m_docWritten = false;
	this.m_headerWritten = true;
}

/**
 * Schreibt eine DocType Definition
 * @param {String} strDTD DTD String  (der teil zwischen <!DOCTYPE und >)
 * @return {Boolean} true, wenn die DTD geschrieben wurde, false wenn keine DTD geschrieben wurde, weil bereits elemente geschrieben wurden 
 * @see XMLWriter.DTD_*
 */
XMLWriter.prototype.writeDTD = function(strDTD) {
	var bRes = false;
	if (this.m_nStartedElemsCount === 0) {
		this.m_strSrc += '<!DOCTYPE ' + strDTD + '>\n';
		bRes = true;
	}
	return bRes;
	
}

/**
 * Öffnet und schreibt Schreibt ein neues XML Element
 * @param {String} strElemName Name des Elements
 * @param {Array} attribs Ein Array von XMLAttribute Elementen, wenn nicht gegeben werden keine Attribute geschrieben
 * @see {XMLAttribute}
 */
XMLWriter.prototype.writeStartElement = function(strElemName, attribs) {
	this.m_strSrc += '<' + strElemName;
	if (attribs) {
		for (var i=0; i<attribs.length; i++) {
			this.m_strSrc += ' ' + attribs[i].getName() + '="' + attribs[i].getValue() + '"';
		}	
	}
	this.m_strSrc += '>';
	this.m_nStartedElemsCount++;
	this.m_stack.push(strElemName);
}

/**
 * Schreibt eine Zeichenkette in den XML Text.
 * ACHTUNG: Diese Zeichenkette wird nicht enkodiert. Es ist also möglich XML Strings einzufügen. Daher ist diese Funktion äußerst unsicher und Ihre Verwendung nicht empfohlen.
 * @deprecated
 * @param {String} strChars Zeichenkette
 * @return {Boolean} true Wenn die Zeichenkette geschrieben wurde, false wenn die Zeichenkette nicht geschrieben wurde, weil kein Element offen war.
 */
XMLWriter.prototype.writeCharacters = function(strChars) {
	var bRes = false;
	if (this.m_stack.length > 0) {
		this.m_strSrc += strChars;
		bRes = true;
	}
	return bRes;
}

/**
 * Schreibt eine Zeichenkette und wandelt ggf. vorhandene Sonderzeichen in Ihre Entitäten um 
 * @param {String} strChars Zeichenkette
 * @return {Boolean} true Wenn die Zeichenkette geschrieben wurde, false wenn die Zeichenkette nicht geschrieben wurde, weil kein Element offen war.
 */
XMLWriter.prototype.writeEncodedCharacters = function(strChars) {
	var bRes = false;
	if (this.m_stack.length > 0) {
		var strEncChars = "";
		if (typeof(bb.string.escapeXml)=='function') {
			strEncChars = bb.string.escapeXml(strChars);
		}
		else {
			var div = document.createElement('div');
			var textNode = document.createTextNode(strChars);
			div.appendChild(textNode);
			strEncChars = div.innerHTML;
			delete textNode;
			delete div;
		}
		this.m_strSrc += strEncChars;
		bRes = true;
	}
	return bRes;
}

/**
 * Schreibt eine CDATA Section mit dem geg. Inhalt
 * @param {String} strCDataContents Inhalt der CDATA Section
 * @return {Boolean} false, wenn die CDATA nicht geschrieben werden konnet da kein Element offen ist, ansonten true
 */
XMLWriter.prototype.writeCDATASection = function(strCDataContents) {
	var bRes = false;
	if (this.m_stack.length>0) {
		this.m_strSrc += '<![CDATA['
		this.writeCharacters(strCDataContents); 
		this.m_strSrc += ']]>';
		bRes = true;
	}
	return bRes;
}

/**
 * Schreib eine XML Kommentar
 * @param {String} strCommentContents Kommentarinhalt
 */
XMLWriter.prototype.writeComment = function(strCommentContents) {
	this.m_strSrc += '<!-- ';
	this.writeEncodedCharacters(strCommentContents);
	this.m_strSrc += ' -->';
}

/**
 * Schließt das zuletzt geöffnete Element
 */
XMLWriter.prototype.writeEndElement = function() {
	this.m_strSrc += '</' + this.m_stack.pop() + '>\n'
}

/**
 * Schreibt ein leeres XML Element
 * @param {String} strElemName Name des Elements
 * @param {Array} attribs Ein Array von XMLAttribute Elementen, wenn nicht gegeben werden keine Attribute geschrieben
 * @deprecated Benutzen Sie stattdessen @see {XMLWriter.writeEmptyElement}
 * @see {XMLAttribute}
 */
XMLWriter.prototype.writeEmptyElementWithAttribs = function(strElemName, attribs) {
	this.writeEmptyElement(strElemName, attribs);
}

/**
 * Öffnet und schreibt Schreibt ein neues XML Element
 * @param {String} strElemName Name des Elements
 * @param {Array} attribs Ein Array von XMLAttribute Elementen, wenn nicht gegeben werden keine Attribute geschrieben
 * @deprecated Benutzen Sie stattdessen @see {XMLWriter.writeStartElement}
 * @see {XMLAttribute}
 */
XMLWriter.prototype.writeStartElementWithAttribs = function(strElemName, attribs) {
	this.writeStartElement(strElemName, attribs);
}

/**
 * Schreibt ein leeres XML Element
 * @param {String} strElemName Name des Elements
 * @param {Array} attribs Ein Array von XMLAttribute Elementen, wenn nicht gegeben werden keine Attribute geschrieben
 * @see {XMLAttribute}
 */
XMLWriter.prototype.writeEmptyElement = function(strElemName, attribs) {
	this.m_strSrc += '<' + strElemName;
	if (attribs) {
		for (var i = 0; i < attribs.length; i++) {
			this.m_strSrc += ' ' + attribs[i].getName() + '="' + attribs[i].getValue() + '"';
		}
	}
	this.m_strSrc += '/>';	
	this.m_nStartedElemsCount++;
}

/**
 * Schließt das Schreiben des Dokument ab
 */
XMLWriter.prototype.writeEndDocument = function() {
	if (!this.m_stack.length == 0) {
		for (var i=0; i<this.m_stack.length; i++) {
			this.writeEndElement();
		}
	}
	this.m_docWritten = true;
}

/**
 * Gibt das geschriebene Dokument als DOM Knoten zurück.
 * Die Verwendung dieser Funktion ist erst möglich wenn @see {XMLWriter.writeEndDocument} aufgerufen wurde
 * @return {Node} das Geschriebene Dokument als DOM Knoten, oder undefined, wenn das DOM nicht geschlossen wurde
 */
XMLWriter.prototype.getDocumentAsDOM = function() {
	var dom = undefined;
	if (this.m_docWritten === true) {
		var xml = bb.xml.parse(this.m_strSrc, false);
		dom = xml.documentElement;
	}
	return dom;
}

/**
 * Gibt das geschriebene Dokument als XML String zurück.
 * Die Verwendung dieser Funktion ist erst möglich wenn @see {XMLWriter.writeEndDocument} aufgerufen wurde
 * @return {String} das Geschriebene Dokument als XML String, oder undefined, wenn das DOM nicht geschlossen wurde
 */
XMLWriter.prototype.getDocumentAsString = function() {
	var str = undefined;
	if (this.m_docWritten === true) {
		str = this.m_strSrc;
	}
	return str;
}

