package com.github.davidmoten.rx.jdbc;

import com.github.davidmoten.util.Preconditions;

import rx.Observable;

/**
 * Builds base information for a query (either select or update).
 */
final class QueryBuilder {

    /**
     * JDBC sql either select/update/insert.
     */
    private String sql;

    /**
     * Parameters for the query corresponding to ? characters in the sql.
     */
    private Observable<Parameter> parameters = Observable.empty();

    /**
     * Observables to complete before the query is executed.
     */
    private Observable<?> depends = Observable.empty();

    /**
     * {@link Database} to use the query against.
     */
    private final Database db;

    /**
     * Execution context to use to run the query.
     */
    private final QueryContext context;

    /**
     * Constructor.
     * 
     * @param sql
     * @param db
     */
    QueryBuilder(String sql, Database db) {
        this.sql = sql;
        this.db = db;
        this.context = db.queryContext();
    }

    /**
     * Appends the given parameters to the parameter list for the query. If
     * there are more parameters than required for one execution of the query
     * then more than one execution of the query will occur.
     * 
     * @param params
     */
    <T> void parameters(Observable<T> params) {
        this.parameters = Observable.concat(parameters, params.map(Parameter.TO_PARAMETER));
    }

    /**
     * Appends the given parameter values to the parameter list for the query.
     * If there are more parameters than required for one execution of the query
     * then more than one execution of the query will occur.
     * 
     * @param objects
     */
    void parameters(Object... objects) {
        for (Object object : objects)
            parameter(object);
    }

    /**
     * Appends a parameter to the parameter list for the query. If there are
     * more parameters than required for one execution of the query then more
     * than one execution of the query will occur.
     * 
     * @param value
     */
    void parameter(Object value) {
        // TODO check on supported types?
        if (value instanceof Observable)
            throw new RuntimeException(
                    "use parameters() method not the parameter() method for an Observable");
        parameters(Observable.just(value));
    }

    /**
     * Sets a named parameter. If name is null throws a
     * {@link NullPointerException}. If value is instance of Observable then
     * throws an {@link IllegalArgumentException}.
     * 
     * @param name
     *            the parameter name. Cannot be null.
     * @param value
     *            the parameter value
     */
    void parameter(String name, Object value) {
        Preconditions.checkNotNull(name, "parameter name cannot be null");
        if (value instanceof Observable)
            throw new IllegalArgumentException(
                    "use parameters() method not the parameter() method for an Observable");
        this.parameters = parameters.concatWith(Observable.just(new Parameter(name, value)));
    }

    /**
     * Appends a dependency to the dependencies that have to complete their
     * emitting before the query is executed.
     * 
     * @param dependency
     */
    void dependsOn(Observable<?> dependency) {
        depends = Observable.concat(depends, dependency);
    }

    /**
     * Appends a dependency on the result of the last transaction (
     * <code>true</code> for commit or <code>false</code> for rollback) to the
     * dependencies that have to complete their emitting before the query is
     * executed.
     */
    void dependsOnLastTransaction() {
        dependsOn(db.lastTransactionResult());
    }

    /**
     * Returns the sql of the query.
     * 
     * @return sql
     */
    String sql() {
        return sql;
    }

    /**
     * Returns the parameters for the query.
     * 
     * @return parameters
     */
    Observable<Parameter> parameters() {
        return parameters;
    }

    /**
     * Returns the dependencies of the query.
     * 
     * @return dependencies
     */
    public Observable<?> depends() {
        return depends;
    }

    /**
     * Returns the query's {@link QueryContext}.
     * 
     * @return context
     */
    QueryContext context() {
        return context;
    }

    void clearParameters() {
        this.parameters = Observable.empty();
    }

    void setSql(String sql) {
        this.sql = sql;
    }
}

@@ -2195,15 +2195,23 @@
 new 
-Runtime
+IllegalArgument
 Exce

throw IllegalArgumentException in QueryBuilder instead of RuntimeException