/*
 * Copyright (C) 2014 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 __WIFI_HAL_CPP_BINDINGS_H__
#define __WIFI_HAL_CPP_BINDINGS_H__

#include "wifi_hal.h"
#include "common.h"
#include "sync.h"

class WifiEvent
{
    /* TODO: remove this when nl headers are updated */
    static const unsigned NL80211_ATTR_MAX_INTERNAL = 256;
private:
    struct nl_msg *mMsg;
    struct genlmsghdr *mHeader;
    struct nlattr *mAttributes[NL80211_ATTR_MAX_INTERNAL + 1];

public:
    WifiEvent(nl_msg *msg) {
        mMsg = msg;
        mHeader = NULL;
        memset(mAttributes, 0, sizeof(mAttributes));
    }
    ~WifiEvent() {
        /* don't destroy mMsg; it doesn't belong to us */
    }

    void log();

    int parse();

    genlmsghdr *header() {
        return mHeader;
    }

    int get_cmd() {
        return mHeader->cmd;
    }

    int get_vendor_id() {
        return get_u32(NL80211_ATTR_VENDOR_ID);
    }

    int get_vendor_subcmd() {
        return get_u32(NL80211_ATTR_VENDOR_SUBCMD);
    }

    void *get_vendor_data() {
        return get_data(NL80211_ATTR_VENDOR_DATA);
    }

    int get_vendor_data_len() {
        return get_len(NL80211_ATTR_VENDOR_DATA);
    }

    const char *get_cmdString();

    nlattr ** attributes() {
        return mAttributes;
    }

    nlattr *get_attribute(int attribute) {
        return mAttributes[attribute];
    }

    uint8_t get_u8(int attribute) {
        return mAttributes[attribute] ? nla_get_u8(mAttributes[attribute]) : 0;
    }

    uint16_t get_u16(int attribute) {
        return mAttributes[attribute] ? nla_get_u16(mAttributes[attribute]) : 0;
    }

    uint32_t get_u32(int attribute) {
        return mAttributes[attribute] ? nla_get_u32(mAttributes[attribute]) : 0;
    }

    uint64_t get_u64(int attribute) {
        return mAttributes[attribute] ? nla_get_u64(mAttributes[attribute]) : 0;
    }

    int get_len(int attribute) {
        return mAttributes[attribute] ? nla_len(mAttributes[attribute]) : 0;
    }

    void *get_data(int attribute) {
        return mAttributes[attribute] ? nla_data(mAttributes[attribute]) : NULL;
    }

private:
    WifiEvent(const WifiEvent&);        // hide copy constructor to prevent copies
};

class nl_iterator {
    struct nlattr *pos;
    int rem;
public:
    nl_iterator(struct nlattr *attr) {
        pos = (struct nlattr *)nla_data(attr);
        rem = nla_len(attr);
    }
    bool has_next() {
        return nla_ok(pos, rem);
    }
    void next() {
        pos = (struct nlattr *)nla_next(pos, &(rem));
    }
    struct nlattr *get() {
        return pos;
    }
    uint16_t get_type() {
        return pos->nla_type;
    }
    uint8_t get_u8() {
        return nla_get_u8(pos);
    }
    uint16_t get_u16() {
        return nla_get_u16(pos);
    }
    uint32_t get_u32() {
        return nla_get_u32(pos);
    }
    uint64_t get_u64() {
        return nla_get_u64(pos);
    }
    void* get_data() {
        return nla_data(pos);
    }
    int get_len() {
        return nla_len(pos);
    }
private:
    nl_iterator(const nl_iterator&);    // hide copy constructor to prevent copies
};

class WifiRequest
{
private:
    int mFamily;
    int mIface;
    struct nl_msg *mMsg;

public:
    WifiRequest(int family) {
        mMsg = NULL;
        mFamily = family;
        mIface = -1;
    }

    WifiRequest(int family, int iface) {
        mMsg = NULL;
        mFamily = family;
        mIface = iface;
    }

    ~WifiRequest() {
        destroy();
    }

    void destroy() {
        if (mMsg) {
            nlmsg_free(mMsg);
            mMsg = NULL;
        }
    }

    nl_msg *getMessage() {
        return mMsg;
    }

