/*
 * JBoss, Home of Professional Open Source.
 * See the COPYRIGHT.txt file distributed with this work for information
 * regarding copyright ownership.  Some portions may be licensed
 * to Red Hat, Inc. under one or more contributor license agreements.
 * 
 * This library 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 2.1 of the License, or (at your option) any later version.
 * 
 * This library 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 this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA.
 * Modified by
 * @author  Data Virtuality GmbH
 * @date    $Date$
 *  
 */
// https://issues.jboss.org/browse/TEIID-2153
// https://issues.jboss.org/browse/TEIID-2171
// https://issues.jboss.org/browse/TEIID-2359
// https://issues.jboss.org/browse/TEIID-2149
// https://issues.jboss.org/browse/TEIID-2358
// https://issues.jboss.org/browse/TEIID-2410
// https://issues.jboss.org/browse/TEIID-2271
// https://issues.jboss.org/browse/TEIID-2467
// https://issues.jboss.org/browse/TEIID-2429
// https://issues.jboss.org/browse/TEIID-2363
// https://issues.jboss.org/browse/TEIID-2160
// https://issues.jboss.org/browse/TEIID-2461
// https://issues.jboss.org/browse/TEIID-2797
// https://issues.jboss.org/browse/TEIID-2326
// https://issues.jboss.org/browse/TEIID-1557
// https://issues.jboss.org/browse/TEIID-2626
// https://issues.jboss.org/browse/TEIID-2707
// https://issues.jboss.org/browse/TEIID-2900
// https://issues.jboss.org/browse/TEIID-2902
// https://issues.jboss.org/browse/TEIID-2457
// https://issues.jboss.org/browse/TEIID-2942
// https://github.com/teiid/teiid/commit/f840ed8283aad9cbe8d3019467a88d951d695659
// https://issues.jboss.org/browse/TEIID-2482
// https://issues.jboss.org/browse/TEIID-2346
// https://issues.jboss.org/browse/TEIID-2430
// https://issues.jboss.org/browse/TEIID-2731
// https://issues.jboss.org/browse/TEIID-2747
// https://issues.jboss.org/browse/TEIID-2946
// https://issues.jboss.org/browse/TEIID-2390
package org.teiid.query.processor.relational;

import java.util.ArrayList;
import java.util.List;

import org.teiid.common.buffer.BufferManager;
import org.teiid.common.buffer.BufferManager.TupleSourceType;
import org.teiid.common.buffer.TupleBuffer;
import org.teiid.common.buffer.TupleSource;
import org.teiid.core.TeiidComponentException;
import org.teiid.core.TeiidProcessingException;
import org.teiid.query.function.aggregate.AggregateFunction;
import org.teiid.query.processor.relational.SortUtility.Mode;
import org.teiid.query.sql.lang.OrderByItem;
import org.teiid.query.sql.symbol.ElementSymbol;
import org.teiid.query.util.CommandContext;

/**
 */
public class SortingFilter extends AggregateFunction {

	// Initial setup - can be reused
    private AggregateFunction proxy;
    private BufferManager mgr;
    private String groupName;
    private boolean removeDuplicates;

    // Derived and static - can be reused
    private List<ElementSymbol> elements;
    private List<OrderByItem> sortItems;
    
    // Temporary state - should be reset
    private TupleBuffer collectionBuffer;
    private SortUtility sortUtility;

    /**
     * Constructor for DuplicateFilter.
     */
    public SortingFilter(AggregateFunction proxy, BufferManager mgr, String groupName, boolean removeDuplicates) {
        super();

        this.proxy = proxy;
        this.mgr = mgr;
        this.groupName = groupName;
        this.removeDuplicates = removeDuplicates;
    }
    
    public List<ElementSymbol> getElements() {
		return elements;
	}
    
	public void setElements(List<ElementSymbol> elements) {
		this.elements = elements;
	}
    
    public void setSortItems(List<OrderByItem> sortItems) {
		this.sortItems = sortItems;
	}
    
    @Override
    public void initialize(java.lang.Class<?> dataType, java.lang.Class<?>[] inputTypes) {
    	this.proxy.initialize(dataType, inputTypes);
    }

    public void reset() {
        this.proxy.reset();
        close();
    }

	private void close() {
		if (this.collectionBuffer != null) {
        	collectionBuffer.remove();
            this.collectionBuffer = null;
        }
		if (this.sortUtility != null) {
			sortUtility.remove();
	        this.sortUtility = null;
		}
	}
	
	@Override
	public void addInputDirect(List<?> tuple, CommandContext commandContext)
			throws TeiidComponentException, TeiidProcessingException {
        if(collectionBuffer == null) {
            collectionBuffer = mgr.createTupleBuffer(elements, groupName, TupleSourceType.PROCESSOR);
        }
        List<Object> row = new ArrayList<Object>(argIndexes.length);
        //TODO remove overlap
        for (int i = 0; i < argIndexes.length; i++) {
			row.add(tuple.get(argIndexes[i]));
		}
        if (!this.proxy.filter(row)) {
            this.collectionBuffer.addTuple(row);
        }
	}
	
    /**
     * @throws TeiidProcessingException 
     * @see org.teiid.query.function.aggregate.AggregateFunction#getResult(CommandContext)
     */
    public Object getResult(CommandContext commandContext)
        throws TeiidComponentException, TeiidProcessingException {

        if(collectionBuffer != null) {
            this.collectionBuffer.close();

            // Sort
            if (sortUtility == null) {
            	sortUtility = new SortUtility(null, sortItems, removeDuplicates?Mode.DUP_REMOVE_SORT:Mode.SORT, mgr, groupName, collectionBuffer.getSchema());
            	collectionBuffer.setForwardOnly(true);
            	this.sortUtility.setWorkingBuffer(collectionBuffer);
            }
            TupleBuffer sorted = sortUtility.sort();
            sorted.setForwardOnly(true);
            try {
	            // Add all input to proxy
	            TupleSource sortedSource = sorted.createIndexedTupleSource();
	            while(true) {
	                List<?> tuple = sortedSource.nextTuple();
	                if(tuple == null) {
	                    break;
	                }
	                //TODO should possibly remove the order by columns from this tuple
	                this.proxy.addInputDirect(tuple, commandContext);
	            }
            } finally {
            	sorted.remove();
            }
            
            close();
        }

        // Return
        return this.proxy.getResult(commandContext);
    }
    
    public boolean respectsNull() {
    	return true;
    }
}
