/**
 * 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
 *
 *      http://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.activemq.kaha.impl;

import java.io.IOException;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.activemq.kaha.ContainerId;
import org.apache.activemq.kaha.Marshaller;
import org.apache.activemq.kaha.Store;
import org.apache.activemq.kaha.StoreEntry;
import org.apache.activemq.kaha.StoreLocation;
import org.apache.activemq.kaha.impl.data.Item;
import org.apache.activemq.kaha.impl.index.IndexItem;
import org.apache.activemq.kaha.impl.index.IndexManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A container of roots for other Containers
 * 
 * 
 */

class IndexRootContainer {
    
    protected static final Marshaller ROOT_MARSHALLER = Store.OBJECT_MARSHALLER;
    private static final Logger LOG = LoggerFactory.getLogger(IndexRootContainer.class);

    protected IndexItem root;
    protected IndexManager indexManager;
    protected DataManager dataManager;
    protected Map<Object, StoreEntry> map = new ConcurrentHashMap<Object, StoreEntry>();
    protected LinkedList<StoreEntry> list = new LinkedList<StoreEntry>();

    IndexRootContainer(IndexItem root, IndexManager im, DataManager dfm) throws IOException {
        this.root = root;
        this.indexManager = im;
        this.dataManager = dfm;
        long nextItem = root.getNextItem();
        while (nextItem != Item.POSITION_NOT_SET) {
            StoreEntry item = indexManager.getIndex(nextItem);
            StoreLocation data = item.getKeyDataItem();
            Object key = dataManager.readItem(ROOT_MARSHALLER, data);
            map.put(key, item);
            list.add(item);
            nextItem = item.getNextItem();
            dataManager.addInterestInFile(item.getKeyFile());
        }
    }

    Set<Object> getKeys() {
        return map.keySet();
    }

    IndexItem addRoot(IndexManager containerIndexManager, ContainerId key) throws IOException {
        if (map.containsKey(key)) {
            removeRoot(containerIndexManager, key);
        }

        StoreLocation data = dataManager.storeDataItem(ROOT_MARSHALLER, key);
        IndexItem newRoot = indexManager.createNewIndex();
        newRoot.setKeyData(data);
        IndexItem containerRoot = containerIndexManager.createNewIndex();
        containerIndexManager.storeIndex(containerRoot);
        newRoot.setValueOffset(containerRoot.getOffset());

        IndexItem last = list.isEmpty() ? null : (IndexItem)list.getLast();
        last = last == null ? root : last;
        long prev = last.getOffset();
        newRoot.setPreviousItem(prev);
        indexManager.storeIndex(newRoot);
        last.setNextItem(newRoot.getOffset());
        indexManager.storeIndex(last);
        map.put(key, newRoot);
        list.add(newRoot);
        return containerRoot;
    }

    void removeRoot(IndexManager containerIndexManager, ContainerId key) throws IOException {
        StoreEntry oldRoot = map.remove(key);
        if (oldRoot != null) {
            dataManager.removeInterestInFile(oldRoot.getKeyFile());
            // get the container root
            IndexItem containerRoot = containerIndexManager.getIndex(oldRoot.getValueOffset());
            if (containerRoot != null) {
                containerIndexManager.freeIndex(containerRoot);
            }
            int index = list.indexOf(oldRoot);
            IndexItem prev = index > 0 ? (IndexItem)list.get(index - 1) : root;
            prev = prev == null ? root : prev;
            IndexItem next = index < (list.size() - 1) ? (IndexItem)list.get(index + 1) : null;
            if (next != null) {
                prev.setNextItem(next.getOffset());
                next.setPreviousItem(prev.getOffset());
                indexManager.updateIndexes(next);
            } else {
                prev.setNextItem(Item.POSITION_NOT_SET);
            }
            indexManager.updateIndexes(prev);
            list.remove(oldRoot);
            indexManager.freeIndex((IndexItem)oldRoot);
        }
    }

    IndexItem getRoot(IndexManager containerIndexManager, ContainerId key) throws IOException {
        StoreEntry index = map.get(key);
        if (index != null) {
            return containerIndexManager.getIndex(index.getValueOffset());
        }
        return null;
    }

    boolean doesRootExist(Object key) {
        return map.containsKey(key);
    }

}
