/**********************************************************************
 * Copyright (c) 2008 IBM Inc. and others
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 * IBM - Initial API and implementation
 **********************************************************************/

package org.eclipse.cosmos.internal.dr.drs.service.outputter;
	
import java.io.ByteArrayInputStream;
import java.io.PrintWriter;
import java.io.StringWriter;

import org.eclipse.cosmos.dc.provisional.cmdbf.services.common.CMDBfServicesUtil;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.transform.QueryOutputTransformer;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.transform.response.artifacts.IQueryResult;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.transform.response.artifacts.IQueryServiceElementCollection;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.transform.artifacts.IAdditionalRecordType;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.transform.artifacts.IGraphElement;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.transform.artifacts.IInstanceId;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.transform.artifacts.IRecord;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.transform.artifacts.IRecordMetadata;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.transform.artifacts.IRelationship;
import org.eclipse.cosmos.internal.dr.drs.service.handler.common.IOutputterConstants;
import org.eclipse.cosmos.internal.dr.drs.service.handler.common.JSONArray;
import org.eclipse.cosmos.internal.dr.drs.service.handler.common.JSONObject;
import org.eclipse.cosmos.provisional.dr.drs.service.handler.common.IParameters;


/**
 * The outputter class for the CMDBf query response.  This
 * class will convert a CMDBf query response to a JSON representation
 * consumable by DOJO widgets.
 * 
 * @author Ali Mehregani
 */
public class GraphResponseTreeOutputter extends AbstractGraphResponse 
{
	/**
	 * @see org.eclipse.cosmos.dr.drs.service.handler.sml.IOutputter#render(java.io.PrintWriter, java.util.Map)
	 */
	public void render(PrintWriter output, IParameters input) throws Exception 
	{
		String queryResponse = input.getParameter("queryResponse");
		String historyId = input.getParameter("historyId");
		String uuid = input.getParameter("uuid");
				
		if (historyId != null)
		{			
			queryResponse = super.loadFromHistory (uuid, historyId);			
		}
		
		// Convert the query response to JSON
		if (queryResponse != null)
		{
			IQueryResult queryResult = QueryOutputTransformer.transform(new ByteArrayInputStream(queryResponse.getBytes()));
			writeLogicalJSON(output, queryResult);
		}
	}

		
	private void writeLogicalJSON(PrintWriter output, IQueryResult response) 
	{
			JSONObject main = new JSONObject();
			main.put(IOutputterConstants.LABEL, IOutputterConstants.LABEL);
			
			JSONArray elementsArray = new JSONArray();
			convertTemplates(elementsArray, response, response.getNodesIds(), true);
			convertTemplates(elementsArray, response, response.getEdgesIds(), false);					
			main.put(IOutputterConstants.ITEMS, elementsArray);
			
			output.println(main.toString());

	}
	
	
	/**
	 * Traverse the graph response and append to a JSONArray
	 * representing all items and relationships
	 * 
	 * @param store The JSON Array used for storage
	 * @param response The query response
	 * @param elements The elements to be processed
	 * @param isItem Indicates if the element are items
	 * 
	 * @ In case of an unexpected error
	 */
	private void convertTemplates(JSONArray store, IQueryResult response, String[] templateIds, boolean isItem)  
	{		
		// For each element		
		String type = isItem ? IOutputterConstants.ITEM_TEMPLATE : IOutputterConstants.RELATIONSHIP_TEMPLATE;
		for (int i = 0; i < templateIds.length; i++)
		{
			JSONObject templateObject = new JSONObject();
			templateObject.put(IOutputterConstants.TYPE, type);
			templateObject.put(IOutputterConstants.LABEL, templateIds[i]);
			templateObject.put(IOutputterConstants.ELEMENTS, 
					convertElements(isItem ? 
							response.getNodes(templateIds[i]) : 
							response.getEdges(templateIds[i]), isItem));
			store.add(templateObject);
		}
	}
	
	
	private JSONArray convertElements(IQueryServiceElementCollection elementCollection, boolean isItem) 
	{
		JSONArray elementsArray = new JSONArray();
		IGraphElement[] elements = elementCollection.getElements();
		String type = isItem ? IOutputterConstants.ITEM : IOutputterConstants.RELATIONSHIP;
		
		for (int i = 0; i < elements.length; i++)
		{
			JSONObject elementObject = new JSONObject();
			elementObject.put(IOutputterConstants.TYPE, type);
			elementObject.put(IOutputterConstants.LABEL, getFirstInstanceId(elements[i].getInstanceIds()));		
			
			if (!isItem)
			{
				IRelationship relationship = (IRelationship)elements[i];
				addJSONEntries(elementObject, new String[][]{
						new String[]{IOutputterConstants.SOURCE, getFirstInstanceId(new IInstanceId[]{relationship.getSourceId()})},
						new String[]{IOutputterConstants.TARGET, getFirstInstanceId(new IInstanceId[]{relationship.getTargetId()})}
				});				
			}
			
			elementObject.put(IOutputterConstants.INSTANCE_ID, convertInstanceIds(elements[i]));
			elementObject.put(IOutputterConstants.ADDITIONAL_RECORDS, convertAdditionalRecords(elements[i]));
			elementObject.put(IOutputterConstants.RECORDS, convertRecords(elements[i]));
			elementsArray.add(elementObject);
		}
		
		return elementsArray;
	}

