/*
 * Copyright 2016 Richard Cartwright
 *
 * 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.
 */

/*
 * $Log: AAFElement.java,v $
 * Revision 1.9  2011/07/27 17:07:21  vizigoth
 * Additions and alterations coincidence with editing the Reg-XML document part 1.
 *
 * Revision 1.8  2011/02/14 22:32:58  vizigoth
 * First commit after major sourceforge outage.
 *
 * Revision 1.7  2011/01/13 17:44:26  vizigoth
 * Major refactor of the industrial area and improved front-end documentation.
 *
 * Revision 1.6  2011/01/04 10:40:23  vizigoth
 * Refactor all package names to simpler forms more consistent with typical Java usage.
 *
 * Revision 1.5  2010/06/14 17:13:51  vizigoth
 * Improvements towards writing compliant Reg XML. Not yet complete.
 *
 * Revision 1.4  2009/05/14 16:15:34  vizigoth
 * Major refactor to remove dependency on JPA and introduce better interface and implementation separation. Removed all setPropertiesFromInterface and castFromInterface methods.
 *
 * Revision 1.3  2009/03/30 09:05:07  vizigoth
 * Refactor to use SMPTE harmonized names and add early KLV file support.
 *
 * Revision 1.2  2008/10/15 14:16:08  vizigoth
 * Documentation improved to an early release level.
 *
 * Revision 1.1  2007/11/13 22:14:57  vizigoth
 * Public release of MAJ API.
 *
 */

package tv.amwa.maj.io.xml;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import org.w3c.dom.Comment;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

import tv.amwa.maj.constant.CommonConstants;
import tv.amwa.maj.industry.Forge;
import tv.amwa.maj.industry.MediaEngine;
import tv.amwa.maj.industry.MetadataObject;
import tv.amwa.maj.meta.ClassDefinition;
import tv.amwa.maj.meta.MetaDefinition;
import tv.amwa.maj.meta.MetaDictionary;
import tv.amwa.maj.meta.PropertyDefinition;
import tv.amwa.maj.meta.TypeDefinition;
import tv.amwa.maj.meta.impl.MetaDictionaryImpl;
import tv.amwa.maj.model.Preface;

// TODO comments
// TODO tests

/**
 * <p>Representation of the root element of an AAF XML document "<code>&lt;AAF&gt;</code>".</p>
 * 
 * <p>DOCUMENTATION FOR THIS CLASS IS INCOMPLETE.</p>
 * 
 *
 *
 */

