/*
 * 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 static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

/**
 * The configuration to use when running tests with {@link CCMTestsSupport}.
 *
 * <p>This annotation can be used at the class level, or at the method level if you annotate the
 * class with {@code @CreateCCM(PER_METHOD)}.
 */
@Retention(RUNTIME)
@Target({TYPE, METHOD})
public @interface CCMConfig {

  final class Undefined {}

  /**
   * The number of nodes to create, per data center. If not set, this defaults to {@code {1}}, i.e.,
   * one data center with one node.
   *
   * <p>This attribute is ignored if {@link #ccmProvider()} is defined.
   *
   * @return The number of nodes to create, per data center.
   */
  int[] numberOfNodes() default {};

  /**
   * The C* or DSE version to use; defaults to the version defined by the System property {@code
   * cassandra.version}.
   *
   * <p>Note that setting this attribute completely overrides the System properties {@code
   * cassandra.version} and {@code cassandra.directory}.
   *
   * <p>This attribute is ignored if {@link #ccmProvider()} is defined.
   *
   * @return The C* or DSE version to use
   * @see CCMBridge#getCassandraVersion()
   */
  String version() default "";

  /**
   * Whether to launch a DSE instance rather than an OSS C*.
   *
   * <p>Note that setting this attribute completely overrides the System property {@code dse}.
   *
   * <p>This attribute is ignored if {@link #ccmProvider()} is defined.
   *
   * @return {@code true} to launch a DSE instance, {@code false} to launch an OSS C* instance
   *     (default).
   */
  boolean[] dse() default {};

  /**
   * Configuration items to add to cassandra.yaml configuration file. Each configuration item must
   * be in the form {@code key:value}.
   *
   * <p>This attribute is ignored if {@link #ccmProvider()} is defined.
   *
   * @return Configuration items to add to cassandra.yaml configuration file.
   */
  String[] config() default {};

  /**
   * Configuration items to add to dse.yaml configuration file. Each configuration item must be in
   * the form {@code key:value}.
   *
   * <p>This attribute is ignored if {@link #ccmProvider()} is defined.
   *
   * @return Configuration items to add to dse.yaml configuration file.
   */
  String[] dseConfig() default {};

  /**
   * JVM args to use when starting hosts. System properties should be provided one by one, in the
   * form {@code -Dname=value}.
   *
   * <p>This attribute is ignored if {@link #ccmProvider()} is defined.
   *
   * @return JVM args to use when starting hosts.
   */
  String[] jvmArgs() default {};

  /**
   * Free-form options that will be added at the end of the {@code ccm create} command.
   *
   * <p>This attribute is ignored if {@link #ccmProvider()} is defined.
   *
   * @return Free-form options that will be added at the end of the {@code ccm create} command.
   */
  String[] options() default {};

  /**
   * Whether to use SSL encryption.
   *
   * <p>This attribute is ignored if {@link #ccmProvider()} is defined.
   *
   * @return {@code true} to use encryption, {@code false} to use unencrypted communication
   *     (default).
   */
  boolean[] ssl() default {};

  /**
   * Whether to use authentication. Implies the use of SSL encryption.
   *
   * <p>This attribute is ignored if {@link #ccmProvider()} is defined.
   *
   * @return {@code true} to use authentication, {@code false} to use unauthenticated communication
   *     (default).
   */
  boolean[] auth() default {};

  /**
   * The workloads to assign to each node. If this attribute is defined, the number of its elements
   * must be lesser than or equal to the number of nodes in the cluster.
   *
   * <p>This attribute is ignored if {@link #ccmProvider()} is defined.
   *
   * @return The workloads to assign to each node.
   */
  CCMWorkload[] workloads() default {};

  /**
   * Returns {@code true} if a {@link CCMBridge} instance should be automatically created, {@code
   * false} otherwise.
   *
   * <p>If {@code true}, a CCM cluster builder will be created or retrieved from the cache. If
   * {@code false}, no CCM cluster will be activated for this test; this can be useful when some
   * tests in a test class require CCM, while others don't but are in one of the allowed CCM test
   * groups ("short" for example).
   *
   * <p>The cluster will be created once for the whole class if CCM test mode is {@link
   * CreateCCM.TestMode#PER_CLASS}, or once per test method, if the CCM test mode is {@link
   * CreateCCM.TestMode#PER_METHOD},
   *
   * @return {@code true} if a {@link Cluster} instance should be automatically created, {@code
   *     false} otherwise.
   */
  boolean[] createCcm() default {};

  /**
   * Returns {@code true} if a {@link Cluster} instance should be automatically created, {@code
   * false} otherwise.
   *
   * <p>If {@code true}, a cluster builder will be obtained by invoking the method specified by
   * {@link #clusterProvider()} and {@link #clusterProviderClass()}.
   *
   * <p>The CCM cluster will be created once for the whole class if CCM test mode is {@link
   * CreateCCM.TestMode#PER_CLASS}, or once per test method, if the CCM test mode is {@link
   * CreateCCM.TestMode#PER_METHOD},
   *
   * @return {@code true} if a {@link Cluster} instance should be automatically created, {@code
   *     false} otherwise.
   */
  boolean[] createCluster() default {};