	private JSONArray convertInstanceIds(IGraphElement graphElement) 
	{
		IInstanceId[] instanceIds = graphElement.getInstanceIds();
		JSONArray instanceIdArray = new JSONArray();
		for (int i = 0; i < instanceIds.length; i++)
		{
			JSONObject instanceIdObject = new JSONObject();			
			instanceIdObject.put(IOutputterConstants.MDR_ID, CMDBfServicesUtil.toString(instanceIds[i].getMdrId()));
			instanceIdObject.put(IOutputterConstants.LOCAL_ID, CMDBfServicesUtil.toString(instanceIds[i].getLocalId()));
			instanceIdArray.add(instanceIdObject);
		}
		
		return instanceIdArray;
	}

	private JSONArray convertAdditionalRecords(IGraphElement graphElement) 
	{
		IAdditionalRecordType[] additionalRecords = graphElement.getAdditionalRecordTypes();
		JSONArray additionalRecordsArray = new JSONArray();
		for (int i = 0; i < additionalRecords.length; i++)
		{
			JSONObject additionalRecordsObject = new JSONObject();			
			additionalRecordsObject.put(IOutputterConstants.NAMESPACE, CMDBfServicesUtil.toString(additionalRecords[i].getNamespace()));
			additionalRecordsObject.put(IOutputterConstants.LOCAL_NAME, additionalRecords[i].getLocalName());
			additionalRecordsArray.add(additionalRecordsObject);
		}
		
		return additionalRecordsArray;	
	}

	private JSONArray convertRecords(IGraphElement graphElement) 
	{
		IRecord[] records = graphElement.getRecords();
		JSONArray recordsArray = new JSONArray();
		for (int i = 0; i < records.length; i++)
		{
			IRecordMetadata recordMetaData = records[i].getRecordMetadata();
			JSONObject recordsObject = new JSONObject();			
			
			StringWriter sw = new StringWriter();
			records[i].getValue().toXML(sw, 0);
			String value = sw.getBuffer().toString();
			
			addJSONEntries(recordsObject, new String[][]{
					new String[]{IOutputterConstants.TYPE, IOutputterConstants.RECORD},
					new String[]{IOutputterConstants.LABEL, CMDBfServicesUtil.toString(recordMetaData.getRecordId())},
					new String[]{IOutputterConstants.LAST_MODIFIED, recordMetaData.getLastModified()},
					new String[]{IOutputterConstants.SNAP_ID, recordMetaData.getSnapshotId()},
					new String[]{IOutputterConstants.BASE_ID, recordMetaData.getBaselineId()},
					new String[]{IOutputterConstants.VALUE, value}
			});
			
									
			recordsArray.add(recordsObject);
		}
		
		return recordsArray;	
	}

	
	private String getFirstInstanceId(IInstanceId[] instanceIds)
	{
		String localId = null;
		return 	instanceIds.length > 0 && 
				instanceIds[0] != null &&
				(localId = CMDBfServicesUtil.toString(instanceIds[0].getLocalId())) != null && 
				localId.length() > 0 ?
				
						localId : IOutputterConstants.UNKNOWN;
	}
	
	private void addJSONEntries(JSONObject jsonObject, String[][] values) 
	{
		for (int i = 0; i < values.length; i++)
		{
			if (values[i][1] != null && values[i][1].length() > 0)
			{
				jsonObject.put(values[i][0], values[i][1]);
			}
		}
	}
	
	
	// Used for debugging purposes only 
	public static void main(String[] args) throws Exception
	{
		GraphResponseTreeOutputter outputter = new GraphResponseTreeOutputter();
		IQueryResult result = QueryOutputTransformer.transform(outputter.getClass().getClassLoader().getResourceAsStream("Staff02Students.xml"));
		StringWriter sw = new StringWriter();
		PrintWriter pw = new PrintWriter(sw);
		
		outputter.writeLogicalJSON(pw, result);
		System.out.println(sw.getBuffer());
	}
}