/*
 * JBoss, Home of Professional Open Source
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package org.jboss.cache.marshall;

import junit.framework.TestCase;
import org.jboss.cache.Fqn;
import org.jboss.cache.Version;
import org.jgroups.blocks.MethodCall;

import java.util.Arrays;
import java.util.List;
import java.io.ObjectInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.ByteArrayOutputStream;

/**
 * Tests the enhanced treecache marshaller
 *
 * @author <a href="mailto:manik@jboss.org">Manik Surtani (manik@jboss.org)</a>
 */
public class VersionAwareMarshallerTest extends TestCase
{
    public void testStringBasedFqn() throws Exception
    {
        VersionAwareMarshaller marshaller = new VersionAwareMarshaller(new RegionManager(), false, false, Version.getVersionString(Version.getVersionShort()));
        Fqn fqn = new Fqn(new Object[]{"JSESSIONID", "1010.10.5:3000", "1234567890", "1"});
        byte[] asBytes = marshaller.objectToByteBuffer(fqn);
        System.out.println("Marshalled to " + asBytes.length + " bytes");
        Object o2 = marshaller.objectFromByteBuffer(asBytes);
        assertEquals(fqn, o2);
    }

    public void testNonStringBasedFqn() throws Exception
    {
        VersionAwareMarshaller marshaller = new VersionAwareMarshaller(new RegionManager(), false, false, Version.getVersionString(Version.getVersionShort()));
        Fqn fqn = new Fqn(new Object[]{new Integer(3), Boolean.FALSE});
        byte[] asBytes = marshaller.objectToByteBuffer(fqn);
        Object o2 = marshaller.objectFromByteBuffer(asBytes);
        assertEquals(fqn, o2);
    }

    public void testMethodCall() throws Exception
    {
        VersionAwareMarshaller marshaller = new VersionAwareMarshaller(new RegionManager(), false, false, Version.getVersionString(Version.getVersionShort()));
        Fqn fqn = new Fqn(new Object[]{new Integer(3), Boolean.FALSE});
        MethodCall call = MethodCallFactory.create(MethodDeclarations.putKeyValMethodLocal, new Object[]{fqn, "key", "value"});
        byte[] asBytes = marshaller.objectToByteBuffer(call);
        Object o2 = marshaller.objectFromByteBuffer(asBytes);
        assertTrue("Unmarshalled object should be a method call", o2 instanceof JBCMethodCall);
        MethodCall m2 = (MethodCall) o2;
        assertEquals(call.getMethod(), m2.getMethod());
        List l1 = Arrays.asList(call.getArgs());
        List l2 = Arrays.asList(m2.getArgs());
        assertEquals(l1, l2);
    }

    public void testNestedMethodCall() throws Exception
    {
        VersionAwareMarshaller marshaller = new VersionAwareMarshaller(new RegionManager(), false, false, Version.getVersionString(Version.getVersionShort()));
        Fqn fqn = new Fqn(new Object[]{new Integer(3), Boolean.FALSE});
        MethodCall call = MethodCallFactory.create(MethodDeclarations.putKeyValMethodLocal, new Object[]{fqn, "key", "value"});
        MethodCall replicateCall = MethodCallFactory.create(MethodDeclarations.replicateMethod, new Object[]{call});
        byte[] asBytes = marshaller.objectToByteBuffer(replicateCall);
        Object o2 = marshaller.objectFromByteBuffer(asBytes);
        assertTrue("Unmarshalled object should be a method call", o2 instanceof JBCMethodCall);
        MethodCall m2 = (MethodCall) o2;
        assertEquals(replicateCall.getMethod(), m2.getMethod());

        // 1st arg should be another method call.
        MethodCall m3 = (MethodCall) m2.getArgs()[0];
        assertEquals(call.getMethod(), m3.getMethod());

        List l1 = Arrays.asList(call.getArgs());
        List l2 = Arrays.asList(m3.getArgs());
        assertEquals(l1, l2);
    }

