// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

#ifndef RUNTIME_BIN_EVENTHANDLER_WIN_H_
#define RUNTIME_BIN_EVENTHANDLER_WIN_H_

#if !defined(RUNTIME_BIN_EVENTHANDLER_H_)
#error Do not include eventhandler_win.h directly; use eventhandler.h instead.
#endif

#include <mswsock.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <memory>
#include <utility>

#include "bin/builtin.h"
#include "bin/lockers.h"
#include "bin/reference_counting.h"
#include "bin/socket_base.h"
#include "bin/thread.h"

namespace dart {
namespace bin {

// Forward declarations.
class ClientSocket;
class EventHandlerImplementation;
class FileHandle;
class Handle;
class ListenSocket;
class SocketHandle;

// An OverlappedBuffer encapsulates the OVERLAPPED structure and the
// associated data buffer. For accept it also contains the pre-created
// socket for the client.
class OverlappedBuffer {
 public:
  enum Operation {
    kAccept,
    kRead,
    kRecvFrom,
    kWrite,
    kSendTo,
    kDisconnect,
    kConnect
  };

  static std::unique_ptr<OverlappedBuffer> AllocateAcceptBuffer(
      Handle* handle,
      ADDRESS_FAMILY sa_family);
  static std::unique_ptr<OverlappedBuffer> AllocateReadBuffer(Handle* handle,
                                                              int buffer_size);
  static std::unique_ptr<OverlappedBuffer> AllocateRecvFromBuffer(
      Handle* handle,
      int buffer_size);
  static std::unique_ptr<OverlappedBuffer> AllocateWriteBuffer(Handle* handle,
                                                               int buffer_size);
  static std::unique_ptr<OverlappedBuffer> AllocateSendToBuffer(
      Handle* handle,
      int buffer_size);
  static std::unique_ptr<OverlappedBuffer> AllocateDisconnectBuffer(
      Handle* handle);
  static std::unique_ptr<OverlappedBuffer> AllocateConnectBuffer(
      Handle* handle);

  // Find the IO buffer from the OVERLAPPED address.
  static OverlappedBuffer* GetFromOverlapped(OVERLAPPED* overlapped);

  // Read data from a buffer which has been received. It will read up
  // to num_bytes bytes of data returning the actual number of bytes
  // read. This will update the index of the next byte in the buffer
  // so calling Read several times will keep returning new data from
  // the buffer until all data have been read.
  int Read(void* buffer, int num_bytes);

  // Write data to a buffer before sending it. Returns the number of bytes
  // actually written to the buffer. Calls to Write will always write to
  // the buffer from the beginning.
  int Write(const void* buffer, int num_bytes);

  // Check the amount of data in a read buffer which has not been read yet.
  int GetRemainingLength();
  bool IsEmpty() { return GetRemainingLength() == 0; }

  Operation operation() const { return operation_; }
  SOCKET client() const { return client_; }
  char* GetBufferStart() { return reinterpret_cast<char*>(&buffer_data_); }
  int GetBufferSize() const { return buflen_; }
  RawAddr* from() const { return from_; }

  // Returns the address of the OVERLAPPED structure with all fields
  // initialized to zero.
  OVERLAPPED* GetCleanOverlapped() {
    memset(&overlapped_, 0, sizeof(overlapped_));
    return &overlapped_;
  }

  // Returns a WASBUF structure initialized with the data in this IO buffer.
  WSABUF* GetWASBUF() {
    wbuf_.buf = GetBufferStart();
    wbuf_.len = GetBufferSize();
    return &wbuf_;
  }

  void set_data_length(int data_length) { data_length_ = data_length; }

  void operator delete(void* buffer) { free(buffer); }

  Handle* StealHandle() {
    auto handle = handle_;
    handle_ = nullptr;
    return handle;
  }
  void DetachClient() { client_ = INVALID_SOCKET; }

  ~OverlappedBuffer();

 private:
  OverlappedBuffer(Handle* handle, int buffer_size, Operation operation);

  void* operator new(size_t size, int buffer_size) {
    return malloc(size + buffer_size);
  }

