/*
 * Copyright (C) 2005-2010 Alfresco Software Limited.
 *
 * This file is part of Alfresco
 *
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Alfresco 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see .
 */
package org.alfresco.opencmis.search;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.alfresco.opencmis.dictionary.CMISDictionaryService;
import org.alfresco.opencmis.dictionary.CMISNodeInfo;
import org.alfresco.repo.search.impl.querymodel.Query;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.search.LimitBy;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.ResultSetRow;
import org.alfresco.service.cmr.search.ResultSetSPI;
import org.alfresco.service.cmr.search.SpellCheckResult;
import org.alfresco.util.Pair;
/**
 * @author andyh
 */
public class CMISResultSet implements ResultSetSPI, Serializable
{
    private static final long serialVersionUID = 2014688399588268994L;
    private Map wrapped;
    
    private Map nodeInfos;
    private LimitBy limitBy;
    CMISQueryOptions options;
    NodeService nodeService;
    Query query;
    CMISDictionaryService cmisDictionaryService;
    DictionaryService alfrescoDictionaryService;
    public CMISResultSet(Map wrapped, CMISQueryOptions options, LimitBy limitBy,
            NodeService nodeService, Query query, CMISDictionaryService cmisDictionaryService,
            DictionaryService alfrescoDictionaryService)
    {
        this.wrapped = wrapped;
        this.options = options;
        this.limitBy = limitBy;
        this.nodeService = nodeService;
        this.query = query;
        this.cmisDictionaryService = cmisDictionaryService;
        this.alfrescoDictionaryService = alfrescoDictionaryService;
        this.nodeInfos = new HashMap();
    }
    /*
     * (non-Javadoc)
     * 
     * @see org.alfresco.cmis.search.CMISResultSet#close()
     */
    public void close()
    {
        // results sets can be used for more than one selector so we need to
        // keep track of what we have closed
        Set closed = new HashSet();
        for (ResultSet resultSet : wrapped.values())
        {
            if (!closed.contains(resultSet))
            {
                resultSet.close();
                closed.add(resultSet);
            }
        }
    }
    /*
     * (non-Javadoc)
     * 
     * @see org.alfresco.cmis.search.CMISResultSet#getMetaData()
     */
    public CMISResultSetMetaData getMetaData()
    {
        return new CMISResultSetMetaData(options, query, limitBy, cmisDictionaryService, alfrescoDictionaryService);
    }
    /*
     * (non-Javadoc)
     * 
     * @see org.alfresco.cmis.search.CMISResultSet#getRow(int)
     */
    public CMISResultSetRow getRow(int i)
    {
        return new CMISResultSetRow(this, i, getScores(i), nodeService, getNodeRefs(i), nodeInfos, query, cmisDictionaryService);
    }
    /*
     * (non-Javadoc)
     * 
     * @see org.alfresco.cmis.search.CMISResultSet#hasMore()
     */
    public boolean hasMore()
    {
        for (ResultSet resultSet : wrapped.values())
        {
            if (resultSet.hasMore())
            {
                return true;
            }
        }
        return false;
    }
    /*
     * (non-Javadoc)
     * 
     * @see org.alfresco.cmis.search.CMISResultSet#length()
     */
    public int getLength()
    {
        for (ResultSet resultSet : wrapped.values())
        {
            return resultSet.length();
        }
        throw new IllegalStateException();
    }
    /*
     * (non-Javadoc)
     * 
     * @see org.alfresco.cmis.search.CMISResultSet#start()
     */
    public int getStart()
    {
        return options.getSkipCount();
    }
    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Iterable#iterator()
     */
    public Iterator iterator()
    {
        return new CMISResultSetRowIterator(this);
    }
    private Map getNodeRefs(int i)
    {
        HashMap refs = new HashMap();
        for (String selector : wrapped.keySet())
        {
            ResultSet rs = wrapped.get(selector);
            refs.put(selector, rs.getNodeRef(i));
        }
        return refs;
    }
    private Map getScores(int i)
    {
        HashMap scores = new HashMap();
        for (String selector : wrapped.keySet())
        {
            ResultSet rs = wrapped.get(selector);
            scores.put(selector, Float.valueOf(rs.getScore(i)));
        }
        return scores;
    }
    public ChildAssociationRef getChildAssocRef(int n)
    {
        NodeRef nodeRef = getNodeRef(n);
        return nodeService.getPrimaryParent(nodeRef);
    }
    public List getChildAssocRefs()
    {
        ArrayList cars = new ArrayList(length());
        for (ResultSetRow row : this)
        {
            cars.add(row.getChildAssocRef());
        }
        return cars;
    }
    public NodeRef getNodeRef(int n)
    {
        Map refs = getNodeRefs(n);
        if (refs.size() == 1)
        {
            return refs.values().iterator().next();
        } else if (allNodeRefsEqual(refs))
        {
            return refs.values().iterator().next();
        } else
        {
            throw new IllegalStateException("Ambiguous selector");
        }
    }
    private boolean allNodeRefsEqual(Map selected)
    {
        NodeRef last = null;
        for (NodeRef current : selected.values())
        {
            if (last == null)
            {
                last = current;
            } else
            {
                if (!last.equals(current))
                {
                    return false;
                }
            }
        }
        return true;
    }
    public List getNodeRefs()
    {
        ArrayList nodeRefs = new ArrayList(length());
        for (ResultSetRow row : this)
        {
            nodeRefs.add(row.getNodeRef());
        }
        return nodeRefs;
    }
    public CMISResultSetMetaData getResultSetMetaData()
    {
        return getMetaData();
    }
    public float getScore(int n)
    {
        Map scores = getScores(n);
        if (scores.size() == 1)
        {
            return scores.values().iterator().next();
        } else if (allScoresEqual(scores))
        {
            return scores.values().iterator().next();
        } else
        {
            throw new IllegalStateException("Ambiguous selector");
        }
    }
    private boolean allScoresEqual(Map scores)
    {
        Float last = null;
        for (Float current : scores.values())
        {
            if (last == null)
            {
                last = current;
            } else
            {
                if (!last.equals(current))
                {
                    return false;
                }
            }
        }
        return true;
    }
    public int length()
    {
        return getLength();
    }
    /**
     * Bulk fetch results in the cache - not supported here
     * 
     * @param bulkFetch boolean
     */
    public boolean setBulkFetch(boolean bulkFetch)
    {
        return false;
    }
    /**
     * Do we bulk fetch - not supported here
     * 
     * @return - true if we do
     */
    public boolean getBulkFetch()
    {
        return false;
    }
    /**
     * Set the bulk fetch size
     * 
     * @param bulkFetchSize int
     */
    public int setBulkFetchSize(int bulkFetchSize)
    {
        return 0;
    }
    /**
     * Get the bulk fetch size.
     * 
     * @return the fetch size
     */
    public int getBulkFetchSize()
    {
        return 0;
    }
    
    @Override
    public List> getFieldFacet(String field)
    {
        return Collections.>emptyList();
    }
    /* (non-Javadoc)
     * @see org.alfresco.service.cmr.search.ResultSetSPI#getNumberFound()
     */
    @Override
    public long getNumberFound()
    {
        for (ResultSet resultSet : wrapped.values())
        {
            return resultSet.getNumberFound();
        }
        throw new IllegalStateException();
    }
    @Override
    public Map getFacetQueries()
    {
        return Collections.emptyMap();
    }
    
    @Override
    public SpellCheckResult getSpellCheckResult()
    {
        return new SpellCheckResult(null, null, false);
    }
}