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

#include "src/privet/wifi_ssid_generator.h"

#include <bitset>

#include <base/bind.h>
#include <base/rand_util.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/stringprintf.h>

#include "src/privet/cloud_delegate.h"
#include "src/privet/device_delegate.h"
#include "src/privet/wifi_delegate.h"

namespace weave {
namespace privet {

namespace {

const int kDeviceNameSize = 20;
// [DeviceName+Idx <= 20].[modelID == 5][flags == 2]prv
const char kSsidFormat[] = "%s %s.%5.5s%2.2sprv";

const char base64chars[] =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

bool IsSetupNeeded(const ConnectionState& state) {
  if (state.error())
    return true;
  switch (state.status()) {
    case ConnectionState::kUnconfigured:
      return true;
    case ConnectionState::kDisabled:
    case ConnectionState::kConnecting:
    case ConnectionState::kOnline:
    case ConnectionState::kOffline:
      return false;
  }
  CHECK(false);
  return false;
}

}  // namespace

WifiSsidGenerator::WifiSsidGenerator(const CloudDelegate* cloud,
                                     const WifiDelegate* wifi)
    : gcd_(cloud), wifi_(wifi), get_random_(base::Bind(&base::RandInt, 0, 99)) {
  CHECK(gcd_);
}

std::string WifiSsidGenerator::GenerateFlags() const {
  return GenerateFlagsInternal();
}

std::string WifiSsidGenerator::GenerateFlagsInternal() const {
  std::bitset<6> flags1;
  // Device needs WiFi configuration.
  flags1[0] = wifi_ && IsSetupNeeded(wifi_->GetConnectionState());

  // Device needs GCD registration.
  flags1[1] = IsSetupNeeded(gcd_->GetConnectionState());

  std::bitset<6> flags2;

  if (wifi_) {
    std::set<WifiType> types = wifi_->GetTypes();
    // Device supports 2.4Ghz WiFi networks.
    flags2[0] = types.find(WifiType::kWifi24) != types.end();

    // Device supports 5.0Ghz WiFi networks.
    flags2[1] = types.find(WifiType::kWifi50) != types.end();
  }

  std::string result{2, base64chars[0]};
  result[0] = base64chars[flags1.to_ulong()];
  result[1] = base64chars[flags2.to_ulong()];
  return result;
}

std::string WifiSsidGenerator::GenerateSsid() const {
  std::string name = gcd_->GetName();
  std::string model_id = gcd_->GetModelId();
  std::string idx = base::IntToString(get_random_.Run());
  name = name.substr(0, kDeviceNameSize - idx.size() - 1);
  CHECK_EQ(5u, model_id.size());

  std::string result =
      base::StringPrintf(kSsidFormat, name.c_str(), idx.c_str(),
                         model_id.c_str(), GenerateFlagsInternal().c_str());
  CHECK_EQ(result[result.size() - 11], '.');
  return result;
}

void WifiSsidGenerator::SetRandomForTests(int n) {
  get_random_ = base::Bind(&base::RandInt, n, n);
}

}  // namespace privet
}  // namespace weave