  // Allocate an overlapped buffer for thse specified amount of data and
  // operation. Some operations need additional buffer space, which is
  // handled by this method.
  static std::unique_ptr<OverlappedBuffer> AllocateBuffer(Handle* handle,
                                                          int buffer_size,
                                                          Operation operation);

  OVERLAPPED overlapped_;           // OVERLAPPED structure for overlapped IO.
  SOCKET client_ = INVALID_SOCKET;  // Used for AcceptEx client socket.
  int buflen_;                      // Length of the buffer.
  Operation operation_;             // Type of operation issued.
  Handle* handle_ = nullptr;

  int index_;        // Index for next read from read buffer.
  int data_length_;  // Length of the actual data in the buffer.

  WSABUF wbuf_;  // Structure for passing buffer to WSA functions.

  // For the recvfrom operation additional storace is allocated for the
  // source sockaddr.
  RawAddr* from_;

  // Buffer for recv/send/AcceptEx. This must be at the end of the
  // object as the object is allocated larger than it's definition
  // indicate to extend this array.
  uint8_t buffer_data_[1];

  DISALLOW_COPY_AND_ASSIGN(OverlappedBuffer);
};

// Abstract super class for holding information on listen and connected
// sockets.
class Handle : public ReferenceCounted<Handle>, public DescriptorInfoBase {
 public:
  enum Type {
    kFile,
    kStd,
    kDirectoryWatch,
    kClientSocket,
    kListenSocket,
    kDatagramSocket
  };

  enum class SupportsOverlappedIO { kYes, kNo };

  // Socket interface exposing normal socket operations.
  intptr_t Available();
  bool DataReady();
  intptr_t Read(void* buffer, intptr_t num_bytes);
  intptr_t RecvFrom(void* buffer, intptr_t num_bytes, RawAddr* from);
  virtual intptr_t Write(const void* buffer, intptr_t num_bytes);
  virtual intptr_t SendTo(const void* buffer,
                          intptr_t num_bytes,
                          const RawAddr& addr);

  // Internal interface used by the event handler.
  virtual bool IssueReadLocked(MonitorLocker* ml);
  virtual bool IssueRecvFromLocked(MonitorLocker* ml);
  bool HasPendingRead();
  bool HasPendingWrite();
  void ReadComplete(std::unique_ptr<OverlappedBuffer> buffer);
  void WriteComplete(std::unique_ptr<OverlappedBuffer> buffer);

  bool IsClosing() { return (flags_ & (1 << kClosing)) != 0; }
  bool IsClosedRead() { return (flags_ & (1 << kCloseRead)) != 0; }
  bool IsClosedWrite() { return (flags_ & (1 << kCloseWrite)) != 0; }
  bool IsError() { return (flags_ & (1 << kError)) != 0; }
  void MarkClosing() { flags_ |= (1 << kClosing); }
  void MarkClosedRead() { flags_ |= (1 << kCloseRead); }
  void MarkClosedWrite() { flags_ |= (1 << kCloseWrite); }
  void MarkError() { flags_ |= (1 << kError); }

  HANDLE handle() { return handle_; }

  void Close();
  void CloseLocked(MonitorLocker* ml);

  virtual void DoCloseLocked(MonitorLocker* ml);
  virtual bool IsClosed() = 0;

  bool IsHandleClosed() const { return handle_ == INVALID_HANDLE_VALUE; }

  Type type() { return type_; }
  bool is_file() { return type_ == kFile; }
  bool is_socket() {
    return type_ == kListenSocket || type_ == kClientSocket ||
           type_ == kDatagramSocket;
  }
  bool is_listen_socket() { return type_ == kListenSocket; }
  bool is_client_socket() { return type_ == kClientSocket; }
  bool is_datagram_socket() { return type_ == kDatagramSocket; }

  bool supports_overlapped_io() {
    return (flags_ & (1 << kDoesNotSupportOverlappedIO)) == 0;
  }

  void ReadSyncCompleteAsync();

  DWORD last_error() { return last_error_; }
  void set_last_error(DWORD last_error) { last_error_ = last_error; }

 protected:
  // For access to monitor_;
  friend class EventHandlerImplementation;

