package net.azib.ipscan.exporters;

import junit.framework.ComparisonFailure;
import net.azib.ipscan.config.Labels;
import net.azib.ipscan.fetchers.IPFetcher;
import net.azib.ipscan.fetchers.PortsFetcher;
import org.junit.Before;
import org.junit.Test;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;

import static org.junit.Assert.*;

/**
 * TestCase for Exporters.
 * It contains initialization and generic tests for any Exporter.
 *
 * @author Anton Keks
 */
public abstract class AbstractExporterTestCase {
	protected Exporter exporter;
	protected ByteArrayOutputStream outputStream;
	protected String outputContent;
	
	protected abstract Exporter createExporter();

	@Before
	public void setUp() throws Exception {
		outputStream = new ByteArrayOutputStream();
		exporter = createExporter();
	}

	@Test
	public void testLabel() {
		assertNotNull(Labels.getLabel(exporter.getId()));
	}

	@Test
	public void testFilenameExtension() {
		assertNotNull(exporter.getFilenameExtension());
	}
	
	@Test
	public void testStreamFlushAndClose() throws IOException {
		final var wasClosed = new boolean[] {false, false};
		var exporter2 = createExporter();
		var mockOutputStream = new OutputStream() {
			public void write(int b) throws IOException {
			}
			public void close() throws IOException {
				wasClosed[0] = true;
			}
			public void flush() {
				wasClosed[1] = true;
			}
		};
		exporter2.start(mockOutputStream, "feederstuff");
		// output something to ensure that the flush will be called
		exporter2.setFetchers(new String[] {Labels.getLabel("fetcher.ip"), Labels.getLabel("fetcher.ports")});
		exporter2.nextAddressResults(new Object[] {"1", "2"});
		// this should invoke flush among other things
		exporter2.end();
		// close: no
		assertFalse(wasClosed[0]);
		// flush: yes
		assertTrue(wasClosed[1]);
	}
	
	@Test
	public void testBasic() throws Exception {
		exporter.start(outputStream, "feederstuff");
		exporter.setFetchers(new String[] {"IP", "hello", "fetcher2"});
		exporter.nextAddressResults(new Object[] {InetAddress.getLocalHost().getHostAddress(), "world", 53});
		exporter.nextAddressResults(new Object[] {InetAddress.getLocalHost().getHostAddress(), "buga", -1});
		exporter.end();
		assertContains(InetAddress.getLocalHost().getHostAddress());
		assertContains("hello");
		assertContains("fetcher2");
		assertContains("world");
		assertContains("53");
		assertContains("buga");
		assertContains("-1");
	}
		
	@Test
	public void testFetchersWithoutAppend() throws IOException {
		exporter.start(outputStream, "feederstuff");
		exporter.setFetchers(new String[] {"IP", "fetcher1", "mega long fetcher 2"});
		exporter.end();
		assertContains("IP");
		assertContains("fetcher1");		
		assertContains("mega long fetcher 2");		
	}

	@Test
	public void testFetchersWithAppend() throws IOException {
		exporter.shouldAppendTo(null);
		exporter.start(outputStream, "feederstuff");
		exporter.setFetchers(new String[] {"IP", "fetcher1", "mega long fetcher 2"});
		exporter.end();
		assertNotContains("IP");
		assertNotContains("fetcher1");		
		assertNotContains("mega long fetcher 2");		
	}
	
	@Test
	public void testClone() throws CloneNotSupportedException {
		var exporter2 = (Exporter) exporter.clone();
		assertNotSame(exporter, exporter2);
	}
	
	@Test
	public void testNextAddressResultsWithNulls() throws IOException {
		exporter.start(outputStream, "feederstuff");
		exporter.setFetchers(new String[] {Labels.getLabel(IPFetcher.ID), "fetcher1", Labels.getLabel(PortsFetcher.ID)});
		exporter.nextAddressResults(new Object[] {InetAddress.getLocalHost(), null, null});
		exporter.end();
	}

	protected void assertContains(String string, boolean contains) throws IOException {
		if (outputContent == null) {
			outputStream.close();
			outputContent = outputStream.toString();
		}

		if (outputContent.contains(string) == !contains)
			throw new ComparisonFailure("Contains check failed", string, outputContent);
	}

	protected void assertContains(String string) throws IOException {
		assertContains(string, true);
	}

	protected void assertNotContains(String string) throws IOException {
		assertContains(string, false);
	}
}
