/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed 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.
 */

#ifndef WIFICOND_NET_NETLINK_MANAGER_H_
#define WIFICOND_NET_NETLINK_MANAGER_H_

#include <functional>
#include <map>
#include <memory>

#include <android-base/macros.h>
#include <android-base/unique_fd.h>

#include "event_loop.h"

namespace android {
namespace wificond {

class MlmeEventHandler;
class NL80211Packet;

// Encapsulates all the different things we know about a specific message
// type like its name, and its id.
struct MessageType {
   // This constructor is needed by map[key] operation.
   MessageType() {};
   explicit MessageType(uint16_t id) {
     family_id = id;
   };
   uint16_t family_id;
   // Multicast groups supported by the family.  The string and mapping to
   // a group id are extracted from the CTRL_CMD_NEWFAMILY message.
   std::map<std::string, uint32_t> groups;
};

// This describes a type of function handling scan results ready notification.
// |interface_index| is the index of interface which the scan results
// are from.
// |aborted| is a boolean indicating if this scan was aborted or not.
// According to nl80211.h document, part of the scan result might still be
// available even when the scan was aborted.
// |ssids| is a vector of scan ssids associated with the corresponding
// scan request.
// |frequencies| is a vector of scan frequencies associated with the
// corresponding scan request.
typedef std::function<void(
    uint32_t interface_index,
    bool aborted,
    std::vector<std::vector<uint8_t>>& ssids,
    std::vector<uint32_t>& frequencies)> OnScanResultsReadyHandler;

// This describes a type of function handling scheduled scan results ready
// notification. This can also be used for notificating the stopping of a
// scheduled scan.
// |interface_index| is the index of interface which the scan results
// are from.
// |scan_stopped| is a boolean indicating if this scheduled scan was stopped
// or not.
typedef std::function<void(
    uint32_t interface_index,
    bool scan_stopped)> OnSchedScanResultsReadyHandler;

// This describes a type of function handling regulatory domain change
// notification.
// If the regulatory domain set is one that pertains to a specific country,
// |country_code| will be set accordingly.
// If the regulatory domain set does not pertain to a specific country,
// |country_code| will be an empty string. This could be a world regulatory
// domain or a intersection regulatory domain.
// See details in defination of |nl80211_reg_type| from nl80211.h.
typedef std::function<void(
    std::string& country_code)> OnRegDomainChangedHandler;

// Enum used for identifying the type of a station event.
// This is used by function |OnStationEventHandler|.
enum StationEvent {
    NEW_STATION,
    DEL_STATION
};

// This describes a type of function handling station events.
// |event| specifies the type of this event.
// |mac_address| is the station mac address associated with this event.
typedef std::function<void(
    StationEvent event,
    const std::vector<uint8_t>& mac_address)> OnStationEventHandler;

class NetlinkManager {
 public:
  explicit NetlinkManager(EventLoop* event_loop);
  virtual ~NetlinkManager();
  // Initialize netlink manager.
  // This includes setting up socket and requesting nl80211 family id from kernel.
  // Returns true on success.
  virtual bool Start();
  // Returns true if this netlink manager object is started.
  virtual bool IsStarted() const;
  // Returns a sequence number available for use.
  virtual uint32_t GetSequenceNumber();
  // Get NL80211 netlink family id,
  virtual uint16_t GetFamilyId();

  // Send |packet| to kernel.
  // This works in an asynchronous way.
  // |handler| will be run when we receive a valid reply from kernel.
  // Do not use this asynchronous interface to send a dump request.
  // Returns true on success.
  virtual bool RegisterHandlerAndSendMessage(const NL80211Packet& packet,
      std::function<void(std::unique_ptr<const NL80211Packet>)> handler);
  // Synchronous version of |RegisterHandlerAndSendMessage|.
  // Returns true on successfully receiving an valid reply.
  // Reply packets will be stored in |*response|.
  virtual bool SendMessageAndGetResponses(
      const NL80211Packet& packet,
      std::vector<std::unique_ptr<const NL80211Packet>>* response);
  // Wrapper of |SendMessageAndGetResponses| for messages with a single
  // response.
  // Returns true on successfully receiving an valid reply.
  // This will returns false if a NLMSG_ERROR is received.
  // Reply packet will be stored in |*response|.
  virtual bool SendMessageAndGetSingleResponse(
      const NL80211Packet& packet,
      std::unique_ptr<const NL80211Packet>* response);

  // Wrapper of |SendMessageAndGetResponses| for messages with a single
  // response.
  // Returns true on successfully receiving an valid reply.
  // This will returns true if a NLMSG_ERROR is received.
  // This is useful when the caller needs the error code from kernel.
  // Reply packet will be stored in |*response|.
  virtual bool SendMessageAndGetSingleResponseOrError(
      const NL80211Packet& packet,
      std::unique_ptr<const NL80211Packet>* response);

  // Wrapper of |SendMessageAndGetResponses| for messages that trigger
  // only a NLMSG_ERROR response
  // Returns true if the message is successfully sent and a NLMSG_ERROR response
  // comes back, regardless of the error code.
  // Error code will be stored in |*error_code|
  virtual bool SendMessageAndGetAckOrError(const NL80211Packet& packet,
                                           int* error_code);
  // Wrapper of |SendMessageAndGetResponses| that returns true iff the response
  // is an ACK.
  virtual bool SendMessageAndGetAck(const NL80211Packet& packet);

  // Sign up to receive and log multicast events of a specific type.
  // |group| is one of the string NL80211_MULTICAST_GROUP_* in nl80211.h.
  virtual bool SubscribeToEvents(const std::string& group);