  enum Flags {
    kClosing = 0,
    kCloseRead = 1,
    kCloseWrite = 2,
    kDoesNotSupportOverlappedIO = 3,
    kError = 4
  };

  Handle(intptr_t handle,
         Type type_,
         SupportsOverlappedIO supports_overlapped_io);
  virtual ~Handle();

  virtual void HandleIssueError();

  Monitor monitor_;
  const Type type_;
  HANDLE handle_;

  std::unique_ptr<OverlappedBuffer>
      data_ready_;  // Buffer for data ready to be read.
  OverlappedBuffer* pending_read_ = nullptr;   // Buffer for pending read.
  OverlappedBuffer* pending_write_ = nullptr;  // Buffer for pending write

  DWORD last_error_ = NOERROR;

  HANDLE read_thread_ = INVALID_HANDLE_VALUE;
  bool read_thread_starting_ = false;
  bool read_thread_finished_ = false;

 private:
  void WaitForReadThreadStarted();
  void NotifyReadThreadStarted();
  void WaitForReadThreadFinished();
  void NotifyReadThreadFinished();

  virtual bool IssueWriteLocked(MonitorLocker* ml,
                                std::unique_ptr<OverlappedBuffer> buffer);
  virtual bool IssueSendToLocked(MonitorLocker* ml,
                                 std::unique_ptr<OverlappedBuffer> buffer,
                                 const RawAddr& addr);

  int flags_ = 0;

  friend class ReferenceCounted<Handle>;
  DISALLOW_COPY_AND_ASSIGN(Handle);
};

class FileHandle : public DescriptorInfoSingleMixin<Handle> {
 public:
  explicit FileHandle(HANDLE handle)
      : DescriptorInfoSingleMixin(reinterpret_cast<intptr_t>(handle),
                                  kFile,
                                  SupportsOverlappedIO::kYes) {}

  virtual bool IsClosed();

 protected:
  FileHandle(HANDLE handle,
             Type type,
             SupportsOverlappedIO supports_overlapped_io)
      : DescriptorInfoSingleMixin(reinterpret_cast<intptr_t>(handle),
                                  type,
                                  supports_overlapped_io) {}

 private:
  DISALLOW_COPY_AND_ASSIGN(FileHandle);
};

class StdHandle : public FileHandle {
 public:
  static StdHandle* Stdin(HANDLE handle);

  virtual void DoCloseLocked(MonitorLocker* ml);
  virtual intptr_t Write(const void* buffer, intptr_t num_bytes);

  void WriteSyncCompleteAsync();
  void RunWriteLoop();

#if defined(DEBUG)
  static StdHandle* StdinPtr() { return stdin_; }
#endif

 private:
  static Mutex* stdin_mutex_;
  static StdHandle* stdin_;

  explicit StdHandle(HANDLE handle)
      : FileHandle(handle, kStd, SupportsOverlappedIO::kNo) {}

  HANDLE thread_handle_ = INVALID_HANDLE_VALUE;
  intptr_t thread_wrote_ = 0;
  bool write_thread_exists_ = false;
  bool write_thread_running_ = false;

  DISALLOW_COPY_AND_ASSIGN(StdHandle);
};

class DirectoryWatchHandle : public DescriptorInfoSingleMixin<Handle> {
 public:
  DirectoryWatchHandle(HANDLE handle, int events, bool recursive)
      : DescriptorInfoSingleMixin(reinterpret_cast<intptr_t>(handle),
                                  kDirectoryWatch,
                                  SupportsOverlappedIO::kYes),
        events_(events),
        recursive_(recursive) {}

  virtual bool IsClosed();

  virtual bool IssueReadLocked(MonitorLocker* ml);

  void Start();
  void Stop();

 private:
  int events_;
  bool recursive_;

  DISALLOW_COPY_AND_ASSIGN(DirectoryWatchHandle);
};

class SocketHandle : public Handle {
 public:
  SOCKET socket() const { return socket_; }

 protected:
  explicit SocketHandle(intptr_t s, Type type)
      : Handle(s, type, SupportsOverlappedIO::kYes), socket_(s) {}

