/*******************************************************************************
 * Copyright (c) 2007,2008 CA, 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:
 *     CA - initial API and implementation
 *     IBM - defects and enhancements
 *******************************************************************************/
package org.eclipse.cosmos.internal.dr.drs.service.outputter;


import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URI;
import java.util.ArrayList;

import javax.xml.namespace.QName;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.eclipse.cosmos.dc.provisional.cmdbf.exception.CMDBfException;
import org.eclipse.cosmos.dc.provisional.cmdbf.registration.client.RegistrationServiceClient;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.common.CMDBfServicesUtil;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.common.ICMDBfServicesConstants;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.deregistration.transform.input.artifacts.DeregisterInputArtifactFactory;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.deregistration.transform.input.artifacts.IDeregisterRequest;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.transform.IQueryTransformerConstants;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.transform.QueryOutputTransformer;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.transform.response.artifacts.IEdges;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.transform.response.artifacts.INodes;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.query.transform.response.artifacts.IQueryResult;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.registration.transform.input.artifacts.IRegisterRequest;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.registration.transform.input.artifacts.RegisterInputArtifactFactory;
import org.eclipse.cosmos.dc.provisional.cmdbf.services.registration.transform.output.artifacts.IResponse;
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.IItem;
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.internal.dr.drs.service.handler.common.XMLUtil;
import org.eclipse.cosmos.provisional.dr.drs.service.handler.common.AbstractOutputter;
import org.eclipse.cosmos.provisional.dr.drs.service.handler.common.IParameters;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;


public class RegistrationOutputter extends AbstractOutputter {

	//
	// TODO temporary 
	//
	private Element cleanseResponse(Element queryResponse) throws Exception
	{
		Element cleansedRoot = XMLUtil.createElement(new QName(ICMDBfServicesConstants.CMDBF_MODEL_NAMESPACE, IQueryTransformerConstants.QUERY_RESULT_ELEMENT, ICMDBfServicesConstants.CMDBF_PREFIX_RAW));
		Document document = cleansedRoot.getOwnerDocument();

		NodeList children = queryResponse.getChildNodes();
		int counter = 0;
		for (int i = 0, childCount = children.getLength(); i < childCount; i++)
		{
			Node child = children.item(counter);			
			Node adoptedNode = document.adoptNode(child);
			if (adoptedNode == null)
			{
				adoptedNode = document.importNode(child, true);
				counter++;
			}
			cleansedRoot.appendChild(adoptedNode);
		}		
				
		return cleansedRoot;
	}

