/*****************************************************************
 *   Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *    https://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 ****************************************************************/
package org.apache.cayenne.configuration.server;

import org.apache.cayenne.access.DataContext;
import org.apache.cayenne.access.DataDomain;
import org.apache.cayenne.access.DataRowStoreFactory;
import org.apache.cayenne.access.DefaultDataRowStoreFactory;
import org.apache.cayenne.access.DefaultObjectMapRetainStrategy;
import org.apache.cayenne.access.ObjectMapRetainStrategy;
import org.apache.cayenne.access.flush.DataDomainFlushActionFactory;
import org.apache.cayenne.access.flush.operation.DbRowOpSorter;
import org.apache.cayenne.access.flush.DefaultDataDomainFlushActionFactory;
import org.apache.cayenne.access.flush.operation.DefaultDbRowOpSorter;
import org.apache.cayenne.ashwood.AshwoodEntitySorter;
import org.apache.cayenne.cache.MapQueryCache;
import org.apache.cayenne.cache.QueryCache;
import org.apache.cayenne.configuration.DefaultObjectStoreFactory;
import org.apache.cayenne.configuration.DefaultRuntimeProperties;
import org.apache.cayenne.configuration.ObjectStoreFactory;
import org.apache.cayenne.configuration.RuntimeProperties;
import org.apache.cayenne.di.AdhocObjectFactory;
import org.apache.cayenne.di.ClassLoaderManager;
import org.apache.cayenne.di.DIBootstrap;
import org.apache.cayenne.di.Injector;
import org.apache.cayenne.di.Module;
import org.apache.cayenne.di.spi.DefaultAdhocObjectFactory;
import org.apache.cayenne.di.spi.DefaultClassLoaderManager;
import org.apache.cayenne.event.EventBridge;
import org.apache.cayenne.event.EventManager;
import org.apache.cayenne.event.MockEventManager;
import org.apache.cayenne.event.NoopEventBridgeProvider;
import org.apache.cayenne.log.JdbcEventLogger;
import org.apache.cayenne.log.Slf4jJdbcEventLogger;
import org.apache.cayenne.map.EntitySorter;
import org.apache.cayenne.tx.DefaultTransactionFactory;
import org.apache.cayenne.tx.DefaultTransactionManager;
import org.apache.cayenne.tx.TransactionFactory;
import org.apache.cayenne.tx.TransactionManager;
import org.junit.Test;

import java.util.Collections;

import static org.junit.Assert.*;

public class DataContextFactoryTest {

    @Test
    public void testCreateDataContextWithDedicatedCache() throws Exception {

        final EventManager eventManager = new MockEventManager();
        final DataDomain domain = new DataDomain("d1");

        domain.setSharedCacheEnabled(false);

        Module testModule = binder -> {
            binder.bind(JdbcEventLogger.class).to(Slf4jJdbcEventLogger.class);
            binder.bind(DataDomain.class).toInstance(domain);
            binder.bind(EventManager.class).toInstance(eventManager);
            binder.bind(QueryCache.class).toInstance(new MapQueryCache(5));
            binder.bind(RuntimeProperties.class).toInstance(
                    new DefaultRuntimeProperties(Collections.<String, String>emptyMap()));
            binder.bind(ObjectMapRetainStrategy.class).to(
                    DefaultObjectMapRetainStrategy.class);
            binder.bind(ObjectStoreFactory.class).to(DefaultObjectStoreFactory.class);
            binder.bind(TransactionFactory.class).to(DefaultTransactionFactory.class);
            binder.bind(TransactionManager.class).to(DefaultTransactionManager.class);
            binder.bind(DataRowStoreFactory.class).to(DefaultDataRowStoreFactory.class);
            binder.bind(EventBridge.class).toProvider(NoopEventBridgeProvider.class);
            binder.bind(DataRowStoreFactory.class).to(DefaultDataRowStoreFactory.class);
            binder.bind(DataDomainFlushActionFactory.class).to(DefaultDataDomainFlushActionFactory.class);
            binder.bind(DbRowOpSorter.class).to(DefaultDbRowOpSorter.class);
            binder.bind(EntitySorter.class).to(AshwoodEntitySorter.class);
            binder.bind(AdhocObjectFactory.class).to(DefaultAdhocObjectFactory.class);
            binder.bind(ClassLoaderManager.class).to(DefaultClassLoaderManager.class);
        };

        Injector injector = DIBootstrap.createInjector(testModule);

        DataContextFactory factory = new DataContextFactory();
        injector.injectMembers(factory);

        DataContext c3 = (DataContext) factory.createContext();
        assertNotNull(c3.getObjectStore().getDataRowCache());
        assertNull(domain.getSharedSnapshotCache());
        assertNotSame(
                c3.getObjectStore().getDataRowCache(),
                domain.getSharedSnapshotCache());
    }

    @Test
    public void testCreateDataContextValidation() throws Exception {
        final EventManager eventManager = new MockEventManager();
        final DataDomain domain = new DataDomain("d1");

        domain.setValidatingObjectsOnCommit(true);

        Module testModule = binder -> {
            binder.bind(JdbcEventLogger.class).to(Slf4jJdbcEventLogger.class);
            binder.bind(DataDomain.class).toInstance(domain);
            binder.bind(EventManager.class).toInstance(eventManager);
            binder.bind(QueryCache.class).toInstance(new MapQueryCache(5));
            binder.bind(RuntimeProperties.class).toInstance(
                    new DefaultRuntimeProperties(Collections.<String, String>emptyMap()));
            binder.bind(ObjectMapRetainStrategy.class).to(
                    DefaultObjectMapRetainStrategy.class);
            binder.bind(ObjectStoreFactory.class).to(DefaultObjectStoreFactory.class);
            binder.bind(TransactionFactory.class).to(DefaultTransactionFactory.class);
            binder.bind(TransactionManager.class).to(DefaultTransactionManager.class);
            binder.bind(EventBridge.class).toProvider(NoopEventBridgeProvider.class);
            binder.bind(DataRowStoreFactory.class).to(DefaultDataRowStoreFactory.class);
            binder.bind(DataDomainFlushActionFactory.class).to(DefaultDataDomainFlushActionFactory.class);
            binder.bind(DbRowOpSorter.class).to(DefaultDbRowOpSorter.class);
            binder.bind(EntitySorter.class).to(AshwoodEntitySorter.class);
            binder.bind(AdhocObjectFactory.class).to(DefaultAdhocObjectFactory.class);
            binder.bind(ClassLoaderManager.class).to(DefaultClassLoaderManager.class);
        };

        Injector injector = DIBootstrap.createInjector(testModule);

        domain.setDataRowStoreFactory(injector.getInstance(DataRowStoreFactory.class));

        DataContextFactory factory = new DataContextFactory();
        injector.injectMembers(factory);

        DataContext c1 = (DataContext) factory.createContext();
        assertTrue(c1.isValidatingObjectsOnCommit());

        domain.setValidatingObjectsOnCommit(false);

        DataContext c2 = (DataContext) factory.createContext();
        assertFalse(c2.isValidatingObjectsOnCommit());
    }

}
