package com.ibm.emb.test;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;

import javax.emb.ContentTooLargeException;
import javax.emb.FormatFeatureException;
import javax.emb.FormatSyntaxException;
import javax.emb.GenericMediaFormat;
import javax.emb.LinkTranslationException;
import javax.emb.Media;
import javax.emb.MediaException;
import javax.emb.MediaFormat;
import javax.emb.MediaFormatRegistry;
import javax.emb.MediaHeader;
import javax.emb.MediaSegment;

import junit.framework.Test;
import junit.framework.TestSuite;

import org.objectweb.jonas.emb.mfb.formats.image.JpegFormat;

import com.ibm.emb.junit.EMBStaticHelper;
import com.ibm.emb.junit.EMBTestCaseBase;

/**
 * <pre>
 *
 *   Line Item: MFB API
 *   Subcategory 1: javax.emb
 *   Subcategory 2: MediaFormat
 *
 *   This abstract class provides test methods for all methods described in the interface
 *   MediaFormat.
 *   For testing implementations of this interface one needs to create a Tester
 *   class which extends this abstract class.
 *
 * </pre>
 */
public abstract class MediaFormatTest extends EMBTestCaseBase {

    // constructor
    public MediaFormatTest(String name) throws MediaException {
        super(name);
    }

    /**
     * stdSuite method of this test class instantiates all tests necesssary to
     * for the standard compliance tests
     * @param String complete class Name of the implementor of the abstract test
     */
    public static Test stdSuite(String TestCaseImplementor) {
        TestSuite suite = new TestSuite(TestCaseImplementor);
        try {
            Class implementorClass = Class.forName(TestCaseImplementor);
            java.lang.reflect.Constructor implementorConstructor = implementorClass
                    .getDeclaredConstructor(new Class[] {String.class});
            suite.addTest((Test) implementorConstructor.newInstance(new String[] {"testEMB013"}));
            suite.addTest((Test) implementorConstructor.newInstance(new String[] {"testEMB014"}));
            suite.addTest((Test) implementorConstructor.newInstance(new String[] {"testEMB015"}));
            suite.addTest((Test) implementorConstructor.newInstance(new String[] {"testEMB016"}));
            suite.addTest((Test) implementorConstructor.newInstance(new String[] {"testEMB017"}));
            suite.addTest((Test) implementorConstructor.newInstance(new String[] {"testEMB018"}));
            suite.addTest((Test) implementorConstructor.newInstance(new String[] {"testEMB019"}));
        } catch (java.lang.reflect.InvocationTargetException ex) {
            String msg = "error dynamically creating " + TestCaseImplementor + " " + ex.toString();
            EMBStaticHelper.testTrace(msg);
            throw new RuntimeException(msg);
        } catch (ClassNotFoundException ex) {
            String msg = "Class not found " + TestCaseImplementor + " " + ex.toString();
            EMBStaticHelper.testTrace(msg);
            throw new RuntimeException(msg);
        } catch (NoSuchMethodException ex) {
            String msg = "No such method in " + TestCaseImplementor + " " + ex.toString();
            EMBStaticHelper.testTrace(msg);
            throw new RuntimeException(msg);
        } catch (InstantiationException ex) {
            String msg = TestCaseImplementor + " " + ex.toString();
            EMBStaticHelper.testTrace(msg);
            throw new RuntimeException(msg);
        } catch (IllegalAccessException ex) {
            String msg = TestCaseImplementor + " " + ex.toString();
            EMBStaticHelper.testTrace(msg);
            throw new RuntimeException(msg);
        }
        return suite;
    }

    /**
     * abstract method to create an instance of MediaFormat this method needs to
     * be implemented in the test subclass and will create an instance of the
     * concrete implementation of the class under test
     */
    public abstract MediaFormat createMediaFormat();

