package wicket.contrib.spring.injection;


import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Method;

import wicket.contrib.spring.injection.SerializableLazyProxyCreator.ProxyReplacement;


import net.sf.cglib.proxy.MethodProxy;
import junit.framework.TestCase;

public class SerializableLazyProxyCreatorTest extends TestCase
{

    public static class TestObject {
     
        public int _counter = 0;
        public int _fcounter = 0;
        public int _cloneCounter = 0;
        public int _thisCounter = 0;
        
        public void increase(){
         _counter++;   
        }
        
        public TestObject getThis(){
            _thisCounter++;
            return this;
        }
        
        @Override
        public void finalize() throws Throwable {
            _fcounter++;
        }
        
        @Override
        public Object clone() throws CloneNotSupportedException {
            _cloneCounter++;
            return null;
        }
        
    }
    public static TestObject SINGLETON_TESTOBJ = new TestObject();
    
    public void testSerialize() throws Throwable{
    
        TObjectResolver tI = new TObjectResolver();
        TestObject tO = (TestObject) SerializableLazyProxyCreator.makeProxy(TestObject.class, tI); 
        
        assertTrue(tO instanceof Serializable);
        
        //test the deserialization methods
        Method m = tO.getClass().getMethod("writeReplace", new Class[]{});
        assertNotNull(m);
        Object ob = m.invoke(tO, new Object[]{});
        assertTrue(ob instanceof ProxyReplacement);
        assertTrue(((ProxyReplacement)ob).readResolve() instanceof TestObject);
        
        //test the for LazyInitProxy
        assertTrue(tO instanceof LazyInitProxy);
        LazyInitProxy lip = (LazyInitProxy) tO;
        assertSame(tI,lip.getLazyInitProxyObjectResolver());
        
        //test replacement of this retung
        assertSame(tO, tO.getThis());
        assertEquals(1,SINGLETON_TESTOBJ._thisCounter);
        SINGLETON_TESTOBJ._thisCounter = 0;
        
        //test the equals method
        assertTrue(tO.equals(tO));
        assertFalse(tO.equals(SINGLETON_TESTOBJ));
        
        //test the toString
        assertEquals(SINGLETON_TESTOBJ.toString(),tO.toString());
        
        //test the finalize
        tO.finalize();
        assertEquals(0,SINGLETON_TESTOBJ._fcounter);
        
        //test clone
        tO.clone();
        assertEquals(0,SINGLETON_TESTOBJ._cloneCounter);
        
        //test the increase method
        tO.increase();
        assertEquals(1,tI._counter);
        assertEquals(1,SINGLETON_TESTOBJ._counter);
        tO.increase();
        assertEquals(1,tI._counter);
        assertEquals(2,SINGLETON_TESTOBJ._counter);
        
        //reset the counter
        tI._counter = 0;
        SINGLETON_TESTOBJ._counter = 0;
        
        //serialize
        tI._printStacktrace = true;
        System.out.println("Serializing()");
        serializeDeserialize(tO);
        System.out.println("EndSerializing()");
        
        //check that finalize is not called af
        assertEquals(0,SINGLETON_TESTOBJ._fcounter);
        
        assertEquals(0,tI._counter);
        tO.increase();
        assertEquals(0,tI._counter);
        assertEquals(1,SINGLETON_TESTOBJ._counter);
        
    }
    
    public static class TObjectResolver implements ObjectResolver{

        transient int _counter = 0;
        transient boolean _printStacktrace = false;
        public Object resolveObject() {
            _counter++;
            if(_printStacktrace){
                new Throwable().printStackTrace();
                System.out.println("TObjectResolver counter: "+_counter);
            }
            return SINGLETON_TESTOBJ;
        }
        
    }

    public static Object serializeDeserialize(Object ob) throws IOException, ClassNotFoundException{
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bout);
        oos.writeObject(ob);
        oos.close();
        
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bout.toByteArray()));
        Object ret = (TestObject) ois.readObject();
        ois.close();
        return ret;
    }

}