public class AAFElement 
	implements MetadataObject,
		XMLSerializable,
		CommonConstants {

	private Preface preface;
	private Element aafElement = null;
	
	public AAFElement(
			Preface preface) {
		
		this.preface = preface;
	}
	
	public final static String AAF_TAG = "AAF";
	public final static String AAF_VERSION = "1.1";
	public final static String EXTENSIONS_TAG = "Extensions";
	public final static String EXTENSION_TAG = "Extension";
	public final static String IDENTIFICATION_TAG = "Identification";
	public final static String SYMBOLSPACE_TAG = "Symbolspace";
	public final static String PREFERREDPREFIX_TAG = "PreferredPrefix";
	public final static String DESCRIPTION_TAG = "Description";
	public final static String DEFINITIONS_TAG = "Definitions";
	
	public void appendXMLChildren(
			Node parent) {

		Document document = parent.getOwnerDocument();
		Comment majComment = document.createComment("Generated by the Media Authoring with Java API (MAJ API)");
		parent.appendChild(majComment);
		aafElement = XMLBuilder.createChild(parent, AAF_XML_NAMESPACE, AAF_XML_PREFIX, AAF_TAG);
		XMLBuilder.setAttribute(aafElement, AAF_XML_NAMESPACE, AAF_XML_PREFIX, "version", AAF_VERSION);
		
		Element extensions = XMLBuilder.createChild(aafElement, AAF_XML_NAMESPACE, AAF_XML_PREFIX, EXTENSIONS_TAG);
		
		Map<String, List<MetaDefinition>> extensionMap = new HashMap<String, List<MetaDefinition>>();
		
		addExtensions(preface, extensionMap);
		
		for ( String namespace : extensionMap.keySet() ) {
			
			List<MetaDefinition> namespaceDefinitions = extensionMap.get(namespace);
			Element extension = XMLBuilder.createChild(extensions, AAF_XML_NAMESPACE, AAF_XML_PREFIX, EXTENSION_TAG);
			String likelyPrefix = "this";

			for ( MetaDefinition metaDefinition : namespaceDefinitions) {
				
				if (!(metaDefinition instanceof TypeDefinition))
					likelyPrefix = metaDefinition.getPrefix();
			}
			
			// TODO storing IDs and descriptions for namespace definitions
			XMLBuilder.appendElement(extension, AAF_XML_NAMESPACE, AAF_XML_PREFIX, IDENTIFICATION_TAG, Forge.randomAUID().toString()); 
			XMLBuilder.appendElement(extension, AAF_XML_NAMESPACE, AAF_XML_PREFIX, SYMBOLSPACE_TAG, namespace);
			XMLBuilder.appendElement(extension, AAF_XML_NAMESPACE, AAF_XML_PREFIX, PREFERREDPREFIX_TAG, likelyPrefix);
			XMLBuilder.appendElement(extension, AAF_XML_NAMESPACE, AAF_XML_PREFIX, DESCRIPTION_TAG, "Extensions for namespace " + namespace);
			Element definitions = XMLBuilder.createChild(extension, AAF_XML_NAMESPACE, AAF_XML_PREFIX, DEFINITIONS_TAG);
			
			for ( MetaDefinition metaDefinition : namespaceDefinitions) {
				
				metaDefinition.appendMetadictXML(definitions, AAF_XML_NAMESPACE, AAF_XML_PREFIX);
			}
		}
		
		// TODO generate valid dictionary or check the one that is provided
		
		XMLBuilder.appendObject(aafElement, preface);
	}
	
	private final static void addExtensions(
			Preface preface,
			Map<String, List<MetaDefinition>> extensionMap) {
		
		MetaDictionary metaDictionary = new MetaDictionaryImpl();
		metaDictionary.makeDynamic(preface);
		
		for ( ClassDefinition classDefinition : metaDictionary.getClassDefinitions() ) {
			addExtensionToMap(extensionMap, classDefinition, classDefinition.getNamespace());
			for ( PropertyDefinition propertyDefinition : classDefinition.getPropertyDefinitions() )
				addExtensionToMap(extensionMap, propertyDefinition, propertyDefinition.getNamespace());
		}
		
		for ( TypeDefinition typeDefinition : metaDictionary.getTypeDefinitions() ) {
			addExtensionToMap(extensionMap, typeDefinition, typeDefinition.getNamespace());
		}
	}
	
	private final static void addExtensionToMap(
			Map<String, List<MetaDefinition>> extensionMap,
			MetaDefinition candidateDefinition,
			String namespace) {
		
		if (!MediaEngine.isBaseline(candidateDefinition)) {
			if (namespace == null)
				namespace = candidateDefinition.getNamespace();
			if ((namespace == null) || (namespace.length() == 0))
				namespace = "this";
			List<MetaDefinition> namespaceDefinitions = null;
			
			if (!extensionMap.containsKey(namespace)) {
				namespaceDefinitions = new Vector<MetaDefinition>();
				extensionMap.put(namespace, namespaceDefinitions);
			}
			else {
				namespaceDefinitions = extensionMap.get(namespace);
			}
			namespaceDefinitions.add(candidateDefinition);
		}

	}
	
	public Document getDocument() {
		
		if (aafElement == null) return null;
		
		return aafElement.getOwnerDocument();
	}

	public String getComment() {

		return " SMPTE-2001 Reg-XML generated by the MAJ API. ";
	}
}