    /**
     * <pre>
     *
     *  Testcase Name: assembleContent(URL, MediaSegment[])
     *  Testcase Number: EMB013
     *
     *  setup:
     *
     *  test procedure:
     *  1.call assembleContent with null as segment array and null as location
     *    expected result: NullPointerException
     *
     *  2.call assembleContent with empty segment array and null as location
     *    expected result: empty byte array result
     *
     *  3.create 1 element segment array containing a null element
     *    call assembleContent with the segment array created and null as location
     *    expected result: NullPointerException
     *
     *  4.create 1 element segment array, fill in random content and null as child location
     *    call assembleContent with the segment array created and null as location
     *    expected result: content equal to the random content passed with the segment array
     *
     *  5.create several MediaSegments (combined size=2GB) and store in MediaSegment array
     *    call assembleContent
     *    expected result: ContentTooLargeException
     *
     *  6.create 1 element segment array with random content and invalid childLocation
     *    call assembleContent with segment array and null location
     *    expected result: if (isEmbedded) -&gt; FormatSyntaxException
     *  					  if (!isEmbedded) -&gt; LinkTranslationException
     *
     *  7.crate 2 element segemnt array with random content and null childLocation
     *    call assembleContent
     *    expected result: if (isEmbedded) -&gt; FormatSyntaxException
     *  					  if (!isEmbedded) -&gt; byte array not null
     *
     *  8.create 1 element array with random content and random childLocation
     *    call assembleContent
     *    expected result: if (isEmbedded) -&gt; FormatSyntaxException
     *  					  if (!isEmbedded) -&gt;byte array not null
     *
     * </pre>
     */
    public void testEMB013() throws MediaException, IOException {

        // media segment
        MediaSegment mediaSegment1 = null;
        MediaSegment mediaSegment2 = null;
        MediaSegment[] testMediaSegmentArray = null;
        // test instance
        MediaFormat testInstance = createMediaFormat();
        byte[] testByteArray1 = null;
        byte[] testByteArray2 = null;
        //
        // test 1
        //
        try {
            testInstance.assembleContent(null, null);
            fail("test1: Should throw a NullPointerException");
        } catch (NullPointerException e) {
            testTrace("test 1 passed");
        } catch (Exception e) {
            fail("test 1: Should throw a NullPointerException but threw " + e.toString());
        }
        //
        // test 2
        //
        testMediaSegmentArray = new MediaSegment[0];
        if (this instanceof com.ibm.emb.test.mfb.GenericPlayListTest) {
            logProductError("PEMB0067STZ reported only for GenericPlaylistFormat;  ");
        }
        testByteArray1 = testInstance.assembleContent(null, testMediaSegmentArray);
        assertEquals("test 2", 0, testByteArray1.length);
        testTrace("test 2 passed");
        //
        // test 3
        //
        testMediaSegmentArray = new MediaSegment[] {null};
        try {
            testInstance.assembleContent(null, testMediaSegmentArray);
            fail("test3: Should throw a NullPointerException");
        } catch (NullPointerException e) {
            testTrace("test 3 passed");
        } catch (Exception e) {
            fail("test 3: Should throw a NullPointerException but throwed " + e.toString());
        }
        //
        // test 4
        //
        testInstance = createMediaFormat();

        testByteArray1 = EMBStaticHelper.createRandomByteArray(1024);
        mediaSegment1 = new MediaSegment();
        mediaSegment1.setContent(testByteArray1);
        testMediaSegmentArray = new MediaSegment[] {mediaSegment1};
        if (this instanceof com.ibm.emb.test.mfb.GenericPlayListTest) {
            logProductError("PEMB0067STZ reported only for GenericPlaylistFormat;  ");
        }
        testByteArray2 = testInstance.assembleContent(null, testMediaSegmentArray);
        assertTrue("test4", java.util.Arrays.equals(testByteArray1, testByteArray2));
        testTrace("test 4 passed");
        //
        // test 5
        //
        if (embTestConfig.getIncludeLongRunningTest()) {
            int maxSize = Integer.parseInt(embTestConfig.getMaxMediaSize());
            if ((maxSize == -1) || (maxSize > Integer.MAX_VALUE)) {
                GenericMediaFormat testMedia = new GenericMediaFormat();
                testByteArray1 = EMBStaticHelper.createRandomByteArray(Integer
                        .parseInt(embTestConfig.getMaxMediaSize()) + 1);
                mediaSegment1 = new MediaSegment();
                mediaSegment1.setContent(testByteArray1);
                testMediaSegmentArray = new MediaSegment[] {mediaSegment1};
                try {
                    testMedia.assembleContent(null, testMediaSegmentArray);
                    fail("test 5: Should throw a ContentTooLargeException");
                } catch (ContentTooLargeException e) {
                    testTrace("test 5 passed");
                } catch (Exception e) {
                    fail("test 5: Should throw a ContentTooLargeException but threw " + e.toString());
                }
            }
        }
        //
        // test 6
        //
        testInstance = createMediaFormat();
        testByteArray1 = EMBStaticHelper.createRandomByteArray(1024);
        mediaSegment1 = new MediaSegment();
        mediaSegment1.setContent(testByteArray1);
        try {
            mediaSegment1
                    .setChildLocation(new URL("file://" + embTestConfig.getMediaDir() + File.separatorChar + "childTestFile.test"));
        } catch (MalformedURLException e) {
            System.out.println("setChildLocation called with malformed URL");
        }
        testMediaSegmentArray = new MediaSegment[] {mediaSegment1};
        if (testInstance.isEmbedded()) {
            try {
                testInstance.assembleContent(null, testMediaSegmentArray);
                fail("test 6: Should throw a FormatSyntaxException");
            } catch (FormatSyntaxException e) {
                testTrace("test 6 passed");
            } catch (Exception e) {
                fail("test 6: Should throw a FormatSyntaxException but threw " + e.toString());
            }
        } else {
            try {
                testInstance.assembleContent(null, testMediaSegmentArray);
                fail("test 6: Should throw a LinkTranslationException");
            } catch (LinkTranslationException e) {
                testTrace("test 6 passed");
            } catch (Exception e) {
                fail("test 6: Should throw a LinkTranslationException but threw " + e.toString());
            }
        }
        //
        // test 7
        //
        testInstance = createMediaFormat();
        testByteArray1 = EMBStaticHelper.createRandomByteArray(1024);
        testByteArray2 = EMBStaticHelper.createRandomByteArray(1024);
        mediaSegment1 = new MediaSegment();
        mediaSegment2 = new MediaSegment();
        mediaSegment1.setContent(testByteArray1);
        mediaSegment2.setContent(testByteArray2);
        testMediaSegmentArray = new MediaSegment[] {mediaSegment1, mediaSegment2};
        if (testInstance.isEmbedded()) {
            try {
                testInstance.assembleContent(null, testMediaSegmentArray);
                fail("test 7: Should throw a FormatSyntaxException");
            } catch (FormatSyntaxException e) {
                testTrace("test 7 passed");
            } catch (Exception e) {
                fail("test 7: Should throw a FormatSyntaxException but threw " + e.toString());
            }
        } else {
            testByteArray1 = testInstance.assembleContent(null, testMediaSegmentArray);
            assertNotNull("test 7: ", testByteArray1);
        }
        //
        // test 8
        //
        testInstance = createMediaFormat();
        testByteArray1 = EMBStaticHelper.createRandomByteArray(1024);
        testByteArray2 = EMBStaticHelper.createRandomByteArray(1024);
        String childMediaPath = embTestConfig.getMediaDir() + File.separatorChar + "Generated" + File.separatorChar + "childMedia.test";
        File childMedia = new File(childMediaPath);
        childMedia.createNewFile();
        FileOutputStream childMediaOutputStream = new FileOutputStream(childMedia);
        childMediaOutputStream.write(testByteArray2);
        childMediaOutputStream.close();
        mediaSegment1 = new MediaSegment();
        mediaSegment1.setContent(testByteArray1);
        mediaSegment1.setChildLocation(new URL("file:///" + childMediaPath));

        if (testInstance.isEmbedded()) {
            try {
                testInstance.assembleContent(null, testMediaSegmentArray);
                fail("test 8: Should throw a FormatSyntaxException");
            } catch (FormatSyntaxException e) {
                testTrace("test 8 passed");
            } catch (Exception e) {
                fail("test 8: Should throw a FormatSyntaxException but threw " + e.toString());
            }
        } else {
            testByteArray1 = testInstance.assembleContent(null, testMediaSegmentArray);
            assertNotNull("test 8: ", testByteArray1);
        }

        succeed();
    }

