/*
 * 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.
 */

#pragma once

#ifndef GEODE_POOL_H_
#define GEODE_POOL_H_

#include <chrono>
#include <iosfwd>
#include <memory>

#include "Cache.hpp"
#include "CacheFactory.hpp"
#include "CacheableBuiltins.hpp"
#include "internal/chrono/duration.hpp"
#include "internal/geode_base.hpp"
#include "internal/geode_globals.hpp"

namespace apache {
namespace geode {
namespace client {

class AuthenticatedView;
class Cache;
class CacheFactory;
class PoolAttributes;
class CacheImpl;
class Properties;
class QueryService;

/**
 * A pool of connections to connect from a client to a set of Geode Cache
 * Servers.
 * <p>Instances of this interface are created using
 * {@link PoolFactory#create}.
 * <p>Existing instances can be found using {@link PoolFactory#find}
 * and {@link PoolFactory#getAll}.
 * <p>The pool name must be configured
 * on the client regions that will use this pool by calling
 * {@link AttributeFactory#setPoolName}.
 *
 *
 */
class APACHE_GEODE_EXPORT Pool : public std::enable_shared_from_this<Pool> {
 public:
  /**
   * Gets the name of the connection pool
   *
   * @return the name of the pool
   * @see PoolFactory#create
   */
  virtual const std::string& getName() const = 0;

  /**
   * Returns the connection timeout of this pool.
   * @see PoolFactory#setFreeConnectionTimeout
   */
  std::chrono::milliseconds getFreeConnectionTimeout() const;

  /**
   * Returns the load conditioning interval of this pool.
   * @see PoolFactory#setLoadConditioningInterval
   */
  std::chrono::milliseconds getLoadConditioningInterval() const;

  /**
   * Returns the socket buffer size of this pool.
   * @see PoolFactory#setSocketBufferSize
   */
  int getSocketBufferSize() const;

  /**
   * Returns the read timeout of this pool.
   * @see PoolFactory#setReadTimeout
   */
  std::chrono::milliseconds getReadTimeout() const;

  /**
   * Gets the host name of the SniProxy.
   * @see PoolFactory#setSniProxy(string, int)
   */
  const std::string& getSniProxyHost() const;

  /**
   * Gets the port of the SniProxy.
   * @see PoolFactory#setSniProxy(string, int)
   */
  uint16_t getSniProxyPort() const;

  /**
   * Gets the minimum connections for this pool.
   * @see PoolFactory#setMinConnections(int)
   */
  int getMinConnections() const;

  /**
   * Gets the maximum connections for this pool.
   * @see PoolFactory#setMaxConnections(int)
   */
  int getMaxConnections() const;

  /**
   * Gets the idle connection timeout for this pool.
   * @see PoolFactory#setIdleTimeout(long)
   */
  std::chrono::milliseconds getIdleTimeout() const;

  /**
   * Gets the ping interval for this pool.
   * @see PoolFactory#setPingInterval(long)
   */
  std::chrono::milliseconds getPingInterval() const;

  /**
   * Gets the update locator list interval for this pool.
   * @see PoolFactory#setUpdateLocatorListInterval(long)
   */
  std::chrono::milliseconds getUpdateLocatorListInterval() const;

  /**
   * Gets the statistic interval for this pool.
   * @see PoolFactory#setStatisticInterval(int)
   */
  std::chrono::milliseconds getStatisticInterval() const;

  /**
   * Gets the retry attempts for this pool.
   * @see PoolFactory#setRetryAttempts(int)
   */
  int getRetryAttempts() const;

  /**
   * Returns the true if a server-to-client subscriptions are enabled on this
   * pool.
   * @see PoolFactory#setSubscriptionEnabled
   */
  bool getSubscriptionEnabled() const;

  /**
   * Returns the subscription redundancy level of this pool.
   * @see PoolFactory#setSubscriptionRedundancy
   */
  int getSubscriptionRedundancy() const;

  /**
   * Returns the subscription message tracking timeout of this pool.
   * @see PoolFactory#setSubscriptionMessageTrackingTimeout
   */
  std::chrono::milliseconds getSubscriptionMessageTrackingTimeout() const;

  /**
   * Returns the subscription ack interval of this pool.
   * @see PoolFactory#setSubscriptionAckInterval(int)
   */
  std::chrono::milliseconds getSubscriptionAckInterval() const;

  /**
   * Returns the server group of this pool.
   * @see PoolFactory#setServerGroup
   */
  const std::string& getServerGroup() const;

  /**
   * Returns <code>true</code> if thread local connections are enabled on this
   * pool.
   * @see PoolFactory#setThreadLocalConnections
   */
  bool getThreadLocalConnections() const;

