/*
 * Copyright (C) 2005-2007 Alfresco Software Limited.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

 * As a special exception to the terms and conditions of version 2.0 of 
 * the GPL, you may redistribute this Program in connection with Free/Libre 
 * and Open Source Software ("FLOSS") applications as described in Alfresco's 
 * FLOSS exception.  You should have recieved a copy of the text describing 
 * the FLOSS exception, and it is also available here: 
 * http://www.alfresco.com/legal/licensing"
 */
package org.alfresco.repo.content;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;

import org.alfresco.repo.cache.EhCacheAdapter;
import org.alfresco.repo.content.filestore.FileContentStore;
import org.alfresco.service.cmr.repository.ContentIOException;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.util.TempFileProvider;

/**
 * Ensures that the routing of URLs based on context is working.  A combination
 * of fully featured and incompletely featured stores is used to ensure that
 * all routing scenarios are handled.
 * 
 * @see AbstractRoutingContentStore
 * @since 2.1
 * 
 * @author Derek Hulley
 */
public class RoutingContentStoreTest extends AbstractWritableContentStoreTest
{
    private ContentStore storeA;
    private ContentStore storeB;
    private ContentStore storeC;
    private ContentStore storeD;
    private ContentStore routingStore;
    
    @Override
    public void setUp() throws Exception
    {
        super.setUp();
        File tempDir = TempFileProvider.getTempDir();
        // Create a subdirectory for A
        File storeADir = new File(tempDir, "A");
        storeA = new FileContentStore(storeADir);
        // Create a subdirectory for B
        File storeBDir = new File(tempDir, "B");
        storeB = new FileContentStore(storeBDir);
        // Create a subdirectory for C
        File storeCDir = new File(tempDir, "C");
        storeC = new DumbReadOnlyFileStore(new FileContentStore(storeCDir));
        // No subdirectory for D
        storeD = new SupportsNoUrlFormatStore();
        // Create the routing store
        routingStore = new RandomRoutingContentStore(storeA, storeB, storeC, storeD);
    }
    
    @Override
    protected ContentStore getStore()
    {
        return routingStore;
    }

    public void testSetUp() throws Exception
    {
        assertNotNull(routingStore);
    }
    
    private void checkForContent(String contentUrl, String content)
    {
        for (ContentStore store : new ContentStore[] {storeA, storeB})
        {
            // Does the store have it
            if (store.exists(contentUrl))
            {
                // Check it
                ContentReader reader = store.getReader(contentUrl);
                String checkContent = reader.getContentString();
                assertEquals("Content found but is incorrect", content, checkContent);
                return;
            }
        }
        fail("Content not found in any of the stores: " + contentUrl);
    }
    
    /**
     * Checks that requests for missing content URLs are served.
     */
    public void testMissingUrl()
    {
        String missingContentUrl = FileContentStore.createNewFileStoreUrl();
        
        ContentReader reader = routingStore.getReader(missingContentUrl);
        assertNotNull("Missing URL should not return null", reader);
        assertFalse("Empty reader should say content doesn't exist.", reader.exists());
        try
        {
            reader.getContentString();
            fail("Empty reader cannot return content.");
        }
        catch (Throwable e)
        {
            // Expected
        }
    }
    
    public void testGeneralUse()
    {
        for (int i = 0 ; i < 20; i++)
        {
            ContentContext contentContext = new ContentContext(null, null);
            ContentWriter writer = routingStore.getWriter(contentContext);
            String content = "This was generated by " + this.getClass().getName() + "#" + getName() + " number " + i;
            writer.putContent(content);
            // Check that it exists
            String contentUrl = writer.getContentUrl();
            checkForContent(contentUrl, content);
            
            // Now go direct to the routing store and check that it is able to find the appropriate URLs
            ContentReader reader = routingStore.getReader(contentUrl);
            assertNotNull("Null reader returned", reader);
            assertTrue("Reader should be onto live content", reader.exists());
        }
    }
    
    /**
     * A test routing store that directs content writes to a randomly-chosen store.
     * Matching of content URLs back to the stores is handled by the base class.
     * 
     * @author Derek Hulley
     */
    private static class RandomRoutingContentStore extends AbstractRoutingContentStore
    {
        private List<ContentStore> stores;
        
        public RandomRoutingContentStore(ContentStore ... stores)
        {
            this.stores = new ArrayList<ContentStore>(5);
            for (ContentStore store : stores)
            {
                this.stores.add(store);
            }
            Cache ehCache = new Cache("RandomRoutingContentStore", 50, false, true, 0L, 0L);
            CacheManager cacheManager = new CacheManager();
            cacheManager.addCache(ehCache);
            EhCacheAdapter<String, ContentStore> cache = new EhCacheAdapter<String, ContentStore>();
            cache.setCache(ehCache);
            super.setStoresCache(cache);
        }
        
        @Override
        protected List<ContentStore> getAllStores()
        {
            return stores;
        }

        @Override
        protected ContentStore selectWriteStore(ContentContext ctx)
        {
            // Shuffle the list of writable stores
            List<ContentStore> shuffled = new ArrayList<ContentStore>(stores);
            Collections.shuffle(shuffled);
            // Pick the first writable store
            for (ContentStore store : shuffled)
            {
                if (store.isWriteSupported())
                {
                    return store;
                }
            }
            // Nothing found
            fail("A request came for a writer when there is no writable store to choose from");
            return null;
        }
    }
    
    /**
     * The simplest possible store.
     * 
     * @author Derek Hulley
     */
    private static class DumbReadOnlyFileStore extends AbstractContentStore
    {
        FileContentStore fileStore;
        public DumbReadOnlyFileStore(FileContentStore fileStore)
        {
            this.fileStore = fileStore;
        }

        public boolean isWriteSupported()
        {
            return false;
        }

        public ContentReader getReader(String contentUrl)
        {
            return fileStore.getReader(contentUrl);
        }

        @Override
        public void getUrls(Date createdAfter, Date createdBefore, ContentUrlHandler handler) throws ContentIOException
        {
            fileStore.getUrls(createdAfter, createdBefore, handler);
        }
    }
    
    /**
     * This store supports nothing.  It is designed to catch the routing code out.
     * 
     * @author Derek Hulley
     */
    private static class SupportsNoUrlFormatStore extends AbstractContentStore
    {
        public SupportsNoUrlFormatStore()
        {
        }

        public boolean isWriteSupported()
        {
            return false;
        }

        public ContentReader getReader(String contentUrl)
        {
            throw new UnsupportedContentUrlException(this, contentUrl);
        }

        @Override
        public void getUrls(Date createdAfter, Date createdBefore, ContentUrlHandler handler) throws ContentIOException
        {
            throw new UnsupportedOperationException("getUrls not supported");
        }
    }
}