/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.datastax.driver.core;

import com.datastax.driver.core.exceptions.InvalidTypeException;
import com.datastax.driver.core.policies.RetryPolicy;
import java.nio.ByteBuffer;
import java.util.Map;

/**
 * Represents a prepared statement, a query with bound variables that has been prepared (pre-parsed)
 * by the database.
 *
 * <p>A prepared statement can be executed once concrete values have been provided for the bound
 * variables. A prepared statement and the values for its bound variables constitute a
 * BoundStatement and can be executed (by {@link Session#execute}).
 *
 * <p>A {@code PreparedStatement} object allows you to define specific defaults for the different
 * properties of a {@link Statement} (Consistency level, tracing, ...), in which case those
 * properties will be inherited as default by every BoundedStatement created from the
 * {PreparedStatement}. The default for those {@code PreparedStatement} properties is the same that
 * in {@link Statement} if the PreparedStatement is created by {@link Session#prepare(String)} but
 * will inherit of the properties of the {@link RegularStatement} used for the preparation if {@link
 * Session#prepare(RegularStatement)} is used.
 */
public interface PreparedStatement {

  /**
   * Returns metadata on the bounded variables of this prepared statement.
   *
   * @return the variables bounded in this prepared statement.
   */
  public ColumnDefinitions getVariables();

  /**
   * Creates a new BoundStatement object and bind its variables to the provided values.
   *
   * <p>While the number of {@code values} cannot be greater than the number of bound variables, the
   * number of {@code values} may be fewer than the number of bound variables. In that case, the
   * remaining variables will have to be bound to values by another mean because the resulting
   * {@code BoundStatement} being executable.
   *
   * <p>This method is a convenience for {@code new BoundStatement(this).bind(...)}.
   *
   * @param values the values to bind to the variables of the newly created BoundStatement.
   * @return the newly created {@code BoundStatement} with its variables bound to {@code values}.
   * @throws IllegalArgumentException if more {@code values} are provided than there is of bound
   *     variables in this statement.
   * @throws InvalidTypeException if any of the provided value is not of correct type to be bound to
   *     the corresponding bind variable.
   * @throws NullPointerException if one of {@code values} is a collection (List, Set or Map)
   *     containing a null value. Nulls are not supported in collections by CQL.
   * @see BoundStatement#bind
   */
  public BoundStatement bind(Object... values);

  /**
   * Creates a new BoundStatement object for this prepared statement.
   *
   * <p>This method do not bind any values to any of the prepared variables. Said values need to be
   * bound on the resulting statement using BoundStatement's setters methods ({@link
   * BoundStatement#setInt}, {@link BoundStatement#setLong}, ...).
   *
   * @return the newly created {@code BoundStatement}.
   */
  public BoundStatement bind();

  /**
   * Sets the routing key for this prepared statement.
   *
   * <p>While you can provide a fixed routing key for all executions of this prepared statement with
   * this method, it is not mandatory to provide one through this method. This method should only be
   * used if the partition key of the prepared query is not part of the prepared variables (that is
   * if the partition key is fixed).
   *
   * <p>Note that if the partition key is part of the prepared variables, the routing key will be
   * automatically computed once those variables are bound.
   *
   * <p>If the partition key is neither fixed nor part of the prepared variables (e.g. a composite
   * partition key where only some of the components are bound), the routing key can also be set on
   * each bound statement.
   *
   * @param routingKey the raw (binary) value to use as routing key.
   * @return this {@code PreparedStatement} object.
   * @see Statement#getRoutingKey
   * @see BoundStatement#getRoutingKey
   */
  public PreparedStatement setRoutingKey(ByteBuffer routingKey);

  /**
   * Sets the routing key for this query.
   *
   * <p>See {@link #setRoutingKey(ByteBuffer)} for more information. This method is a variant for
   * when the query partition key is composite and the routing key must be built from multiple
   * values.
   *
   * @param routingKeyComponents the raw (binary) values to compose to obtain the routing key.
   * @return this {@code PreparedStatement} object.
   * @see Statement#getRoutingKey
   */
  public PreparedStatement setRoutingKey(ByteBuffer... routingKeyComponents);

  /**
   * Returns the routing key set for this query.
   *
   * @return the routing key for this query or {@code null} if none has been explicitly set on this
   *     PreparedStatement.
   */
  public ByteBuffer getRoutingKey();

