/*
 * 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
//     07/15/2010-2.2 Guy Pelletier
//       -311395 : Multiple lifecycle callback methods for the same lifecycle event
package org.eclipse.persistence.testing.tests.jpa.inherited;

import javax.persistence.EntityManager;

import junit.framework.*;

import org.eclipse.persistence.testing.framework.junit.JUnitTestCase;
import org.eclipse.persistence.testing.models.jpa.inherited.Accredidation;
import org.eclipse.persistence.testing.models.jpa.inherited.Beer;
import org.eclipse.persistence.testing.models.jpa.inherited.Alpine;
import org.eclipse.persistence.testing.models.jpa.inherited.BeerConsumer;
import org.eclipse.persistence.testing.models.jpa.inherited.Beverage;
import org.eclipse.persistence.testing.models.jpa.inherited.ExpertBeerConsumer;
import org.eclipse.persistence.testing.models.jpa.inherited.SerialNumber;
import org.eclipse.persistence.testing.models.jpa.inherited.InheritedTableManager;

@SuppressWarnings("deprecation")
public class InheritedCallbacksJunitTest extends JUnitTestCase {
    private static Integer m_Id;

    public InheritedCallbacksJunitTest() {
        super();
    }

    public InheritedCallbacksJunitTest(String name) {
        super(name);
    }

    public static Test suite() {
        TestSuite suite = new TestSuite();
        suite.setName("InheritedCallbacksJunitTest");
        suite.addTest(new InheritedCallbacksJunitTest("testSetup"));
        suite.addTest(new InheritedCallbacksJunitTest("testPreAndPostPersistAlpine"));
        suite.addTest(new InheritedCallbacksJunitTest("testPrePersistAlpineOnMerge"));
        suite.addTest(new InheritedCallbacksJunitTest("testPrePersistAlpineAndSerialNumberOnBeerConsumerMerge"));
        suite.addTest(new InheritedCallbacksJunitTest("testPreAndPostPersistBeerConsumer"));
        suite.addTest(new InheritedCallbacksJunitTest("testPreAndPostPersistExpertBeerConsumer"));
        suite.addTest(new InheritedCallbacksJunitTest("testPostLoadOnFind"));
        suite.addTest(new InheritedCallbacksJunitTest("testPostLoadOnRefresh"));
        suite.addTest(new InheritedCallbacksJunitTest("testPreAndPostUpdate"));
        suite.addTest(new InheritedCallbacksJunitTest("testPreAndPostRemove"));

        return suite;
    }

    /**
     * The setup is done as a test, both to record its failure, and to allow execution in the server.
     */
    public void testSetup() {
        new InheritedTableManager().replaceTables(JUnitTestCase.getServerSession());
        clearCache();
    }

    public void testPreAndPostPersistAlpine() {
        EntityManager em = createEntityManager();

        try {
            int beerPrePersistCount = Beer.BEER_PRE_PERSIST_COUNT;
            int alpinePrePersistCount = Alpine.ALPINE_PRE_PERSIST_COUNT;

            int beveragePostPersistCount = Beverage.BEVERAGE_POST_PERSIST_COUNT;
            int beerPostPersistCount = Beer.BEER_POST_PERSIST_COUNT;
            int alpinePostPersistCount = Alpine.ALPINE_POST_PERSIST_COUNT;

            beginTransaction(em);
            SerialNumber serialNumber = new SerialNumber();
            em.persist(serialNumber);
            Alpine alpine = new Alpine(serialNumber);
            alpine.setBestBeforeDate(new java.util.Date(2007, 8, 17));
            alpine.setAlcoholContent(5.0);

            em.persist(alpine);
            commitTransaction(em);

            verifyNotCalled(beerPrePersistCount, Beer.BEER_PRE_PERSIST_COUNT, "celebrate");
            verifyCalled(alpinePrePersistCount, Alpine.ALPINE_PRE_PERSIST_COUNT, "celebrate");

            verifyNotCalled(beveragePostPersistCount, Beverage.BEVERAGE_POST_PERSIST_COUNT, "celebrateAgain");
            verifyCalled(beerPostPersistCount, Beer.BEER_POST_PERSIST_COUNT, "celebrateSomeMore");
            verifyCalled(alpinePostPersistCount, Alpine.ALPINE_POST_PERSIST_COUNT, "celebrateAgain");
        } catch (RuntimeException ex) {
            if (isTransactionActive(em)){
                rollbackTransaction(em);
            }

            throw ex;
        } finally {
            closeEntityManager(em);
        }
    }

    public void testPrePersistAlpineOnMerge() {
        EntityManager em = createEntityManager();

        try {
            int beerPrePersistCount = Beer.BEER_PRE_PERSIST_COUNT;
            int alpinePrePersistCount = Alpine.ALPINE_PRE_PERSIST_COUNT;

            beginTransaction(em);
            SerialNumber serialNumber = new SerialNumber();
            em.persist(serialNumber);
            Alpine alpine = new Alpine(serialNumber);
            alpine.setBestBeforeDate(new java.util.Date(2007, 8, 17));
            alpine.setAlcoholContent(5.0);

            alpine.setClassification(Alpine.Classification.NONE);

            Alpine mergedAlpine = em.merge(alpine);

            verifyNotCalled(beerPrePersistCount, Beer.BEER_PRE_PERSIST_COUNT, "PrePersist");
            verifyCalled(alpinePrePersistCount, Alpine.ALPINE_PRE_PERSIST_COUNT, "PrePersist");
            assertTrue("The merged alpine classification was not updated from the PrePersist lifecycle method", mergedAlpine.getClassification() == Alpine.Classification.STRONG);

            commitTransaction(em);
        } catch (RuntimeException ex) {
            if (isTransactionActive(em)){
                rollbackTransaction(em);
            }

            throw ex;
        } finally {
            closeEntityManager(em);
        }
    }

    public void testPrePersistAlpineAndSerialNumberOnBeerConsumerMerge() {
        EntityManager em = createEntityManager();

        try {
            int beerPrePersistCount = Beer.BEER_PRE_PERSIST_COUNT;
            int alpinePrePersistCount = Alpine.ALPINE_PRE_PERSIST_COUNT;
            int serialNumberPrePersistCount = SerialNumber.SERIAL_NUMBER_PRE_PERSIST_COUNT;

            beginTransaction(em);
            BeerConsumer beerConsumer = new BeerConsumer();
            beerConsumer.setName("A consumer to delete eventually");
            em.persist(beerConsumer);
            em.clear();

            SerialNumber serialNumber = new SerialNumber();

            Alpine alpine = new Alpine(serialNumber);
            alpine.setBestBeforeDate(new java.util.Date(2007, 8, 17));
            alpine.setAlcoholContent(5.0);

            alpine.setClassification(Alpine.Classification.NONE);
            beerConsumer.addAlpineBeerToConsume(alpine);

            BeerConsumer mergedBeerConsumer = em.merge(beerConsumer);

            mergedBeerConsumer.getAlpineBeerToConsume(0).setId(mergedBeerConsumer.getAlpineBeerToConsume(0).getSerialNumber().getNumber());
            verifyNotCalled(beerPrePersistCount, Beer.BEER_PRE_PERSIST_COUNT, "PrePersist");
            verifyCalled(alpinePrePersistCount, Alpine.ALPINE_PRE_PERSIST_COUNT, "PrePersist");
            verifyCalled(serialNumberPrePersistCount, SerialNumber.SERIAL_NUMBER_PRE_PERSIST_COUNT, "PrePersist");
            assertTrue("The merged alpine classification was not updated from the PrePersist lifecycle method", mergedBeerConsumer.getAlpineBeerToConsume(0).getSerialNumber().getIssueDate() != null);

            commitTransaction(em);
        } catch (RuntimeException ex) {
            if (isTransactionActive(em)){
                rollbackTransaction(em);
            }

            throw ex;
        } finally {
            closeEntityManager(em);
        }
    }

    public void testPreAndPostPersistBeerConsumer() {
        EntityManager em = createEntityManager();

        try {
            beginTransaction(em);
            BeerConsumer beerConsumer = new BeerConsumer();
            beerConsumer.setName("A consumer to delete eventually");
            em.persist(beerConsumer);
            m_Id = beerConsumer.getId();
            commitTransaction(em);

            verifyCalled(0, beerConsumer.pre_persist_count, "PrePersist");
            verifyCalled(0, beerConsumer.post_persist_count, "PostPersist");
        } catch (RuntimeException ex) {
            if (isTransactionActive(em)) {
                rollbackTransaction(em);
            }

            throw ex;
        } finally {
            closeEntityManager(em);
        }
    }

    public void testPreAndPostPersistExpertBeerConsumer() {
        EntityManager em = createEntityManager();

        try {
            beginTransaction(em);
            ExpertBeerConsumer beerConsumer = new ExpertBeerConsumer();
            beerConsumer.setName("An expert consumer to delete eventually");
            Accredidation accredidation = new Accredidation();
            accredidation.setDetails("SuperElite");
            beerConsumer.setAccredidation(accredidation);
            em.persist(beerConsumer);
            commitTransaction(em);

            verifyNotCalled(0, beerConsumer.pre_persist_count, "PrePersist");
            verifyCalled(0, beerConsumer.ebc_pre_persist_count, "PrePersist");
            verifyCalled(0, beerConsumer.post_persist_count, "PostPersist");
        } catch (RuntimeException ex) {
            if (isTransactionActive(em)) {
                rollbackTransaction(em);
            }

            throw ex;
        } finally {
            closeEntityManager(em);
        }
    }

    public void testPostLoadOnFind() {
        BeerConsumer beerConsumer = createEntityManager().find(BeerConsumer.class, m_Id);
        verifyCalled(0, beerConsumer.post_load_count, "PostLoad");
    }

    public void testPostLoadOnRefresh() {
        EntityManager em = createEntityManager();

        try {
            beginTransaction(em);
            BeerConsumer beerConsumer = em.find(BeerConsumer.class, m_Id);
            em.refresh(beerConsumer);
            commitTransaction(em);

            verifyCalled(0, beerConsumer.post_load_count, "PostLoad");
        } catch (RuntimeException ex) {
            if (isTransactionActive(em)) {
                rollbackTransaction(em);
            }

            throw ex;
        } finally {
            closeEntityManager(em);
        }
    }

    public void testPreAndPostUpdate() {
        EntityManager em = createEntityManager();

        try {
            int count1, count2 = 0;

            beginTransaction(em);
            BeerConsumer beerConsumer = em.find(BeerConsumer.class, m_Id);
            count1 = beerConsumer.pre_update_count;
            beerConsumer.setName("An updated name");
            count2 = beerConsumer.post_update_count;
            commitTransaction(em);

            verifyCalled(count1, beerConsumer.pre_update_count, "PreUpdate");
            verifyCalled(count2, beerConsumer.post_update_count, "PostUpdate");
        } catch (RuntimeException ex) {
            if (isTransactionActive(em)) {
                rollbackTransaction(em);
            }

            throw ex;
        } finally {
            closeEntityManager(em);
        }
    }

    public void testPreAndPostRemove() {
        EntityManager em = createEntityManager();

        try {
            int count1, count2 = 0;

            beginTransaction(em);
            BeerConsumer beerConsumer = em.find(BeerConsumer.class, m_Id);
            count1 = beerConsumer.pre_remove_count;
            em.remove(beerConsumer);
            count2 = beerConsumer.post_remove_count;
            commitTransaction(em);

            verifyCalled(count1, beerConsumer.pre_remove_count, "PreRemove");
            verifyCalled(count2, beerConsumer.post_remove_count, "PostRemove");
        } catch (RuntimeException ex) {
            if (isTransactionActive(em)) {
                rollbackTransaction(em);
            }

            throw ex;
        } finally {
            closeEntityManager(em);
        }
    }

    public void verifyCalled(int countBefore, int countAfter, String callback) {
        assertFalse("The callback method [" + callback + "] was not called", countBefore == countAfter);
        assertTrue("The callback method [" + callback + "] was called more than once", countAfter == (countBefore + 1));
    }

    public void verifyNotCalled(int countBefore, int countAfter, String callback) {
        assertTrue("The callback method [" + callback + "] was called.", countBefore == countAfter);
    }
}