    /**
     * <pre>
     *
     *  Testcase Name: disassembleContent(URL, byte[])
     *  Testcase Number: EMB014
     *
     *  setup:
     *
     *  test procedure:
     *  1.call disassembleContent with null as mediaContent and null as location
     *    expected result: NullPointerException
     *
     *  2.create byte array with random content, call disassembleContent with null location
     *    expected result:  if (isEmbedded)
     *  						-&gt; 1 element segment array OR
     *  						-&gt; FormatSyntaxException
     *  					   if (!isEmbedded)
     *  						-&gt; multiple element segment array OR
     *  						-&gt; FormatSyntaxException OR
     *  						-&gt; LinkTranslationException
     *
     *  3.retrieve property file for nonembedded media
     *    use sample media file and call disassembleContent
     *    call assembleContent on returned media segment array
     *    expected result: returned content should be same format as original
     *  	  				  and roughly the same size
     *
     * </pre>
     */
    public void testEMB014() throws MediaException, IOException, ClassNotFoundException, IllegalAccessException,
            InstantiationException {

        // test instance
        MediaFormat testInstance = createMediaFormat();
        //
        // test 1
        //
        try {
            testInstance.disassembleContent(null, null);
            fail("test1: Should throw a NullPointerException");
        } catch (NullPointerException e) {
            testTrace("test 1 passed");
        } catch (Exception e) {
            fail("test 1: Should throw a NullPointerException but threw " + e.toString());
        }
        //
        // test 2
        //
        byte[] testByteArray = EMBStaticHelper.createRandomByteArray(1024);
        if (testInstance.isEmbedded()) {
            try {
                MediaSegment[] testSegmentArray = testInstance.disassembleContent(null, testByteArray);
                assertEquals("test 2: ", 1, testSegmentArray.length);
                testTrace("test 2 passed");
            } catch (FormatSyntaxException e) {
                testTrace("test 2 passed: FormatSyntaxException");
            } catch (Exception e) {
                fail("test 2: Should pass or throw FormatSyntaxException but threw " + e.toString());
            }
        } else {
            try {
                MediaSegment[] testSegmentArray = testInstance.disassembleContent(null, testByteArray);
                assertTrue("test 2: ", testSegmentArray.length > 1);
                testTrace("test 2 passed");
            } catch (FormatSyntaxException e) {
                testTrace("test 2 passed: FormatSyntaxException");
            } catch (LinkTranslationException e) {
                testTrace("test 2 passed: LinkTranslationException");
            } catch (Exception e) {
                fail("test 2: Should pass, throw FormatSyntaxException, or throw LinkTranslationException but threw "
                        + e.toString());
            }
        }
        //
        // test 3
        //
        // retrieve non embedded media information
        String fullClassName = embTestConfig.getNonEmbeddedMediaClassName();
        String fileExtension = embTestConfig.getNonEmbeddedMediaFileExt();
        String nonEmbeddedMediaFile = embTestConfig.getNonEmbeddedMediaFile();

        // register non embedded media format
        javax.emb.MediaFormatRegistry formatRegistry = MediaFormatRegistry.SINGLETON;
        formatRegistry.rebind(fileExtension, (MediaFormat) Class.forName(fullClassName).newInstance());
        testInstance = (MediaFormat) Class.forName(fullClassName).newInstance();

        String mediaFullPath = embTestConfig.getMediaDir() + File.separatorChar + nonEmbeddedMediaFile;
        File mediaFile = new File(mediaFullPath);
        FileInputStream mediaFileInputStream = new FileInputStream(mediaFile);
        byte[] mediaArray = new byte[(int) mediaFile.length()];
        mediaFileInputStream.read(mediaArray);
        mediaFileInputStream.close();
        URL mediaLocation = new URL("file", "localhost", File.separatorChar + mediaFile.getAbsolutePath());

        MediaSegment[] testSegmentArray = testInstance.disassembleContent(mediaLocation, mediaArray);
        assertTrue("test 3: ", testSegmentArray.length >= 1);

        byte[] testArray = testInstance.assembleContent(mediaLocation, testSegmentArray);
        assertTrue("test 3: ", (Math.abs(testArray.length - mediaArray.length) <= 100));

        testTrace("test 3 passed");

        succeed();
    }