    /* Command assembly helpers */
    int create(int family, uint8_t cmd, int flags, int hdrlen);
    int create(uint8_t cmd, int flags, int hdrlen) {
        return create(mFamily, cmd, flags, hdrlen);
    }
    int create(uint8_t cmd) {
        return create(mFamily, cmd, 0, 0);
    }

    int create(uint32_t id, int subcmd);

    int put_u8(int attribute, uint8_t value) {
        return nla_put(mMsg, attribute, sizeof(value), &value);
    }
    int put_u16(int attribute, uint16_t value) {
        return nla_put(mMsg, attribute, sizeof(value), &value);
    }
    int put_u32(int attribute, uint32_t value) {
        return nla_put(mMsg, attribute, sizeof(value), &value);
    }

    int put_u64(int attribute, uint64_t value) {
        return nla_put(mMsg, attribute, sizeof(value), &value);
    }

    int put_s8(int attribute, s8 value) {
        return nla_put(mMsg, attribute, sizeof(int8_t), &value);
    }
    int put_s16(int attribute, s16 value) {
        return nla_put(mMsg, attribute, sizeof(int16_t), &value);
    }
    int put_s32(int attribute, s32 value) {
        return nla_put(mMsg, attribute, sizeof(int32_t), &value);
    }
    int put_s64(int attribute, s64 value) {
        return nla_put(mMsg, attribute, sizeof(int64_t), &value);
    }

    u8 get_u8(const struct nlattr *nla)
    {
        return *(u8 *) nla_data(nla);
    }
    u16 get_u16(const struct nlattr *nla)
    {
        return *(u16 *) nla_data(nla);
    }
    u32 get_u32(const struct nlattr *nla)
    {
        return *(u32 *) nla_data(nla);
    }
    u64 get_u64(const struct nlattr *nla)
    {
        return *(u64 *) nla_data(nla);
    }

    s8 get_s8(const struct nlattr *nla)
    {
        return *(s8 *) nla_data(nla);
    }

    s16 get_s16(const struct nlattr *nla)
    {
        return *(s16 *) nla_data(nla);
    }
    s32 get_s32(const struct nlattr *nla)
    {
        return *(s32 *) nla_data(nla);
    }
    s64 get_s64(const struct nlattr *nla)
    {
        return *(s64 *) nla_data(nla);
    }

    int put_string(int attribute, const char *value) {
        return nla_put(mMsg, attribute, strlen(value) + 1, value);
    }
    int put_addr(int attribute, mac_addr value) {
        return nla_put(mMsg, attribute, sizeof(mac_addr), value);
    }

    struct nlattr * attr_start(int attribute) {
        return nla_nest_start(mMsg, attribute);
    }
    void attr_end(struct nlattr *attr) {
        nla_nest_end(mMsg, attr);
    }

    int set_iface_id(int ifindex) {
        return put_u32(NL80211_ATTR_IFINDEX, ifindex);
    }

    int put_bytes(int attribute, const char *data, int len) {
        return nla_put(mMsg, attribute, len, data);
    }

private:
    WifiRequest(const WifiRequest&);        // hide copy constructor to prevent copies

};

class WifiCommand
{
protected:
    hal_info *mInfo;
    WifiRequest mMsg;
    Condition mCondition;
    wifi_request_id mId;
    interface_info *mIfaceInfo;
public:
    WifiCommand(wifi_handle handle, wifi_request_id id)
            : mMsg(getHalInfo(handle)->nl80211_family_id), mId(id)
    {
        mIfaceInfo = NULL;
        mInfo = getHalInfo(handle);
    }

    WifiCommand(wifi_interface_handle iface, wifi_request_id id)
            : mMsg(getHalInfo(iface)->nl80211_family_id, getIfaceInfo(iface)->id), mId(id)
    {
        mIfaceInfo = getIfaceInfo(iface);
        mInfo = getHalInfo(iface);
    }

    virtual ~WifiCommand() {
    }

    wifi_request_id id() {
        return mId;
    }

    virtual int create() {
        /* by default there is no way to cancel */
        return WIFI_ERROR_NOT_SUPPORTED;
    }

    virtual int cancel() {
        /* by default there is no way to cancel */
        return WIFI_ERROR_NOT_SUPPORTED;
    }

    int requestResponse();
    int requestEvent(int cmd);
    int requestVendorEvent(uint32_t id, int subcmd);
    int requestResponse(WifiRequest& request);

protected:
    wifi_handle wifiHandle() {
        return getWifiHandle(mInfo);
    }

