package test;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URL;
import java.util.regex.Pattern;

import javax.xml.namespace.QName;

import junit.framework.Assert;

import org.junit.Test;

import com.ebmwebsourcing.easybox.api.XmlContext;
import com.ebmwebsourcing.easybox.api.XmlContextFactory;
import com.ebmwebsourcing.easybox.api.XmlObject;
import com.ebmwebsourcing.easybox.api.XmlObjectReader;
import com.ebmwebsourcing.easybox.api.XmlObjectWriter;
import com.ebmwebsourcing.easywsdl11.api.element.Definitions;
import com.ebmwebsourcing.easywsdl11.api.element.Port;
import com.ebmwebsourcing.easywsdl11.api.element.Service;

/**
 * A simple JUnit test to check some assertions on updated WSDL.
 */
public class WsdlWriterTestCase {


	@Test
	public void testNoNewNamespaceDeclarationOnSimpleWsdlUpdate() throws Exception {
		
		final String REPLACEMENT = "Renamed123";
		final String NS = "http://tuxdroid.ebmwebsourcing.com/";
		
		
		// Get the WSDL file to update
		URL url = getClass().getResource( "/tuxDroid.wsdl" );
		String content = readResourceContent( url.toURI());
		int nsCount = content.split( Pattern.quote( NS )).length;
		Assert.assertFalse( content.contains( REPLACEMENT ));
		

		// Create a generic reader: cannot be static (not thread safe)
		XmlContextFactory xmlContextFactory = new XmlContextFactory();
		XmlContext xmlContext = xmlContextFactory.newContext();
		XmlObjectReader reader = xmlContext.createReader();

		
		// Define the qualified name of the SOAP ports
		QName soap11 = new QName( "http://schemas.xmlsoap.org/wsdl/soap/", "address" );
		QName soap12 = new QName( "http://schemas.xmlsoap.org/wsdl/soap12/", "address" );
		boolean updated = false;
		
		
		// Update the WSDL
		Definitions def = reader.readDocument( url, Definitions.class );
		Service service = def.getServiceByName( "TuxDroid" );
		if( service != null ) {

			for( Port port : service.getPorts()) {
				XmlObject[] soapPorts = port.getAnyXmlObjects( soap11 );
				if( soapPorts == null || soapPorts.length == 0 )
					soapPorts = port.getAnyXmlObjects( soap12 );

				if( soapPorts != null && soapPorts.length == 1 ) {
					port.setName( REPLACEMENT );
					updated = true;
					break;
				}
			}
		}


		// There must be a renamed port
		Assert.assertTrue( updated );
		File outputFile = null;
		try {
			
			// Write it
			XmlObjectWriter writer = xmlContext.createWriter();
			outputFile = new File( System.getProperty( "java.io.tmpdir" ), "tmp.wsdl" );
			FileOutputStream os = new FileOutputStream( outputFile );
			writer.writeDocument( def, os );
			os.close();
	
			// Load it and check its content
			content = readResourceContent( outputFile.toURI());
			Assert.assertTrue( content.contains( REPLACEMENT ));
			
			// No new name space declaration
			int newNsCount = content.split( Pattern.quote( NS )).length;
			System.out.println( "Expected: " + nsCount + "; Found: " + newNsCount + ";" );	// 5; 71; on last run
			Assert.assertEquals( nsCount, newNsCount );
			
		} finally {
		
			// Delete the file
			if( outputFile != null && outputFile.exists())
				Assert.assertTrue( outputFile.delete());
		}
	}
	
	
	/**
	 * Reads the content of a resource and returns it as a string.
	 * <p>
	 * The content is tried to be read with UTF-8 encoding.
	 * If it fails, the default system encoding is used.
	 * </p>
	 *
	 * @param uri the URI of the resource whose content must be loaded
	 * @return the resource's content
	 * @throws IOException if the resource's content could not be read
	 */
	public static String readResourceContent( URI uri ) throws IOException {

		String result = null;
		ByteArrayOutputStream os = new ByteArrayOutputStream();
		InputStream is = null;
		try {
			is = uri.toURL().openStream();
			copyStream( is, os );

		} finally {
			if( is != null )
				is.close();
		}

		try {
			result = os.toString( "UTF-8" );
		} catch( UnsupportedEncodingException e ) {
			result = os.toString();
		}

		return result;
	}
	
	
	/**
	 * Copies the content from in into os.
	 * <p>
	 * Neither <i>in</i> nor <i>os</i> are closed by this method.<br />
	 * They must be explicitly closed after this method is called.
	 * </p>
	 *
	 * @param in
	 * @param os
	 * @throws IOException
	 */
	private static void copyStream( InputStream in, OutputStream os ) throws IOException {

		byte[] buf = new byte[ 1024 ];
		int len;
		while((len = in.read( buf )) > 0) {
			os.write( buf, 0, len );
		}
	}
}