    /**
     * <pre>
     *
     *  Testcase Name: extractHeader(InputStream content)
     *  Testcase Number: EMB015
     *
     *  setup:
     *
     *  test procedure:
     *  1.call extractHeader(null)
     *    expected result: NullPointerException
     *
     *  2.create input stream from random content and call extractHeader with input stream
     *    possible results:
     *  				a) not null...instance of GenericMediaHeader
     *  				b) FormatSyntaxException
     *  				c) FormatFeatureException
     *  				d) fail...
     *  &lt;pre&gt;
     *
     */
    public void testEMB015() throws MediaException, IOException {
        MediaFormat testInstance = createMediaFormat();
        //
        // test 1
        //
        try {
            testInstance.extractHeader(null);
            fail("test 1: Should throw a NullPointerException");
        } catch (NullPointerException e) {
            testTrace("test 1 passed");
        } catch (Exception e) {
            fail("test 1: Should throw a NullPointerException but threw " + e.toString());
        }
        //
        // test 2
        //
        testInstance = createMediaFormat();
        byte[] testByteArray = EMBStaticHelper.createRandomByteArray(1024);
        InputStream in = new ByteArrayInputStream(testByteArray);
        try {
            MediaHeader header = testInstance.extractHeader(in);
            assertTrue("test 2: ", header instanceof javax.emb.GenericMediaHeader);
            testTrace("test 2 passed");
        } catch (FormatSyntaxException e) {
            testTrace("test 2 passed: FormatSyntaxException");
        } catch (FormatFeatureException e) {
            testTrace("test 2 passed: FormatFeatureException");
        } catch (Exception e) {
            fail("test 2: Should pass, throw a FormatSyntaxException, or throw a FormatFeatureException "
                    + "but threw " + e.toString());
        }
        in.close();

        succeed();
    }

