/* $This file is distributed under the terms of the license in LICENSE$ */

package edu.cornell.mannlib.vitro.webapp.dao.filtering;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import net.sf.jga.algorithms.Filter;
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
import edu.cornell.mannlib.vitro.webapp.beans.ObjectProperty;
import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatement;
import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatementImpl;
import edu.cornell.mannlib.vitro.webapp.dao.ObjectPropertyStatementDao;
import edu.cornell.mannlib.vitro.webapp.dao.filtering.filters.VitroFilters;

class ObjectPropertyStatementDaoFiltering extends BaseFiltering implements ObjectPropertyStatementDao{
    final ObjectPropertyStatementDao innerObjectPropertyStatementDao;
    final VitroFilters filters;

    public ObjectPropertyStatementDaoFiltering(
            ObjectPropertyStatementDao objectPropertyStatementDao,
            VitroFilters filters) {
        super();
        this.innerObjectPropertyStatementDao = objectPropertyStatementDao;
        this.filters = filters;
    }


    @Override
    public void deleteObjectPropertyStatement(ObjectPropertyStatement objPropertyStmt) {
        innerObjectPropertyStatementDao.deleteObjectPropertyStatement(objPropertyStmt);
    }


    private List<ObjectPropertyStatement> filterAndWrapList(List<ObjectPropertyStatement> list){
        if( ( list ) != null ){
            innerObjectPropertyStatementDao.resolveAsFauxPropertyStatements(list);

            ArrayList<ObjectPropertyStatement> ctemp = new ArrayList<ObjectPropertyStatement>();
            Filter.filter(list,filters.getObjectPropertyStatementFilter(),ctemp);

            List<ObjectPropertyStatement> cout= new ArrayList<ObjectPropertyStatement>(list.size());
            for( ObjectPropertyStatement stmt: ctemp){
                cout.add( new ObjectPropertyStatementFiltering(stmt,filters) );
            }
            return cout;
        }else{
            return null;
        }
    }

    @Override
    public Individual fillExistingObjectPropertyStatements(Individual entity) {
        Individual ind = innerObjectPropertyStatementDao.fillExistingObjectPropertyStatements(entity);
        if( ind == null )
            return null;
        else{
            ind.setObjectPropertyStatements( filterAndWrapList( ind.getObjectPropertyStatements()) );
            return ind;
        }
    }

    @Override
    public List<ObjectPropertyStatement> getObjectPropertyStatements(ObjectProperty objectProperty) {
    	return filterAndWrapList( innerObjectPropertyStatementDao.getObjectPropertyStatements(objectProperty) );
    }

    @Override
    public List<ObjectPropertyStatement> getObjectPropertyStatements(ObjectProperty objectProperty, int startIndex, int endIndex) {
    	return filterAndWrapList( innerObjectPropertyStatementDao.getObjectPropertyStatements(objectProperty, startIndex, endIndex));
    }

    @Override
	public List<ObjectPropertyStatement> getObjectPropertyStatements(
			ObjectPropertyStatement objPropertyStmt) {
    	return filterAndWrapList(innerObjectPropertyStatementDao.getObjectPropertyStatements(objPropertyStmt));
	}

    @Override
	public int insertNewObjectPropertyStatement(ObjectPropertyStatement objPropertyStmt) {
        return innerObjectPropertyStatementDao.insertNewObjectPropertyStatement(objPropertyStmt);
    }

    @Override
    public List<Map<String, String>> getObjectPropertyStatementsForIndividualByProperty(
            String subjectUri, String propertyUri, String objectKey, String domainUri,
            String rangeUri, String query, Set<String> queryStrings, String sortDirection) {

        List<Map<String, String>> data =
        	innerObjectPropertyStatementDao.getObjectPropertyStatementsForIndividualByProperty(
        			subjectUri, propertyUri, objectKey, domainUri, rangeUri, query,
        			        queryStrings,sortDirection);

        /* Filter the data
         *
         * Filtering is applied to a list of ObjectPropertyStatements. Create these statements, mapped
         * to the item in data that they are built from, apply filtering to the statements, then get
         * the associated data out of the original list. Use a LinkedHashMap to preserve the ordering.
         */
        Map<ObjectPropertyStatement, Map<String, String>> stmtsToData =
            new LinkedHashMap<ObjectPropertyStatement, Map<String, String>>(data.size());

        for (Map<String, String> map : data) {
            String objectUri = map.get(objectKey);
            ObjectPropertyStatement statement = new ObjectPropertyStatementImpl(subjectUri, propertyUri, objectUri);
            ObjectProperty op = new ObjectProperty();
            op.setURI(propertyUri);
            op.setDomainVClassURI(domainUri);
            op.setRangeVClassURI(rangeUri);
            statement.setProperty(op);
            stmtsToData.put(statement, map);
        }

        List<ObjectPropertyStatement> stmtList = new ArrayList<ObjectPropertyStatement>(stmtsToData.keySet());

        // Apply the filters to the list of statements
        List<ObjectPropertyStatement> filteredStatements = filterAndWrapList(stmtList);

        // Get the data associated with the filtered statements out of the map
        List<Map<String, String>> filteredData = new ArrayList<Map<String, String>>(filteredStatements.size());
        for (ObjectPropertyStatement ops : filteredStatements) {
            if (ops instanceof ObjectPropertyStatementFiltering) {
                ops = ((ObjectPropertyStatementFiltering)ops).innerStmt;
            }
            filteredData.add(stmtsToData.get(ops));
        }

        // Return the filtered list of data
        return filteredData;

    }

    @Override
    public Map<String, String> getMostSpecificTypesInClassgroupsForIndividual(String subjectUri) {
        return innerObjectPropertyStatementDao.getMostSpecificTypesInClassgroupsForIndividual(subjectUri);
    }


	@Override
	public void resolveAsFauxPropertyStatements(List<ObjectPropertyStatement> list) {
		innerObjectPropertyStatementDao.resolveAsFauxPropertyStatements(list);
	}

}