  virtual void HandleIssueError();

 private:
  const SOCKET socket_;

  DISALLOW_COPY_AND_ASSIGN(SocketHandle);
};

// Information on listen sockets.
class ListenSocket : public DescriptorInfoMultipleMixin<SocketHandle> {
 public:
  explicit ListenSocket(intptr_t s, ADDRESS_FAMILY sa_family)
      : DescriptorInfoMultipleMixin(s, kListenSocket),
        sa_family_(sa_family),
        pending_accept_count_(0),
        accepted_head_(nullptr),
        accepted_tail_(nullptr),
        accepted_count_(0) {}
  virtual ~ListenSocket() {
    ASSERT(!HasPendingAccept());
    ASSERT(accepted_head_ == nullptr);
    ASSERT(accepted_tail_ == nullptr);
  }

  // Socket interface exposing normal socket operations.
  ClientSocket* Accept();
  bool StartAccept();

  // Internal interface used by the event handler.
  bool HasPendingAccept() { return pending_accept_count_ > 0; }
  void AcceptComplete(std::unique_ptr<OverlappedBuffer> buffer);

  virtual void DoCloseLocked(MonitorLocker* ml);
  virtual bool IsClosed();

  int pending_accept_count() { return pending_accept_count_; }

  int accepted_count() { return accepted_count_; }

  void DispatchCompletedAcceptsLocked(MonitorLocker* ml);

 private:
  static constexpr intptr_t kMinIssuedAccepts = 5;

  bool IssueAcceptLocked(MonitorLocker* ml);

  ADDRESS_FAMILY sa_family_;

  // The number of asynchronous `IssueAccept` operations which haven't completed
  // yet.
  int pending_accept_count_;

  // Linked list of accepted connections provided by completion code. Ready to
  // be handed over through accept.
  ClientSocket* accepted_head_;
  ClientSocket* accepted_tail_;

  // The number of accepted connections which are waiting to be removed from
  // this queue and processed by dart isolates.
  int accepted_count_;

  DISALLOW_COPY_AND_ASSIGN(ListenSocket);
};

// Information on connected sockets.
class ClientSocket : public DescriptorInfoSingleMixin<SocketHandle> {
 public:
  explicit ClientSocket(intptr_t s,
                        std::unique_ptr<RawAddr>&& remote_addr = nullptr)
      : DescriptorInfoSingleMixin(s, kClientSocket),
        next_(nullptr),
        connected_(false),
        closed_(false),
        remote_addr_(std::move(remote_addr)) {}

  virtual ~ClientSocket() {
    // Don't delete this object until all pending requests have been handled.
    ASSERT(!HasPendingRead());
    ASSERT(!HasPendingWrite());
    ASSERT(next_ == nullptr);
    ASSERT(closed_ == true);
  }

  void Shutdown(int how);

  // Internal interface used by the event handler.
  virtual bool IssueReadLocked(MonitorLocker* ml);
  void IssueDisconnectLocked(MonitorLocker* ml);
  void DisconnectComplete();
  void ConnectComplete();

  virtual void DoCloseLocked(MonitorLocker* ml);
  virtual bool IsClosed();

  // If `ClientSocket` was constructed with a `remote_addr`, populate `addr`
  // with that value and return `true`. Otherwise leave `addr` untouched and
  // return `false`.
  bool PopulateRemoteAddr(RawAddr& addr);

  ClientSocket* next() { return next_; }
  void set_next(ClientSocket* next) { next_ = next; }

  void mark_connected() { connected_ = true; }
  bool is_connected() const { return connected_; }

  void mark_closed() { closed_ = true; }

#if defined(DEBUG)
  static intptr_t disconnecting() { return disconnecting_; }
#endif

 private:
  virtual bool IssueWriteLocked(MonitorLocker* ml,
                                std::unique_ptr<OverlappedBuffer> buffer);

  ClientSocket* next_;
  bool connected_;
  bool closed_;
  std::unique_ptr<RawAddr> remote_addr_;

#if defined(DEBUG)
  static intptr_t disconnecting_;
#endif

