/*
 * 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.clerezza.platform.typepriority;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.locks.Lock;

import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.References;
import org.apache.felix.scr.annotations.Service;
import org.osgi.service.component.ComponentContext;
import org.apache.clerezza.platform.config.SystemConfig;
import org.apache.clerezza.rdf.core.MGraph;
import org.apache.clerezza.rdf.core.Resource;
import org.apache.clerezza.rdf.core.UriRef;
import org.apache.clerezza.rdf.core.access.LockableMGraph;
import org.apache.clerezza.rdf.ontologies.RDFS;
import org.apache.clerezza.rdf.utils.RdfList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author rbn
 */
@Component
@Service(value=TypePrioritizer.class)
@References({
    @Reference(name="systemGraph",
        cardinality=ReferenceCardinality.MANDATORY_UNARY,
        referenceInterface=LockableMGraph.class,
        target=SystemConfig.SYSTEM_GRAPH_FILTER)})
public class TypePrioritizer {
    public static final UriRef typePriorityListUri = new UriRef("urn:x-localinstance:/typePriorityList");

    private List<UriRef> typePriorityList;
    private static final Logger log = LoggerFactory.getLogger(TypePrioritizer.class);
    
    LockableMGraph systemGraph;

    protected void bindSystemGraph(LockableMGraph systemGraph) {
        Lock l = systemGraph.getLock().readLock();
        l.lock();
        try {
            List<Resource> rdfTypePriorityList = new RdfList(
                 typePriorityListUri, systemGraph);
            typePriorityList  = new ArrayList<UriRef>(rdfTypePriorityList.size());
            for (Resource resource : rdfTypePriorityList) {
                if (resource instanceof UriRef) {
                    typePriorityList.add((UriRef) resource);
                } else {
                    log.warn("Type priority list contains a resource "
                            + "that is not a uri, skipping.");
                }
            }
        } finally {
            l.unlock();
        }
        this.systemGraph = (LockableMGraph) systemGraph;
    }

    protected void unbindSystemGraph(LockableMGraph systemGraph) {
        typePriorityList = null;
        this.systemGraph = null;
    }

    /**
     *
     * @param rdfTypes the rdf types to be sorted
     * @return a sorted iterator of the types
     */
    public Iterator<UriRef> iterate(final Collection<UriRef> rdfTypes) {
        return new Iterator<UriRef>() {
            final Set<UriRef> remaining = new HashSet<UriRef>(rdfTypes);
            boolean rdfsResourceRemovedAndNotYetReturned = remaining.remove(RDFS.Resource);
            final Iterator<UriRef> typePriorityIter = typePriorityList.iterator();
            Iterator<UriRef> remainingIter = null;
            UriRef next = prepareNext();
            
            private UriRef prepareNext() {
                while (typePriorityIter.hasNext()) {
                    UriRef nextPriority = typePriorityIter.next();
                    if (remaining.contains(nextPriority)) {
                        remaining.remove(nextPriority);
                        return nextPriority;
                    }
                }
                if (remainingIter == null) {
                    remainingIter = remaining.iterator();
                }
                if (remainingIter.hasNext()) {
                    return remainingIter.next();
                } else {
                    if (rdfsResourceRemovedAndNotYetReturned) {
                        rdfsResourceRemovedAndNotYetReturned = false;
                        return RDFS.Resource;
                    } else {
                        return null;
                    }
                }
            }

            @Override
            public boolean hasNext() {
                return next != null;
            }

            @Override
            public UriRef next() {
                if (next == null) {
                    throw new NoSuchElementException();
                }
                UriRef current = next;
                next = prepareNext();
                return current;
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException("Not supported yet.");
            }
        };
    }
}