  /**
   * Returns <code>true</code> if multiuser authentication is enabled on this
   * pool.
   * @see PoolFactory#setMultiuserAuthentication
   */
  bool getMultiuserAuthentication() const;

  /**
   * Returns true if single-hop optimization is enabled on this pool.
   * @see PoolFactory#setPRSingleHopEnabled
   */
  bool getPRSingleHopEnabled() const;

  /**
   * If this pool was configured to use <code>threadlocalconnections</code>,
   * then this method will release the connection cached for the calling thread.
   * The connection will then be available for use by other threads.
   *
   * If this pool is not using <code>threadlocalconnections</code>, this method
   * will have no effect.
   */
  virtual void releaseThreadLocalConnection() = 0;

  /**
   * Returns an unmodifiable list locators that
   * this pool is using. Each locator was either
   * {@link PoolFactory#addLocator added explicitly}
   * when the pool was created or was discovered using the explicit locators.
   * <p> If a pool has no locators, then it cannot discover servers or locators
   * at runtime.
   */
  virtual const std::shared_ptr<CacheableStringArray> getLocators() const = 0;

  /**
   * Returns an unmodifiable list of
   * servers this pool is using. These servers where either
   * {@link PoolFactory#addServer added explicitly}
   * when the pool was created or were discovered using this pools {@link
   * #getLocators locators}.
   */
  virtual const std::shared_ptr<CacheableStringArray> getServers() = 0;

  /**
   * Destroys this pool closing any connections it produced.
   * @param keepAlive defines
   *                whether the server should keep the durable client's
   *                subscriptions alive for the timeout period.
   * @throws IllegalStateException
   *                 if the pool is still in use.
   */
  virtual void destroy(bool keepAlive = false) = 0;

  /**
   * Indicates whether this Pool has been
   * destroyed.
   *
   * @return true if the pool has been destroyed.
   */
  virtual bool isDestroyed() const = 0;

  /**
   * Returns the QueryService for this Pool.
   * The query operations performed using this QueryService will be executed
   * on the servers that are associated with this pool.
   * To perform Query operation on the local cache obtain the QueryService
   * instance from the Cache.
   * @throws unSupported Exception when Pool is in multi user mode.
   *
   * @see Cache#getQueryService
   * @return the QueryService
   */
  virtual std::shared_ptr<QueryService> getQueryService() = 0;

  virtual ~Pool();

  /**
   * Returns the approximate number of pending subscription events maintained at
   * server for this durable client pool at the time it (re)connected to the
   * server. Server would start dispatching these events to this durable client
   * pool when it receives {@link Cache#readyForEvents()} from it.
   * <p>
   * Durable clients can call this method on reconnect to assess the amount of
   * 'stale' data i.e. events accumulated at server while this client was away
   * and, importantly, before calling {@link Cache#readyForEvents()}.
   * <p>
   * Any number of invocations of this method during a single session will
   * return the same value.
   * <p>
   * It may return a zero value if there are no events pending at server for
   * this client pool. A negative value returned tells us that no queue was
   * available at server for this client pool.
   * <p>
   * A value -1 indicates that this client pool reconnected to server after its
   * 'durable-client-timeout' period elapsed and hence its subscription queue at
   * server was removed, possibly causing data loss.
   * <p>
   * A value -2 indicates that this client pool connected to server for the
   * first time.
   *
   * @return int The number of subscription events maintained at server for this
   *         durable client pool at the time this pool (re)connected. A negative
   *         value indicates no queue was found for this client pool.
   * @throws IllegalStateException
   *           If called by a non-durable client or if invoked any time after
   *           invocation of {@link Cache#readyForEvents()}.
   * @since 8.1
   */
  int getPendingEventCount() const;

 protected:
  explicit Pool(std::shared_ptr<PoolAttributes> attr);
  std::shared_ptr<PoolAttributes> m_attrs;

 private:
  /**
   * Returns the logical instance of cache from pool.
   * Each operation on this cache will use this "credentials"
   *
   * @throws IllegalStateException if cache has not been created or it has been
   * closed.
   *        Or if Pool is not in multiusersecure mode.
   * @returns Logical instance of cache to do operations on behalf of one
   * particular user.
   */
  virtual AuthenticatedView createAuthenticatedView(
      std::shared_ptr<Properties> credentials, CacheImpl* cacheImpl);

  Pool(const Pool&);

  friend class PoolFactory;
  friend class CacheFactory;
  friend class CacheImpl;
};

}  // namespace client
}  // namespace geode
}  // namespace apache

#endif  // GEODE_POOL_H_