    /**
     * <pre>
     *
     *  Testcase Name: extractProxy(InputStream content)
     *  Testcase Number: EMB016
     *
     *  setup:
     *
     *  test procedure:
     *  1.call extractProxy(null)
     *    expected result: NullPointerException
     *
     *  2.create instance of MediaFormat, bind(&quot;jpg&quot;, new JpegFormat())
     *    call extractProxy with &quot;test.jpg&quot;
     *    expected result: not null
     *
     * </pre>
     */
    public void testEMB016() throws MediaException, IOException {
        MediaFormat testInstance = createMediaFormat();
        MediaFormatRegistry formatRegistry = MediaFormatRegistry.SINGLETON;
        formatRegistry.rebind("jpg", new JpegFormat());
        //
        // test 1
        //
        try {
            testInstance.extractProxy(null);
            fail("test 1: Should throw a NullPointerException");
        } catch (NullPointerException e) {
            testTrace("test 1 passed");
        } catch (Exception e) {
            fail("test 1: Should throw a NullPointerException but threw " + e.toString());
        }
        //
        // test 2
        //
        testInstance = createMediaFormat();
        byte[] testByteArray = EMBStaticHelper.createRandomByteArray(1024);
        InputStream in = new ByteArrayInputStream(testByteArray);
        Media mediaProxy = testInstance.extractProxy(in);
        in.close();
        assertNotNull("test 2: ", mediaProxy);
        testTrace("test 2 passed");

        succeed();
    }

    /**
     * <pre>
     *
     *  Testcase Name: getDefaultMimeType()
     *  Testcase Number: EMB017
     *
     *  setup:
     *
     *  test procedure:
     *  1.call getDefaultMimeType
     *    expected result: result is not null and not an empty string
     *
     * </pre>
     */
    public void testEMB017() throws MediaException {

        // test instance
        MediaFormat testInstance = createMediaFormat();
        //
        // test 1
        //
        assertNotNull("test 1", testInstance.getDefaultMimeType());
        assertFalse("test 1", testInstance.getDefaultMimeType().equals(""));
        testTrace("test 1 passed");

        succeed();
    }

    /**
     * <pre>
     *
     *  Testcase Name: isEmbedded()
     *  Testcase Number: EMB018
     *
     *  setup:
     *
     *  test procedure:
     *  1.create MediaFormat using abstract create method
     *    call isEmbedded()
     *    expected result: no exeception and result is either true or false
     *
     * </pre>
     */
    public void testEMB018() {
        MediaFormat testInstance = createMediaFormat();
        //
        // test 1
        //
        try {
            boolean result = testInstance.isEmbedded();
            testTrace("test 1 passed");
        } catch (Exception e) {
            fail("test 1: Should not throw an Exception but threw " + e.toString());
        }
        succeed();
    }

    /**
     * <pre>
     *
     *  Testcase Name: isStreamingDesirable()
     *  Testcase Number: EMB019
     *
     *  setup:
     *
     *  test procedure:
     *  1.create MediaFormat using abstract create method
     *    call isStreamingDesireable()
     *    expected result: no exeception and result is either true or false
     *
     * </pre>
     */
    public void testEMB019() {
        MediaFormat testInstance = createMediaFormat();
        //
        // test 1
        //
        try {
            boolean result = testInstance.isStreamingDesirable();
            testTrace("test 1 passed");
        } catch (Exception e) {
            fail("test 1: Should not throw an Exception but threw " + e.toString());
        }
        succeed();
    }

}