  /**
   * Returns {@code true} if a {@link Session} instance should be automatically created, {@code
   * false} otherwise.
   *
   * <p>If {@code true}, a cluster builder will be obtained by invoking the method specified by
   * {@link #clusterProvider()} and {@link #clusterProviderClass()}, and then the session will be
   * created through {@link Cluster#connect()}.
   *
   * <p>The session will be created once for the whole class if CCM test mode is {@link
   * CreateCCM.TestMode#PER_CLASS}, or once per test method, if the CCM test mode is {@link
   * CreateCCM.TestMode#PER_METHOD},
   *
   * @return {@code true} if a {@link Session} instance should be automatically created, {@code
   *     false} otherwise.
   */
  boolean[] createSession() default {};

  /**
   * Returns {@code true} if a test keyspace should be automatically created, {@code false}
   * otherwise.
   *
   * <p>If {@code true}, a cluster builder will be obtained by invoking the method specified by
   * {@link #clusterProvider()} and {@link #clusterProviderClass()}, then a session will be created
   * through {@link Cluster#connect()}, and this session will be used to create the test keyspace.
   *
   * <p>The keyspace will be created once for the whole class if CCM test mode is {@link
   * CreateCCM.TestMode#PER_CLASS}, or once per test method, if the CCM test mode is {@link
   * CreateCCM.TestMode#PER_METHOD}.
   *
   * <p>The test keyspace will be automatically populated upon creation with fixtures provided by
   * {@link #testInitializer()} and {@link #testInitializerClass()}.
   *
   * @return {@code true} if a test keyspace should be automatically created, {@code false}
   *     otherwise.
   */
  boolean[] createKeyspace() default {};

  /**
   * Returns {@code true} if the test class or the test method alters the CCM cluster, e.g. by
   * adding or removing nodes, in which case, it should not be reused after the test is finished.
   *
   * <p>If {@code true}, CCM cluster will be destroyed after the test method if test mode is {@link
   * CreateCCM.TestMode#PER_METHOD}, or after the test class, if test mode is {@link
   * CreateCCM.TestMode#PER_CLASS}.
   *
   * @return {@code true} if the test class or the test method alters the CCM cluster used, {@code
   *     false} otherwise.
   */
  boolean[] dirtiesContext() default {};

  /**
   * Returns the name of the method that should be invoked to obtain a {@link
   * com.datastax.driver.core.CCMBridge.Builder} instance.
   *
   * <p>This method should be declared in {@link #ccmProviderClass()}, or if that attribute is not
   * set, it will be looked up on the test class itself.
   *
   * <p>The method should not have parameters. It can be static or not, and have any visibility.
   *
   * <p>By default, a {@link com.datastax.driver.core.CCMBridge.Builder} instance is obtained by
   * parsing other attributes of this annotation.
   *
   * @return The name of the method that should be invoked to obtain a {@link
   *     com.datastax.driver.core.CCMBridge.Builder} instance.
   */
  String ccmProvider() default "";

  /**
   * Returns the name of the class that should be invoked to obtain a {@link
   * com.datastax.driver.core.CCMBridge.Builder} instance.
   *
   * <p>This class should contain a method named after {@link #ccmProvider()}; if this attribute is
   * not set, it will default to the test class itself.
   *
   * @return The name of the class that should be invoked to obtain a {@link
   *     com.datastax.driver.core.CCMBridge.Builder} instance.
   */
  Class<?> ccmProviderClass() default Undefined.class;

  /**
   * Returns the name of the method that should be invoked to obtain a {@link
   * com.datastax.driver.core.Cluster.Builder} instance.
   *
   * <p>This method should be declared in {@link #clusterProviderClass()}, or if that attribute is
   * not set, it will be looked up on the test class itself.
   *
   * <p>The method should not have parameters. It can be static or not, and have any visibility.
   *
   * <p>By default, the test will look for a method named after {@code createClusterBuilder}.
   *
   * @return The name of the method that should be invoked to obtain a {@link
   *     com.datastax.driver.core.Cluster.Builder} instance.
   */
  String clusterProvider() default "";

  /**
   * Returns the name of the class that should be invoked to obtain a {@link
   * com.datastax.driver.core.Cluster.Builder} instance.
   *
   * <p>This class should contain a method named after {@link #clusterProvider()}; if this attribute
   * is not set, it will default to the test class itself.
   *
   * @return The name of the class that should be invoked to obtain a {@link
   *     com.datastax.driver.core.Cluster.Builder} instance.
   */
  Class<?> clusterProviderClass() default Undefined.class;

  /**
   * Returns the name of the method that should be invoked once the test context is initialized.
   *
   * <p>This method should be declared in {@link #testInitializerClass()}, or if that attribute is
   * not set, it will be looked up on the test class itself.
   *
   * <p>The method should not have parameters. It can be static or not, and have any visibility.
   *
   * <p>By default, the framework will look for a method named {@code onTestContextInitialized}.
   *
   * @return The name of the method that should be invoked once the test context is initialized.
   */
  String testInitializer() default "";

  /**
   * Returns the name of the class that should be invoked once the test context is initialized.
   *
   * <p>This class should contain a method named after {@link #testInitializer()}; if this attribute
   * is not set, it will default to the test class itself.
   *
   * @return The name of the class that should be invoked once the test context is initialized.
   */
  Class<?> testInitializerClass() default Undefined.class;
}
