/*
 * Copyright (C) 2004-2006  exedio GmbH (www.exedio.com)
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package com.exedio.cope;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import com.exedio.cope.search.GreaterCondition;
import com.exedio.cope.search.GreaterEqualCondition;
import com.exedio.cope.search.LessCondition;
import com.exedio.cope.search.LessEqualCondition;
import com.exedio.cope.search.OrCondition;

/**
 * A <tt>view</tt> represents a value computed from the
 * attributes of a {@link Type}.
 * The computation is available both in Java and SQL,
 * so you can use views in search conditions.
 * <p>
 * For an overview here is a
 * <a href="functions.png">UML diagram</a>.
 * 
 * @author Ralf Wiebicke
 */
public abstract class View<E> extends Feature implements Function<E>
{
	private final Function<?>[] sources;
	private final List<Function<?>> sourceList;
	// TODO SOON rename to name
	private final String functionName;
	final int jdbcType;
	final Type<? extends Item> sourceType;

	public View(final Function<?>[] sources, final String functionName, final int jdbcType)
	{
		this.sources = sources;
		this.sourceList = Collections.unmodifiableList(Arrays.asList(sources));
		this.functionName = functionName;
		this.jdbcType = jdbcType;
		
		Type<? extends Item> sourceType;
		try
		{
			sourceType = sources[0].getType();
		}
		catch(FeatureNotInitializedException e)
		{
			sourceType = null;
		}
		this.sourceType = sourceType;
	}
	
	public final List<Function<?>> getSources()
	{
		return sourceList;
	}

	protected abstract Object mapJava(Object[] sourceValues);
	
	abstract Object load(ResultSet resultSet, int columnIndex) throws SQLException;

	abstract String surface2Database(Object value);
	
	abstract void surface2DatabasePrepared(Statement bf, Object value);
	
	public final Object getObject(final Item item)
	{
		final List sources = getSources();
		final Object[] values = new Object[sources.size()];
		int pos = 0;
		for(Iterator i = sources.iterator(); i.hasNext(); )
			values[pos++] = ((Function)i.next()).get(item);
	
		return mapJava(values);
	}
	
	public final void appendParameter(final Statement bf, final E value)
	{
		if(bf.parameters==null)
			bf.append(surface2Database(value));
		else
			surface2DatabasePrepared(bf, value);
	}
	
	final String toStringNonInitialized()
	{
		final StringBuffer buf = new StringBuffer(functionName);
		buf.append('(');
		for(int i = 0; i<sources.length; i++)
		{
			if(i>0)
				buf.append(',');
			buf.append(sources[i].toString());
		}
		buf.append(')');
		
		return buf.toString();
	}
	
	public boolean equals(final Object other)
	{
		if(!(other instanceof View))
			return false;
		
		final View o = (View)other;
		
		if(!functionName.equals(o.functionName) || sources.length!=o.sources.length)
			return false;
		
		for(int i = 0; i<sources.length; i++)
		{
			if(!sources[i].equals(o.sources[i]))
				return false;
		}

		return true;
	}
	
	public int hashCode()
	{
		int result = functionName.hashCode();
		
		for(int i = 0; i<sources.length; i++)
			result = (31*result) + sources[i].hashCode(); // may not be commutative

		return result;
	}


	// second initialization phase ---------------------------------------------------

	@Override
	final void initialize(final Type<? extends Item> type, final String name)
	{
		if(sourceType!=null && type!=sourceType)
			throw new RuntimeException();
			
		super.initialize(type, name);
	}
	
	public final Type<? extends Item> getType()
	{
		return (sourceType!=null) ? sourceType : super.getType();
	}
	
	// convenience methods for conditions and views ---------------------------------

	public final EqualCondition equal(final E value)
	{
		return new EqualCondition<E>(this, value);
	}
	
	public final EqualCondition<E> equal(final Join join, final E value)
	{
		return new EqualCondition<E>(new JoinedFunction<E>(this, join), value);
	}
	
	public final OrCondition in(final Collection<E> values)
	{
		return OrCondition.in(this, values);
	}
	
	public final NotEqualCondition notEqual(final E value)
	{
		return new NotEqualCondition<E>(this, value);
	}

	public final EqualFunctionCondition equal(final Function<E> right)
	{
		return new EqualFunctionCondition(this, right);
	}
	
	public final LessCondition less(final E value)
	{
		return new LessCondition<E>(this, value);
	}
	
	public final LessEqualCondition lessOrEqual(final E value)
	{
		return new LessEqualCondition<E>(this, value);
	}
	
	public final GreaterCondition greater(final E value)
	{
		return new GreaterCondition<E>(this, value);
	}
	
	public final GreaterEqualCondition greaterOrEqual(final E value)
	{
		return new GreaterEqualCondition<E>(this, value);
	}
}

@@ -1719,37 +1719,8 @@
 st;%0A
-%09// TODO SOON rename to name%0A
 %09pri
@@ -1737,25 +1737,17 @@
  String 
-functionN
+n
 ame;%0A%09fi
@@ -1860,25 +1860,17 @@
  String 
-functionN
+n
 ame, fin
@@ -2000,32 +2000,16 @@
 his.
-functionName = functionN
+name = n
 ame;
@@ -3145,25 +3145,17 @@
 gBuffer(
-functionN
+n
 ame);%0A%09%09
@@ -3477,25 +3477,17 @@
 %09%0A%09%09if(!
-functionN
+n
 ame.equa
@@ -3491,25 +3491,17 @@
 quals(o.
-functionN
+n
 ame) %7C%7C 
@@ -3726,17 +3726,9 @@
 t = 
-functionN
+n
 ame.

rename functionName to name