	protected InputStream toInputStream(Node rootNode) throws CMDBfException
	{
		try
		{
			// Turn node to string
	        Transformer trans = TransformerFactory.newInstance().newTransformer();
	        StringWriter sw = new StringWriter();
	        trans.transform(new DOMSource(rootNode), new StreamResult(sw));
	
	        // Turn String into input stream
	        InputStream inputStream = new ByteArrayInputStream(sw.toString().getBytes("UTF-8")); //$NON-NLS-1$
	        return inputStream;
		}
		catch (Exception e)
		{
			throw new CMDBfException(Messages.getString("RegistrationOutputter.OPENSTREAM_ERR")); //$NON-NLS-1$
		}
	}

	
	private boolean selectedLocalId( String localIdList[], String localId ){
		for ( String id: localIdList ){
			if ( id.equalsIgnoreCase( localId ) )
				return true;
		}
		
		return false;
	}
	
	
	public void render(PrintWriter output, IParameters input) throws Exception {

		// get parameters...
		String cmd = input.getParameter("cmd"); //$NON-NLS-1$
		String cmdbfepr = input.getParameter("cmdbfEPR");  //$NON-NLS-1$
		String queryResp = input.getParameter("queryResp");  //$NON-NLS-1$
		String username = input.getParameter("username"); //$NON-NLS-1$
		String password = input.getParameter("password"); //$NON-NLS-1$
		String soapversion = input.getParameter("soapversion"); //$NON-NLS-1$
	
		Document document = XMLUtil.createDocument(queryResp);
		Element queryRespElem = document.getDocumentElement();
		
		// TODO fixme!!
		InputStream inputStream = toInputStream(cleanseResponse(queryRespElem));
		
        // transform the response to POJOs
		IQueryResult queryResult = QueryOutputTransformer.transform(inputStream);

		// process command
		if ( cmd.equalsIgnoreCase("query")){ //$NON-NLS-1$

			//
			// query request
			//
			
     		output.print( "[ " ); //$NON-NLS-1$

     		// convert the items (nodes) to JSON
     		INodes[] nodes = queryResult.getAllNodes();
     		int count = 0;
     		for (INodes inode: nodes){
     			IGraphElement[] graphElements = inode.getElements();
     			
     			for (IGraphElement graphElement: graphElements ){
     				IInstanceId[] ids = graphElement.getInstanceIds();
     				
     				for (IInstanceId id: ids){
     					if ( count > 0 ){
     						output.print(", "); //$NON-NLS-1$
     					}
 						++count;
    					output.print("[ \"Node\", '" + id.getLocalId() + "' ]"); //$NON-NLS-1$ //$NON-NLS-2$
     				}
     			}
     		}

     		// convert the relationships (edges) to JSON
     		IEdges[] edges = queryResult.getAllEdges();
    		for (IEdges edge: edges){
     			IGraphElement[] graphElements = edge.getElements();
     			
     			for (IGraphElement graphElement: graphElements ){
     				IInstanceId[] ids = graphElement.getInstanceIds();
     				
     				for (IInstanceId id: ids){
     					if ( count > 0 ){
     						output.print(", "); //$NON-NLS-1$
     					}
 						++count;
    					output.print("[ \"Edge\", '" + id.getLocalId() + "' ]"); //$NON-NLS-1$ //$NON-NLS-2$
     				}
     			}
     		}
    		
    		output.print( "]" ); //$NON-NLS-1$
    		
		} else if ( cmd.equalsIgnoreCase("register") || cmd.equalsIgnoreCase("deregister") ){ //$NON-NLS-1$ //$NON-NLS-2$
			boolean bRegister = cmd.equalsIgnoreCase("register") ? true : false ; //$NON-NLS-1$
			URI mdrId = new URI(""); //$NON-NLS-1$
			//
			// register/deregister request
			//

			// get localId parameters...
			ArrayList<String> ciliststr = new ArrayList<String>();
			for(Integer i = 0;; i++) {
				String ciParm = input.getParameter( "localid" + i.toString() ); //$NON-NLS-1$
				if(ciParm == null)
					break;

				ciliststr.add( ciParm );
			}
			String localIdList[] = ciliststr.toArray(new String[ciliststr.size()]);
			
			// construct the register/deregister request
			IRegisterRequest registerRequest = RegisterInputArtifactFactory.getInstance().createRegisterRequest();			
			IDeregisterRequest deregisterRequest = DeregisterInputArtifactFactory.getInstance().createDeregisterRequest();

     		// add the items (nodes) 
     		INodes[] nodes = queryResult.getAllNodes();
     		for (INodes inode: nodes){
     			IGraphElement[] graphElements = inode.getElements();
     			
     			for (IGraphElement graphElement: graphElements ){
     				boolean bFound = false;
     				
     				IInstanceId[] ids = graphElement.getInstanceIds();
     				for (IInstanceId id: ids){
     					String localId = CMDBfServicesUtil.toString(id.getLocalId());
     					
     					if ( selectedLocalId( localIdList, localId ) ){
         					mdrId = id.getMdrId();
     						bFound = true;
     					}
      				}
     				
     				if ( bFound ){
     					if ( bRegister )
     						registerRequest.addItem( (IItem) graphElement );
     					else
     						deregisterRequest.addItem( (IItem) graphElement );
     				}
     			}
     		}
     		
     		// add the relationships (edges) 
     		IEdges[] edges = queryResult.getAllEdges();
    		for (IEdges edge: edges){
     			IGraphElement[] graphElements = edge.getElements();
     			
     			for (IGraphElement graphElement: graphElements ){
     				boolean bFound = false;
     				
     				IInstanceId[] ids = graphElement.getInstanceIds();
     				for (IInstanceId id: ids){
     					String localId = CMDBfServicesUtil.toString(id.getLocalId());
     					
     					if ( selectedLocalId( localIdList, localId ) ){
         					mdrId = id.getMdrId();     						
     						bFound = true;
     					}
      				}
     				
     				if ( bFound ){
     					if ( bRegister )
     						registerRequest.addRelationship( (IRelationship) graphElement );
     					else
     						deregisterRequest.addRelationship( (IRelationship) graphElement );
     				}

     			}
     		}
			
			// Set a dummy mdr id
			registerRequest.setMdrId(mdrId);
			deregisterRequest.setMdrId(mdrId);
			
			// get Federating CMDB client using EPR
			RegistrationServiceClient client = new RegistrationServiceClient(cmdbfepr);
			if (username != null)
				client.setUsername(username);
			if (password != null)
				client.setPassword(password);
			if (soapversion != null)
				client.setSoapVersion(Integer.parseInt(soapversion));

			// Register/deregister items
			IResponse result = bRegister ? client.register( registerRequest ) : client.deregister( deregisterRequest );		
			
     		// convert the result to JSON
			JSONObject root = StatusResponseOutputter.renderStatusResult(null, result);
			
			if (!root.containsKey(IOutputterConstants.ITEMS))
			{
				root.put(IOutputterConstants.ITEMS, new JSONArray());
			}
						
			output.write(root.toString());
		}
	}
	
}