  // Sign up to be notified when new scan results are available.
  // |handler| will be called when the kernel signals to wificond that a scan
  // has been completed on the given |interface_index|.  See the declaration of
  // OnScanResultsReadyHandler for documentation on the semantics of this
  // callback.
  // Only one handler can be registered per interface index.
  // New handler will replace the registered handler if they are for the
  // same interface index.
  virtual void SubscribeScanResultNotification(
      uint32_t interface_index,
      OnScanResultsReadyHandler handler);

  // Cancel the sign-up of receiving new scan result notification from
  // interface with index |interface_index|.
  virtual void UnsubscribeScanResultNotification(uint32_t interface_index);

  // Sign up to be notified when there is MLME event.
  // Only one handler can be registered per interface index.
  // New handler will replace the registered handler if they are for the
  // same interface index.
  // NetlinkManager is not going to take ownership of this pointer, and that it
  // is the caller's responsibility to make sure that the object exists for the
  // duration of the subscription.
  virtual void SubscribeMlmeEvent(uint32_t interface_index,
                                  MlmeEventHandler* handler);

  // Cancel the sign-up of receiving MLME event notification
  // from interface with index |interface_index|.
  virtual void UnsubscribeMlmeEvent(uint32_t interface_index);

  // Sign up to be notified when new scan results are available.
  // |handler| will be called when the kernel signals to wificond that a
  // scheduled scan has been completed on the given |interface_index|.
  // See the declaration of OnSchedScanResultsReadyHandler for documentation
  // on the semantics of this callback.
  // Only one handler can be registered per interface index.
  // New handler will replace the registered handler if they are for the
  // same interface index.
  virtual void SubscribeSchedScanResultNotification(
      uint32_t interface_index,
      OnSchedScanResultsReadyHandler handler);

  // Cancel the sign-up of receiving new scheduled scan result notification from
  // interface with index |interface_index|.
  virtual void UnsubscribeSchedScanResultNotification(uint32_t interface_index);

  // Sign up to be notified when there is an regulatory domain change.
  // Only one handler can be registered per wiphy index.
  // New handler will replace the registered handler if they are for the
  // same wiphy index.
  virtual void SubscribeRegDomainChange(uint32_t wiphy_index,
                                        OnRegDomainChangedHandler handler);

  // Cancel the sign-up of receiving regulatory domain change notification
  // from wiphy with index |wiphy_index|.
  virtual void UnsubscribeRegDomainChange(uint32_t wiphy_index);

  // Sign up to be notified when there is an station event.
  // Only one handler can be registered per interface index.
  // New handler will replace the registered handler if they are for the
  // same interface index.
  virtual void SubscribeStationEvent(uint32_t interface_index,
                                     OnStationEventHandler handler);

  // Cancel the sign-up of receiving station events.
  virtual void UnsubscribeStationEvent(uint32_t interface_index);

 private:
  bool SetupSocket(android::base::unique_fd* netlink_fd);
  bool WatchSocket(android::base::unique_fd* netlink_fd);
  void ReceivePacketAndRunHandler(int fd);
  bool DiscoverFamilyId();
  bool SendMessageInternal(const NL80211Packet& packet, int fd);
  void BroadcastHandler(std::unique_ptr<const NL80211Packet> packet);
  void OnRegChangeEvent(std::unique_ptr<const NL80211Packet> packet);
  void OnMlmeEvent(std::unique_ptr<const NL80211Packet> packet);
  void OnScanResultsReady(std::unique_ptr<const NL80211Packet> packet);
  void OnSchedScanResultsReady(std::unique_ptr<const NL80211Packet> packet);

  // This handler revceives mapping from NL80211 family name to family id,
  // as well as mapping from group name to group id.
  // These mappings are allocated by kernel.
  void OnNewFamily(std::unique_ptr<const NL80211Packet> packet);

  bool started_;
  // We use different sockets for synchronous and asynchronous interfaces.
  // Kernel will reply error message when we start a new request in the
  // middle of a dump request.
  // Using different sockets help us avoid the complexity of message
  // rescheduling.
  android::base::unique_fd sync_netlink_fd_;
  android::base::unique_fd async_netlink_fd_;
  EventLoop* event_loop_;

  // This is a collection of message handlers, for each sequence number.
  std::map<uint32_t,
      std::function<void(std::unique_ptr<const NL80211Packet>)>> message_handlers_;

  // A mapping from interface index to the handler registered to receive
  // scan results notifications.
  std::map<uint32_t, OnScanResultsReadyHandler> on_scan_result_ready_handler_;
  // A mapping from interface index to the handler registered to receive
  // scheduled scan results notifications.
  std::map<uint32_t, OnSchedScanResultsReadyHandler>
      on_sched_scan_result_ready_handler_;

  std::map<uint32_t, MlmeEventHandler*> on_mlme_event_handler_;

  // A mapping from wiphy index to the handler registered to receive
  // regulatory domain change notifications.
  std::map<uint32_t, OnRegDomainChangedHandler> on_reg_domain_changed_handler_;

  std::map<uint32_t, OnStationEventHandler> on_station_event_handler_;

  // Mapping from family name to family id, and group name to group id.
  std::map<std::string, MessageType> message_types_;

  uint32_t sequence_number_;

  DISALLOW_COPY_AND_ASSIGN(NetlinkManager);
};

}  // namespace wificond
}  // namespace android

#endif  // WIFICOND_NET_NETLINK_MANAGER_H_