//
//  Copyright (C) 2016 Google, Inc.
//
//  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.
//
#include <rapidjson/document.h>
#include <rapidjson/writer.h>
#include <rapidjson/stringbuffer.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>

#include <base.h>
#include <base/at_exit.h>
#include <base/command_line.h>
#include <base/logging.h>
#include <base/macros.h>
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include <utils/command_receiver.h>
#include <utils/common_utils.h>
#include <hardware_legacy/wifi_hal.h>
#include <wifi_system/hal_tool.h>
#include <wifi_system/interface_tool.h>

#include "wifi_facade.h"

const char kWlanInterface[] = "wlan0";
const char kP2pInterface[] = "p2p0";

std::tuple<bool, int> WifiFacade::WifiInit() {
  if (!WifiStartHal()) {
    return std::make_tuple(false, sl4n_error_codes::kFailInt);
  }

  if (!WifiGetInterfaces() || wlan0_index == -1) {
    return std::make_tuple(false, sl4n_error_codes::kFailInt);
  }

  return std::make_tuple(true, sl4n_error_codes::kPassInt);
}

bool WifiFacade::WifiStartHal() {
  android::wifi_system::InterfaceTool if_tool;
  if (wifi_hal_handle == NULL) {
    android::wifi_system::HalTool hal_tool;
    if (!hal_tool.InitFunctionTable(&hal_fn)) {
      return false;
    }

    if (!if_tool.SetWifiUpState(true)) {
      return false;
    }

    res = hal_fn.wifi_initialize(&wifi_hal_handle);
    return res == WIFI_SUCCESS;
  } else {
    return if_tool.SetWifiUpState(true);
  }
}

bool WifiFacade::WifiGetInterfaces() {
  int num_ifaces;
  int result = hal_fn.wifi_get_ifaces(wifi_hal_handle, &num_ifaces,
                                      &wifi_iface_handles);
  if (result < 0) {
    LOG(ERROR) << sl4n::kTagStr << ": Can not get Wi-Fi interfaces";
    return false;
  }

  if (num_ifaces < 0) {
    LOG(ERROR) << sl4n::kTagStr << ": Negative number of interfaces";
    return false;
  }

  if (wifi_iface_handles == NULL) {
    LOG(ERROR) << sl4n::kTagStr
        << "wifi_get_ifaces returned null interface array";
    return false;
  }

  if (num_ifaces > 8) {
    LOG(ERROR) << sl4n::kTagStr
        << "wifi_get_ifaces returned too many interfaces";
    return false;
  }

  char buf[128];
  for (int i = 0; i < num_ifaces; ++i) {
    int result = hal_fn.wifi_get_iface_name(wifi_iface_handles[i], buf,
                                            sizeof(buf));
    if (result < 0) {
      LOG(ERROR) << sl4n::kTagStr
          << "Can't obtain interface name for interface #" << i;
      continue;
    }
    if (!strcmp(buf, kWlanInterface)) {
      wlan0_index = i;
    } else if (!strcmp(buf, kP2pInterface)) {
      p2p0_index = i;
    }
  }

  return true;
}

bool WifiFacade::SharedValidator() {
  if (wifi_hal_handle == NULL) {
    LOG(ERROR) << sl4n::kTagStr << "HAL handle not initialized";
    return false;
  }

  if (wifi_iface_handles == NULL) {
    LOG(ERROR) << sl4n::kTagStr << "HAL interfaces not initialized";
    return false;
  }

  if (wlan0_index == -1) {
    LOG(ERROR) << sl4n::kTagStr << kWlanInterface << " interface not found";
    return false;
  }

  return true;
}

std::tuple<int, int> WifiFacade::WifiGetSupportedFeatureSet() {
  if (!SharedValidator()) {
    return std::make_tuple(0, sl4n_error_codes::kFailInt);
  }

  feature_set set = 0;
  int result = hal_fn.wifi_get_supported_feature_set(
      wifi_iface_handles[wlan0_index], &set);
  if (result == WIFI_SUCCESS) {
    return std::make_tuple(set, sl4n_error_codes::kPassInt);
  } else {
    return std::make_tuple(0, sl4n_error_codes::kFailInt);
  }
}

//////////////////
// wrappers
/////////////////

static WifiFacade facade;  // triggers registration with CommandReceiver

void wifi_init_wrapper(rapidjson::Document &doc) {
  int expected_param_size = 0;
  if (!CommonUtils::IsParamLengthMatching(doc, expected_param_size)) {
    return;
  }
  bool result;
  int error_code;
  std::tie(result, error_code) = facade.WifiInit();
  if (error_code == sl4n_error_codes::kFailInt) {
    doc.AddMember(sl4n::kResultStr, false, doc.GetAllocator());
    doc.AddMember(sl4n::kErrorStr, sl4n::kFailStr, doc.GetAllocator());
  } else {
    doc.AddMember(sl4n::kResultStr, result, doc.GetAllocator());
    doc.AddMember(sl4n::kErrorStr, NULL, doc.GetAllocator());
  }
}

void wifi_get_supported_feature_set_wrapper(rapidjson::Document &doc) {
  int expected_param_size = 0;
  if (!CommonUtils::IsParamLengthMatching(doc, expected_param_size)) {
    return;
  }
  int result;
  int error_code;
  std::tie(result, error_code) = facade.WifiGetSupportedFeatureSet();
  if (error_code == sl4n_error_codes::kFailInt) {
    doc.AddMember(sl4n::kResultStr, false, doc.GetAllocator());
    doc.AddMember(sl4n::kErrorStr, sl4n::kFailStr, doc.GetAllocator());
  } else {
    doc.AddMember(sl4n::kResultStr, result, doc.GetAllocator());
    doc.AddMember(sl4n::kErrorStr, NULL, doc.GetAllocator());
  }
}

////////////////
// constructor
////////////////

WifiFacade::WifiFacade() {
  wifi_hal_handle = NULL;
  wifi_iface_handles = NULL;
  num_wifi_iface_handles = 0;
  wlan0_index = -1;
  p2p0_index = -1;

  CommandReceiver::RegisterCommand("WifiInit", &wifi_init_wrapper);
  CommandReceiver::RegisterCommand("WifiGetSupportedFeatureSet",
                                   &wifi_get_supported_feature_set_wrapper);
}