// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_MAC_H_
#define DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_MAC_H_

#include <queue>
#include <string>

#import <IOBluetooth/IOBluetooth.h>
#import <IOKit/IOReturn.h>
#include <stddef.h>

#include "base/mac/scoped_nsobject.h"
#include "base/macros.h"
#include "base/memory/linked_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/threading/thread_checker.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_socket.h"
#include "device/bluetooth/bluetooth_uuid.h"

@class BluetoothRfcommConnectionListener;
@class BluetoothL2capConnectionListener;

namespace net {
class IOBuffer;
class IOBufferWithSize;
}

namespace device {

class BluetoothAdapterMac;
class BluetoothChannelMac;

// Implements the BluetoothSocket class for the Mac OS X platform.
class BluetoothSocketMac : public BluetoothSocket {
 public:
  static scoped_refptr<BluetoothSocketMac> CreateSocket();

  // Connects this socket to the service on |device| published as UUID |uuid|.
  // The underlying protocol and PSM or Channel is obtained through service
  // discovery. On a successful connection, the socket properties will be
  // updated and |success_callback| called. On failure, |error_callback| will be
  // called with a message explaining the cause of failure.
  void Connect(IOBluetoothDevice* device,
               const BluetoothUUID& uuid,
               const base::Closure& success_callback,
               const ErrorCompletionCallback& error_callback);

  // Listens for incoming RFCOMM connections using this socket: Publishes an
  // RFCOMM service on the |adapter| as UUID |uuid| with Channel
  // |options.channel|, or an automatically allocated Channel if
  // |options.channel| is left null. The service is published with English name
  // |options.name| if that is non-null. |success_callback| will be called if
  // the service is successfully registered, |error_callback| on failure with a
  // message explaining the cause.
  void ListenUsingRfcomm(scoped_refptr<BluetoothAdapterMac> adapter,
                         const BluetoothUUID& uuid,
                         const BluetoothAdapter::ServiceOptions& options,
                         const base::Closure& success_callback,
                         const ErrorCompletionCallback& error_callback);

  // Listens for incoming L2CAP connections using this socket: Publishes an
  // L2CAP service on the |adapter| as UUID |uuid| with PSM |options.psm|, or an
  // automatically allocated PSM if |options.psm| is left null. The service is
  // published with English name |options.name| if that is non-null.
  // |success_callback| will be called if the service is successfully
  // registered, |error_callback| on failure with a message explaining the
  // cause.
  void ListenUsingL2cap(scoped_refptr<BluetoothAdapterMac> adapter,
                        const BluetoothUUID& uuid,
                        const BluetoothAdapter::ServiceOptions& options,
                        const base::Closure& success_callback,
                        const ErrorCompletionCallback& error_callback);

  // BluetoothSocket:
  void Close() override;
  void Disconnect(const base::Closure& callback) override;
  void Receive(int /* buffer_size */,
               const ReceiveCompletionCallback& success_callback,
               const ReceiveErrorCompletionCallback& error_callback) override;
  void Send(scoped_refptr<net::IOBuffer> buffer,
            int buffer_size,
            const SendCompletionCallback& success_callback,
            const ErrorCompletionCallback& error_callback) override;
  void Accept(const AcceptCompletionCallback& success_callback,
              const ErrorCompletionCallback& error_callback) override;

  // Callback that is invoked when the OS completes an SDP query.
  // |status| is the returned status from the SDP query, |device| is the
  // IOBluetoothDevice for which the query was made. The remaining
  // parameters are those from |Connect()|.
  void OnSDPQueryComplete(
      IOReturn status,
      IOBluetoothDevice* device,
      const base::Closure& success_callback,
      const ErrorCompletionCallback& error_callback);

  // Called by BluetoothRfcommConnectionListener and
  // BluetoothL2capConnectionListener.
  void OnChannelOpened(scoped_ptr<BluetoothChannelMac> channel);

  // Called by |channel_|.
  // Note: OnChannelOpenComplete might be called before the |channel_| is set.
  void OnChannelOpenComplete(const std::string& device_address,
                             IOReturn status);
  void OnChannelClosed();
  void OnChannelDataReceived(void* data, size_t length);
  void OnChannelWriteComplete(void* refcon, IOReturn status);

 private:
  struct AcceptRequest {
    AcceptRequest();
    ~AcceptRequest();

    AcceptCompletionCallback success_callback;
    ErrorCompletionCallback error_callback;
  };

  struct SendRequest {
    SendRequest();
    ~SendRequest();
    int buffer_size;
    SendCompletionCallback success_callback;
    ErrorCompletionCallback error_callback;
    IOReturn status;
    int active_async_writes;
    bool error_signaled;
  };

  struct ReceiveCallbacks {
    ReceiveCallbacks();
    ~ReceiveCallbacks();
    ReceiveCompletionCallback success_callback;
    ReceiveErrorCompletionCallback error_callback;
  };

  struct ConnectCallbacks {
    ConnectCallbacks();
    ~ConnectCallbacks();
    base::Closure success_callback;
    ErrorCompletionCallback error_callback;
  };

  BluetoothSocketMac();
  ~BluetoothSocketMac() override;

  // Accepts a single incoming connection.
  void AcceptConnectionRequest();

  void ReleaseChannel();
  void ReleaseListener();

  bool is_connecting() const { return connect_callbacks_; }

  // Used to verify that all methods are called on the same thread.
  base::ThreadChecker thread_checker_;

  // Adapter the socket is registered against. This is only present when the
  // socket is listening.
  scoped_refptr<BluetoothAdapterMac> adapter_;

  // UUID of the profile being connected to, or that the socket is listening on.
  device::BluetoothUUID uuid_;

  // Simple helpers that register for OS notifications and forward them to
  // |this| profile.
  base::scoped_nsobject<BluetoothRfcommConnectionListener>
      rfcomm_connection_listener_;
  base::scoped_nsobject<BluetoothL2capConnectionListener>
      l2cap_connection_listener_;

  // A handle to the service record registered in the system SDP server.
  // Used to eventually unregister the service.
  BluetoothSDPServiceRecordHandle service_record_handle_;

  // The channel used to issue commands.
  scoped_ptr<BluetoothChannelMac> channel_;

  // Connection callbacks -- when a pending async connection is active.
  scoped_ptr<ConnectCallbacks> connect_callbacks_;

  // Packets received while there is no pending "receive" callback.
  std::queue<scoped_refptr<net::IOBufferWithSize> > receive_queue_;

  // Receive callbacks -- when a receive call is active.
  scoped_ptr<ReceiveCallbacks> receive_callbacks_;

  // Send queue -- one entry per pending send operation.
  std::queue<linked_ptr<SendRequest>> send_queue_;

  // The pending request to an Accept() call, or null if there is no pending
  // request.
  scoped_ptr<AcceptRequest> accept_request_;

  // Queue of incoming connections.
  std::queue<linked_ptr<BluetoothChannelMac>> accept_queue_;

  DISALLOW_COPY_AND_ASSIGN(BluetoothSocketMac);
};

}  // namespace device

#endif  // DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_MAC_H_