    wifi_interface_handle ifaceHandle() {
        return getIfaceHandle(mIfaceInfo);
    }

    int familyId() {
        return mInfo->nl80211_family_id;
    }

    int ifaceId() {
        return mIfaceInfo->id;
    }

    /* Override this method to parse reply and dig out data; save it in the object */
    virtual int handleResponse(WifiEvent& reply) {
        UNUSED(reply);
        return NL_SKIP;
    }

    /* Override this method to parse event and dig out data; save it in the object */
    virtual int handleEvent(WifiEvent& event) {
        UNUSED(event);
        return NL_SKIP;
    }

    int registerHandler(int cmd) {
        return wifi_register_handler(wifiHandle(), cmd, &event_handler, this);
    }

    void unregisterHandler(int cmd) {
        wifi_unregister_handler(wifiHandle(), cmd);
    }

    int registerVendorHandler(uint32_t id, int subcmd) {
        return wifi_register_vendor_handler(wifiHandle(), id, subcmd, &event_handler, this);
    }

    void unregisterVendorHandler(uint32_t id, int subcmd) {
        wifi_unregister_vendor_handler(wifiHandle(), id, subcmd);
    }

private:
    WifiCommand(const WifiCommand& );           // hide copy constructor to prevent copies

    /* Event handling */
    static int response_handler(struct nl_msg *msg, void *arg);

    static int event_handler(struct nl_msg *msg, void *arg);

    /* Other event handlers */
    static int valid_handler(struct nl_msg *msg, void *arg);

    static int ack_handler(struct nl_msg *msg, void *arg);

    static int finish_handler(struct nl_msg *msg, void *arg);

    static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg);
};

//WifiVendorCommand class
class WifiVendorCommand: public WifiCommand
{
protected:
    u32 mVendor_id;
    u32 mSubcmd;
    char *mVendorData;
    u32 mDataLen;


public:
    WifiVendorCommand(wifi_handle handle, wifi_request_id id, u32 vendor_id, u32 subcmd);

    virtual ~WifiVendorCommand();

    virtual int create();

    virtual int requestResponse();

    virtual int requestEvent();

    virtual int put_u8(int attribute, uint8_t value);

    virtual int put_u16(int attribute, uint16_t value);

    virtual int put_u32(int attribute, uint32_t value);

    virtual int put_u64(int attribute, uint64_t value);

    virtual int put_s8(int attribute, s8 value);

    virtual int put_s16(int attribute, s16 value);

    virtual int put_s32(int attribute, s32 value);

    virtual int put_s64(int attribute, s64 value);

    virtual u8 get_u8(const struct nlattr *nla);
    virtual u16 get_u16(const struct nlattr *nla);
    virtual u32 get_u32(const struct nlattr *nla);
    virtual u64 get_u64(const struct nlattr *nla);

    virtual s8 get_s8(const struct nlattr *nla);
    virtual s16 get_s16(const struct nlattr *nla);
    virtual s32 get_s32(const struct nlattr *nla);
    virtual s64 get_s64(const struct nlattr *nla);

    virtual int put_string(int attribute, const char *value);

    virtual int put_addr(int attribute, mac_addr value);

    virtual struct nlattr * attr_start(int attribute);

    virtual void attr_end(struct nlattr *attribute);

    virtual int set_iface_id(const char* name);

    virtual int put_bytes(int attribute, const char *data, int len);

    virtual wifi_error get_mac_addr(struct nlattr **tb_vendor,
                                int attribute,
                                mac_addr addr);

protected:

    /* Override this method to parse reply and dig out data; save it in the corresponding
       object */
    virtual int handleResponse(WifiEvent &reply);

    /* Override this method to parse event and dig out data; save it in the object */
    virtual int handleEvent(WifiEvent &event);
};

/* nl message processing macros (required to pass C++ type checks) */

#define for_each_attr(pos, nla, rem) \
    for (pos = (nlattr *)nla_data(nla), rem = nla_len(nla); \
        nla_ok(pos, rem); \
        pos = (nlattr *)nla_next(pos, &(rem)))

wifi_error initialize_vendor_cmd(wifi_interface_handle iface,
                                 wifi_request_id id,
                                 u32 subcmd,
                                 WifiVendorCommand **vCommand);
#endif