/*
 * Copyright (c) 1998, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.testing.tests.weaving;

import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;

import org.eclipse.persistence.internal.jpa.metadata.MetadataLogger;
import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataAsmFactory;
import org.eclipse.persistence.internal.jpa.weaving.PersistenceWeaver;
import org.eclipse.persistence.internal.jpa.weaving.TransformerFactory;
import org.eclipse.persistence.internal.weaving.PersistenceWeavedLazy;
import org.eclipse.persistence.logging.SessionLog;
import org.eclipse.persistence.sessions.Session;
import org.eclipse.persistence.sessions.server.ServerSession;
import org.eclipse.persistence.testing.models.weaving.SimpleObject;
import org.eclipse.persistence.testing.models.weaving.SimpleProject;

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

public class SimpleWeaverTestSuite extends TestCase {

    // fixtures
    public static SimpleClassLoader simpleClassLoader = null;
    public static byte[] originalBytes = null;
    public static Collection entities = null;

    static {
        setUpFixtures();
    }
    public static void setUpFixtures() {
        simpleClassLoader = new SimpleClassLoader();
        InputStream is = simpleClassLoader.getResourceAsStream(SimpleObject.class.getName().replace('.','/') + ".class");
        originalBytes = readStreamContentsIntoByteArray(is);
        entities = new ArrayList();
        entities.add(new MetadataAsmFactory(new MetadataLogger(null), SimpleWeaverTestSuite.class.getClassLoader()).getMetadataClass(SimpleObject.class.getName()));
    }
    public static void tearDownFixtures() {
        simpleClassLoader = null;
        originalBytes = null;
        entities = null;
    }

    public SimpleWeaverTestSuite(String testName) {
        super(testName);
    }

    public static Test suite() {
        TestSuite suite = new TestSuite("Simple TopLinkWeaver Tests");
        suite.addTest(new SimpleWeaverTestSuite(
            "test TopLinkWeaver with null Session") {
            @Override
            public void setUp() {
            }
            @Override
            public void tearDown() {
            }
            @Override
            public void runTest() {
                nullSessionTest();
            }
        });
        suite.addTest(new SimpleWeaverTestSuite(
            "test TopLinkWeaver with null Project") {
            @Override
            public void setUp() {
            }
            @Override
            public void tearDown() {
            }
            @Override
            public void runTest() {
                Session session = new ServerSession();
                session.setLogLevel(SessionLog.OFF);
                nullProjectTest(session);
            }
        });
        suite.addTest(new SimpleWeaverTestSuite(
            "test TopLinkWeaver with null list of entities") {
            @Override
            public void setUp() {
            }
            @Override
            public void tearDown() {
            }
            @Override
            public void runTest() {
                Session session = new ServerSession(new SimpleProject());
                session.setLogLevel(SessionLog.OFF);
                nullEntitiesTest(session);
            }
        });
        suite.addTest(new SimpleWeaverTestSuite(
            "test TopLinkWeaver with empty list of entities") {
            @Override
            public void setUp() {
            }
            @Override
            public void tearDown() {
            }
            @Override
            public void runTest() {
                Session session = new ServerSession(new SimpleProject());
                session.setLogLevel(SessionLog.OFF);
                emptyEntitiesTest(session);
            }
        });
        suite.addTest(new SimpleWeaverTestSuite("build TopLinkWeaver") {
            @Override
            public void setUp() {
            }
            @Override
            public void tearDown() {
            }
            @Override
            public void runTest() {
                Session session = new ServerSession(new SimpleProject());
                session.setLogLevel(SessionLog.OFF);
                buildWeaver(session, entities);
            }
        });
        return suite;
    }

    public void nullSessionTest() {
        boolean expectedFailure = false;
        try {
            TransformerFactory.createTransformerAndModifyProject(null, null, Thread.currentThread().getContextClassLoader(), true, false, true, true, true, false);
        }
        catch (IllegalArgumentException iae) {
            expectedFailure = true;
        }
        assertTrue(
            "TopLinkWeaver did not throw proper IllegalArgumentException for null Session in constructor",
            expectedFailure);
    }

    public void nullProjectTest(Session session) {
        boolean expectedFailure = false;
        try {
            TransformerFactory.createTransformerAndModifyProject(session, null, Thread.currentThread().getContextClassLoader(), true, false, true, true, true, false);
        }
        catch (IllegalArgumentException iae) {
            expectedFailure = true;
        }
        assertTrue(
            "TopLinkWeaver did not throw proper IllegalArgumentException for null Session's Project in constructor",
            expectedFailure);
    }

    public void nullEntitiesTest(Session session) {
        try {
            TransformerFactory.createTransformerAndModifyProject(session, null, Thread.currentThread().getContextClassLoader(), true, false, true, true, true, false);
        }
        catch (Exception e) {
            fail(getName() + " failed: " + e.toString());
        }
    }

    public void emptyEntitiesTest(Session session) {
        try {
            TransformerFactory.createTransformerAndModifyProject(session, new ArrayList(), Thread.currentThread().getContextClassLoader(), true, false, true, true, true, false);
        }
        catch (Exception e) {
            fail(getName() + " failed: " + e.toString());
        }
    }

    public PersistenceWeaver buildWeaver(Session session, Collection entities) {

        PersistenceWeaver tw = null;
        try {
            tw = TransformerFactory.createTransformerAndModifyProject(session, entities, Thread.currentThread().getContextClassLoader(), true, false, true, true, true, false);
        }
        catch (Exception e) {
            fail(getName() + " failed: " + e.toString());
        }
        assertNotNull("could not build TopLinkWeaver", tw);
        return tw;
    }

    public void buildWeavedClass(byte[] weavedBytes) {

        Class weavedClass = null;
        try {
            weavedClass = simpleClassLoader.define_class(SimpleObject.class.getName(), weavedBytes, 0, weavedBytes.length);
        }
        catch (Exception e) {
            fail(getName() + " failed: " + e.toString());
        }
        assertNotNull("could not build weaved class", weavedClass);

        Class[] interfaces = null;
        try {
            interfaces = weavedClass.getInterfaces();
        }
        catch (Exception e) {
            fail(getName() + " failed: " + e.toString());
        }
        assertNotNull("Weaved class has no interfaces", interfaces);
        boolean containsSerializable = false;
        boolean containsTopLinkWeaved = false;
        boolean containsChangeTracker = false;
        for (int i = 0; i < interfaces.length; i++) {
            Class c = interfaces[i];
            if (c.equals(Serializable.class)) {
                containsSerializable = true;
            }
            if (c.equals(PersistenceWeavedLazy.class)) {
                containsTopLinkWeaved = true;
            }
        }
        // test that original interface is still there
        assertNotNull("Weaved class does not implement Serializable",
            containsSerializable);
        // test that 'marker' interface added
        assertNotNull("Weaved class does not implement TopLinkWeaved",
            containsTopLinkWeaved);
        // none of the mapping are isMutable, so it should be a ChangeTracker
        assertNotNull("Weaved class does not implement ChangeTracker",
            containsChangeTracker);
    }

    public static byte[] readStreamContentsIntoByteArray(InputStream is) {

        byte[] result = null;
        try {
            int readPosition = 0;
            result = new byte[is.available()];
            while (true) {
                int bytesRead = is.read(result, readPosition,
                    result.length - readPosition);
                if (bytesRead == -1) {
                    break;
                }
                readPosition += bytesRead;
                if (readPosition == result.length) {
                    int peek = is.read();
                    if (peek == -1) {
                        break;
                    }
                    byte[] temp = new byte[result.length * 2];
                    System.arraycopy(result, 0, temp, 0, readPosition);
                    result = temp;
                    result[readPosition++] = (byte) peek;
                }
            }
            if (readPosition < result.length) {
                byte[] temp = new byte[readPosition];
                System.arraycopy(result, 0, temp, 0, readPosition);
                result = temp;
            }
        }
        catch (Exception e) { /* ignore */ }
        finally {
            try {
                is.close();
            }
            catch (IOException e) { /* ignore */ }
        }
        return result;
    }

}