  /**
   * Sets a default consistency level for all bound statements created from this prepared statement.
   *
   * <p>If no consistency level is set through this method, the bound statement created from this
   * object will use the default consistency level (LOCAL_ONE).
   *
   * <p>Changing the default consistency level is not retroactive, it only applies to BoundStatement
   * created after the change.
   *
   * @param consistency the default consistency level to set.
   * @return this {@code PreparedStatement} object.
   */
  public PreparedStatement setConsistencyLevel(ConsistencyLevel consistency);

  /**
   * Returns the default consistency level set through {@link #setConsistencyLevel}.
   *
   * @return the default consistency level. Returns {@code null} if no consistency level has been
   *     set through this object {@code setConsistencyLevel} method.
   */
  public ConsistencyLevel getConsistencyLevel();

  /**
   * Sets a default serial consistency level for all bound statements created from this prepared
   * statement.
   *
   * <p>If no serial consistency level is set through this method, the bound statement created from
   * this object will use the default serial consistency level (SERIAL).
   *
   * <p>Changing the default serial consistency level is not retroactive, it only applies to
   * BoundStatement created after the change.
   *
   * @param serialConsistency the default serial consistency level to set.
   * @return this {@code PreparedStatement} object.
   * @throws IllegalArgumentException if {@code serialConsistency} is not one of {@code
   *     ConsistencyLevel.SERIAL} or {@code ConsistencyLevel.LOCAL_SERIAL}.
   */
  public PreparedStatement setSerialConsistencyLevel(ConsistencyLevel serialConsistency);

  /**
   * Returns the default serial consistency level set through {@link #setSerialConsistencyLevel}.
   *
   * @return the default serial consistency level. Returns {@code null} if no consistency level has
   *     been set through this object {@code setSerialConsistencyLevel} method.
   */
  public ConsistencyLevel getSerialConsistencyLevel();

  /**
   * Returns the string of the query that was prepared to yield this {@code PreparedStatement}.
   *
   * <p>Note that a CQL3 query may be implicitly applied to the current keyspace (that is, if the
   * keyspace is not explicitly qualified in the query itself). For prepared queries, the current
   * keyspace used is the one at the time of the preparation, not the one at execution time. The
   * current keyspace at the time of the preparation can be retrieved through {@link
   * #getQueryKeyspace}.
   *
   * @return the query that was prepared to yield this {@code PreparedStatement}.
   */
  public String getQueryString();

  /**
   * Returns the keyspace at the time that this prepared statement was prepared, (that is the one on
   * which this statement applies unless it specified a keyspace explicitly).
   *
   * @return the keyspace at the time that this statement was prepared or {@code null} if no
   *     keyspace was set when the query was prepared (which is possible since keyspaces can be
   *     explicitly qualified in queries and so may not require a current keyspace to be set).
   */
  public String getQueryKeyspace();

  /**
   * Convenience method to enables tracing for all bound statements created from this prepared
   * statement.
   *
   * @return this {@code Query} object.
   */
  public PreparedStatement enableTracing();

  /**
   * Convenience method to disable tracing for all bound statements created from this prepared
   * statement.
   *
   * @return this {@code PreparedStatement} object.
   */
  public PreparedStatement disableTracing();

  /**
   * Returns whether tracing is enabled for this prepared statement, i.e. if BoundStatement created
   * from it will use tracing by default.
   *
   * @return {@code true} if this prepared statement has tracing enabled, {@code false} otherwise.
   */
  public boolean isTracing();

  /**
   * Convenience method to set a default retry policy for the {@code BoundStatement} created from
   * this prepared statement.
   *
   * <p>Note that this method is completely optional. By default, the retry policy used is the one
   * returned {@link com.datastax.driver.core.policies.Policies#getRetryPolicy} in the cluster
   * configuration. This method is only useful if you want to override this default policy for the
   * {@code BoundStatement} created from this {@code PreparedStatement}. to punctually override the
   * default policy for this request.
   *
   * @param policy the retry policy to use for this prepared statement.
   * @return this {@code PreparedStatement} object.
   */
  public PreparedStatement setRetryPolicy(RetryPolicy policy);

  /**
   * Returns the retry policy sets for this prepared statement, if any.
   *
   * @return the retry policy sets specifically for this prepared statement or {@code null} if none
   *     have been set.
   */
  public RetryPolicy getRetryPolicy();