    public void testMarshallerSelection()
    {
        VersionAwareMarshaller marshaller = new VersionAwareMarshaller(new RegionManager(), false, false, "1.4.0.GA");
        assertEquals(TreeCacheMarshaller140.class, marshaller.defaultMarshaller.getClass());

        marshaller = new VersionAwareMarshaller(new RegionManager(), false, false, "1.5.0.GA");
        assertEquals(LegacyTreeCacheMarshaller.class, marshaller.defaultMarshaller.getClass());

        marshaller = new VersionAwareMarshaller(new RegionManager(), false, false, "1.3.0.GA");
        assertEquals(LegacyTreeCacheMarshaller.class, marshaller.defaultMarshaller.getClass());

        marshaller = new VersionAwareMarshaller(new RegionManager(), false, false, "1.3.0.SP2");
        assertEquals(LegacyTreeCacheMarshaller.class, marshaller.defaultMarshaller.getClass());

        marshaller = new VersionAwareMarshaller(new RegionManager(), false, false, "1.3.1.GA");
        assertEquals(LegacyTreeCacheMarshaller.class, marshaller.defaultMarshaller.getClass());

        marshaller = new VersionAwareMarshaller(new RegionManager(), false, false, "1.2.4.SP2");
        assertEquals(LegacyTreeCacheMarshaller.class, marshaller.defaultMarshaller.getClass());

        marshaller = new VersionAwareMarshaller(new RegionManager(), false, false, "1.2.3");
        assertEquals(LegacyTreeCacheMarshaller.class, marshaller.defaultMarshaller.getClass());
    }

    public void testGetMarshaller()
    {
        VersionAwareMarshaller marshaller = new VersionAwareMarshaller(new RegionManager(), false, false, "1.4.0.GA");
        assertEquals("Only one marshaller should be in the map by this stage", 1, marshaller.marshallers.size());

        assertEquals(TreeCacheMarshaller140.class, marshaller.getMarshaller(14).getClass());
        assertEquals(LegacyTreeCacheMarshaller.class, marshaller.getMarshaller(15).getClass());
        assertEquals(LegacyTreeCacheMarshaller.class, marshaller.getMarshaller(1).getClass());
        assertEquals(LegacyTreeCacheMarshaller.class, marshaller.getMarshaller(-1).getClass());
        assertEquals(LegacyTreeCacheMarshaller.class, marshaller.getMarshaller(0).getClass());

        assertEquals("Two marshallers should be in the map by this stage", 2, marshaller.marshallers.size());
    }

    public void testVersionHeader140() throws Exception
    {
        VersionAwareMarshaller marshaller = new VersionAwareMarshaller(new RegionManager(), false, false, "1.4.0.GA");
        byte[] bytes = marshaller.objectToByteBuffer("Hello");

        // expect that this has been serialized using JBoss Serialization so use this to get an OIS.
        ObjectInputStream in = ObjectSerializationFactory.createObjectInputStream(bytes);
        assertEquals("Version header short should be '14'", 14, in.readShort());
    }

    public void testVersionHeaderLegacy() throws Exception
    {
        VersionAwareMarshaller marshaller = new VersionAwareMarshaller(new RegionManager(), false, false, "1.3.0.GA");
        byte[] bytes = marshaller.objectToByteBuffer("Hello");

        // expect that this has been serialized traditional serialization
        ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes));

        try
        {
            in.readShort();
            fail("Version header short should not exist.");
        }
        catch (IOException e)
        {
            assertTrue("Expected an exception trying to read the version short", true);
        }
    }

    public void testHandleLegacyStream() throws Exception
    {
        VersionAwareMarshaller marshaller = new VersionAwareMarshaller(new RegionManager(), false, false, "1.4.0.GA");

        // create a 'legacy' stream.
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(bout);
        
        out.writeObject("hello");

        Object unmarshalled = marshaller.objectFromByteBuffer(bout.toByteArray());

        assertEquals("hello", unmarshalled);
    }


}