  DISALLOW_COPY_AND_ASSIGN(ClientSocket);
};

class DatagramSocket : public DescriptorInfoSingleMixin<SocketHandle> {
 public:
  explicit DatagramSocket(intptr_t s)
      : DescriptorInfoSingleMixin(s, kDatagramSocket) {}

  virtual ~DatagramSocket() {
    // Don't delete this object until all pending requests have been handled.
    ASSERT(!HasPendingRead());
    ASSERT(!HasPendingWrite());
  }

  // Internal interface used by the event handler.
  virtual bool IssueRecvFromLocked(MonitorLocker* ml);

  virtual void DoCloseLocked(MonitorLocker* ml);
  virtual bool IsClosed();

 private:
  virtual bool IssueSendToLocked(MonitorLocker* ml,
                                 std::unique_ptr<OverlappedBuffer> buffer,
                                 const RawAddr& addr);

  DISALLOW_COPY_AND_ASSIGN(DatagramSocket);
};

// Event handler.
class EventHandlerImplementation {
 public:
  EventHandlerImplementation();
  virtual ~EventHandlerImplementation();

  void AssociateWithCompletionPort(Handle* handle);

  void SendData(intptr_t id, Dart_Port dart_port, int64_t data);
  void Start(EventHandler* handler);
  void Shutdown();

  static void EventHandlerEntry(uword args);

  int64_t GetTimeout();
  void HandleInterrupt(InterruptMessage* msg);
  void HandleTimeout();

  void HandleIOCompletion(int32_t bytes, ULONG_PTR key, OVERLAPPED* overlapped);

  void HandleCompletionOrInterrupt(BOOL ok,
                                   DWORD bytes,
                                   ULONG_PTR key,
                                   OVERLAPPED* overlapped);

  HANDLE completion_port() { return completion_port_; }

  LPFN_ACCEPTEX accept_ex() {
    InitializeSocketExtensions();
    return accept_ex_;
  }

  LPFN_CONNECTEX connect_ex() {
    InitializeSocketExtensions();
    return connect_ex_;
  }

  LPFN_DISCONNECTEX disconnect_ex() {
    InitializeSocketExtensions();
    return disconnect_ex_;
  }

  LPFN_GETACCEPTEXSOCKADDRS get_accept_ex_sockaddrs() {
    InitializeSocketExtensions();
    return get_accept_ex_sockaddrs_;
  }

 private:
  void InitializeSocketExtensions();

  void HandleAccept(ListenSocket* listen_socket,
                    std::unique_ptr<OverlappedBuffer> buffer);
  void HandleRead(Handle* handle,
                  int bytes,
                  std::unique_ptr<OverlappedBuffer> buffer);
  void HandleRecvFrom(Handle* handle,
                      int bytes,
                      std::unique_ptr<OverlappedBuffer> buffer);
  void HandleWrite(Handle* handle,
                   int bytes,
                   std::unique_ptr<OverlappedBuffer> buffer);
  void HandleDisconnect(ClientSocket* client_socket,
                        int bytes,
                        std::unique_ptr<OverlappedBuffer> buffer);
  void HandleConnect(ClientSocket* client_socket,
                     int bytes,
                     std::unique_ptr<OverlappedBuffer> buffer);

  Monitor monitor_;
  HANDLE handler_thread_ = INVALID_HANDLE_VALUE;

  TimeoutQueue timeout_queue_;  // Time for next timeout.
  bool shutdown_ = false;
  HANDLE completion_port_ = INVALID_HANDLE_VALUE;

  std::atomic<bool> socket_extensions_initialized_{false};
  LPFN_ACCEPTEX accept_ex_ = nullptr;
  LPFN_CONNECTEX connect_ex_ = nullptr;
  LPFN_DISCONNECTEX disconnect_ex_ = nullptr;
  LPFN_GETACCEPTEXSOCKADDRS get_accept_ex_sockaddrs_ = nullptr;

  DISALLOW_COPY_AND_ASSIGN(EventHandlerImplementation);
};

}  // namespace bin
}  // namespace dart

#endif  // RUNTIME_BIN_EVENTHANDLER_WIN_H_
