/*
 * 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_CONNECTOR_H_
#define GEODE_CONNECTOR_H_

#include <chrono>

#include <geode/internal/geode_globals.hpp>

namespace apache {
namespace geode {
namespace client {

constexpr std::chrono::microseconds DEFAULT_CONNECT_TIMEOUT =
    std::chrono::seconds(10);

constexpr std::chrono::milliseconds DEFAULT_TIMEOUT = std::chrono::seconds(15);

constexpr std::chrono::milliseconds DEFAULT_READ_TIMEOUT = DEFAULT_TIMEOUT;

constexpr std::chrono::milliseconds DEFAULT_WRITE_TIMEOUT = DEFAULT_TIMEOUT;

class Connector {
 public:
  Connector() = default;
  virtual ~Connector() = default;

  Connector(const Connector &) = delete;
  Connector &operator=(const Connector &) = delete;

  /**
   * Reads <code>len</code> bytes of data and stores them into the buffer
   * array <code>b</code>. The number of bytes actually read is returned as an
   * integer.  This method blocks until <code>len</code> bytes of data is
   * read, the timer expires or an exception is thrown.
   *
   * <p> If <code>b</code> is <code>null</code>, or <code>len</code> is
   * less than or equal to <code>0</code> an
   * <code>IllegalArgumentException</code>
   * is thrown.
   *
   * <p> If <code>len</code> bytes have not been read when the timeout
   * expires a <code>TimeoutException</code> is thrown.
   *
   * <p> If <code>len</code> bytes cannot be read for any reason, then an
   * <code>GeodeIOException</code> is thrown.
   *
   * <p> The <code>read(b)</code> method for class <code>InputStream</code>
   * has the same effect as: <pre><code> read(b, 0, b.length) </code></pre>
   *
   * @param      b   the buffer into which the data is read.
   * @param      len   the number of bytes to read.
   * @param      timeout time to allow the read to complete.
   * @return     the total number of bytes read into the buffer, or
   *             <code>-1</code> if an error was encountered.
   * @exception  GeodeIOException, TimeoutException, IllegalArgumentException,
   * OutOfMemoryException.
   */
  virtual size_t receive(char *b, size_t len,
                         std::chrono::milliseconds timeout) = 0;

  /**
   * Reads an undetermined number of bytes of data and stores them into the
   * buffer array <code>b</code>. The number of bytes actually read is returned
   * as an integer.  This method blocks until <code>len</code> bytes of data is
   * read, the timeout expires or an exception is thrown.
   *
   * <p> If <code>b</code> is <code>null</code> an
   * <code>IllegalArgumentException</code>
   * is thrown.
   *
   * <p> The <code>read(b)</code> method for class <code>InputStream</code>
   * has the same effect as: <pre><code> read(b, 0, b.length) </code></pre>
   *
   * @param      b   the buffer into which the data is read.
   * @param      timeout time to allow the read to complete.
   * @return     the total number of bytes read into the buffer, or
   *             <code>-1</code> if an error was encountered.
   * @exception  GeodeIOException, IllegalArgumentException,
   * OutOfMemoryException.
   */
  virtual size_t receive_nothrowiftimeout(
      char *b, size_t len, std::chrono::milliseconds timeout) = 0;
  /**
   * Writes <code>len</code> bytes from the specified byte array
   * to the underlying output stream.
   *
   * @param      b     the data.
   * @param      len   the number of bytes to write.
   * @param      timeout time to allow the write to complete.
   * @return     the actual number of bytes written.
   * @exception  GeodeIOException, TimeoutException, IllegalArgumentException.
   */
  virtual size_t send(const char *b, size_t len,
                      std::chrono::milliseconds timeout) = 0;

  /**
   * Returns local port for this TCP connection
   */
  virtual uint16_t getPort() = 0;

  /**
   * Returns the remote endpoint for this connection in the form host:port
   */
  virtual std::string getRemoteEndpoint() = 0;

  /**
   * Writes an array of a known size to the underlying output stream.
   *
   * @param   array A C-style stack array. Be weary of arrays that have been
   * decayed into pointers, they won't compile.
   * @return  The number of bytes written. Don't get confused: this is not the
   * number of elements in the array written.
   * @exception GeodeIOException, TimeoutException
   */
  template <typename T, size_t size>
  size_t send(const T (&array)[size], std::chrono::milliseconds timeout) {
    return send(reinterpret_cast<const char *>(array), sizeof(T) * size,
                timeout);
  }

  /**
   * Reads an array of a known size from the underlying input stream. If the
   * number of bytes read is not equal to the size of the array, no exception
   * will be thrown.
   *
   * @param   array A C-style stack array. Be weary of arrays that have been
   * decayed into pointers, they won't compile.
   * @return  The number of bytes written. Don't get confused: this is not the
   * number of elements in the array written.
   * @exception GeodeIOException
   */
  template <typename T, size_t size>
  size_t receive(T (&array)[size], std::chrono::milliseconds timeout) {
    return receive_nothrowiftimeout(reinterpret_cast<char *>(array),
                                    sizeof(T) * size, timeout);
  }
};

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

#endif  // GEODE_CONNECTOR_H_