  /**
   * Returns the prepared Id for this statement.
   *
   * @return the PreparedId corresponding to this statement.
   */
  public PreparedId getPreparedId();

  /**
   * Return the incoming payload, that is, the payload that the server sent back with its {@code
   * PREPARED} response, if any, or {@code null}, if the server did not include any custom payload.
   *
   * <p>Note that if an incoming payload is present, and if no outgoing payload has been {@link
   * #setOutgoingPayload(Map) explicitly set}, then each time a {@link BoundStatement} is created
   * (with either {@link #bind()} or {@link #bind(Object...)}), the resulting {@link BoundStatement}
   * will inherit from this value as its default outgoing payload.
   *
   * <p>Implementors should return a read-only view of the original map, but even though, its values
   * would remain inherently mutable. Callers should take care not to modify the returned map in any
   * way.
   *
   * <p>This feature is only available with {@link ProtocolVersion#V4} or above; with lower
   * versions, this method will always return {@code null}.
   *
   * @return the custom payload that the server sent back with its response, if any, or {@code
   *     null}, if the server did not include any custom payload.
   * @since 2.2
   */
  public Map<String, ByteBuffer> getIncomingPayload();

  /**
   * Return the outgoing payload currently associated with this statement.
   *
   * <p>If this is set to a non-null value, each time a {@link BoundStatement} is created (with
   * either {@link #bind()} or {@link #bind(Object...)}), the resulting {@link BoundStatement} will
   * inherit from this value as its default outgoing payload.
   *
   * <p>Implementors should return a read-only view of the original map, but even though, its values
   * would remain inherently mutable. Callers should take care not to modify the returned map in any
   * way.
   *
   * <p>This feature is only available with {@link ProtocolVersion#V4} or above.
   *
   * @return this statement's outgoing payload, if any, or {@code null} if no outgoing payload is
   *     set
   * @since 2.2
   */
  public Map<String, ByteBuffer> getOutgoingPayload();

  /**
   * Associate the given payload with this prepared statement.
   *
   * <p>If this is set to a non-null value, each time a {@link BoundStatement} is created (with
   * either {@link #bind()} or {@link #bind(Object...)}), the resulting {@link BoundStatement} will
   * inherit from this value as its default outgoing payload.
   *
   * <p>Implementors should make a defensive, thread-safe copy of the given map, but even though,
   * its values would remain inherently mutable. Callers should take care not to modify the original
   * map once it is passed to this method.
   *
   * <p>This feature is only available with {@link ProtocolVersion#V4} or above. Trying to include
   * custom payloads in requests sent by the driver under lower protocol versions will result in an
   * {@link com.datastax.driver.core.exceptions.UnsupportedFeatureException} (wrapped in a {@link
   * com.datastax.driver.core.exceptions.NoHostAvailableException}).
   *
   * @param payload the outgoing payload to associate with this statement, or {@code null} to clear
   *     any previously associated payload.
   * @return this {@link Statement} object.
   * @since 2.2
   */
  public PreparedStatement setOutgoingPayload(Map<String, ByteBuffer> payload);

  /**
   * Return the {@link CodecRegistry} instance associated with this prepared statement.
   * Implementations should never return {@code null}; instead, they should always return the {@link
   * CodecRegistry} instance registered with the {@link Cluster} instance this prepared statement
   * belongs to.
   *
   * @return the {@link CodecRegistry} instance associated with this prepared statement.
   */
  public CodecRegistry getCodecRegistry();

  /**
   * Sets whether this statement is idempotent.
   *
   * <p>See {@link com.datastax.driver.core.Statement#isIdempotent} for more explanations about this
   * property.
   *
   * @param idempotent the new value.
   * @return this {@code IdempotenceAwarePreparedStatement} object.
   */
  public PreparedStatement setIdempotent(Boolean idempotent);

  /**
   * Whether this statement is idempotent, i.e. whether it can be applied multiple times without
   * changing the result beyond the initial application.
   *
   * <p>See {@link com.datastax.driver.core.Statement#isIdempotent} for more explanations about this
   * property.
   *
   * <p>Please note that idempotence will be propagated to all {@link BoundStatement}s created from
   * this prepared statement.
   *
   * @return whether this statement is idempotent, or {@code null} to use {@link
   *     QueryOptions#getDefaultIdempotence()}.
   */
  public Boolean isIdempotent();
}
