/*
 * Copyright 2020 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.
 */

#include "test/headless/get_options.h"

#include <getopt.h>
#include <unistd.h>

#include <list>
#include <sstream>
#include <string>

#include "types/bluetooth/uuid.h"
#include "types/raw_address.h"

namespace {
enum OptionType {
  kOptionDevice = 0,
  kOptionLoop = 1,
  kOptionUuid = 2,
  kOptionMsleep = 3,
  kOptionStdErr = 4,
  kOptionFlags = 5,
  kOptionClear = 6,
};

constexpr struct option long_options[] = {{"device", required_argument, 0, 0},  // kOptionDevice
                                          {"loop", required_argument, 0, 0},    // kOptionLoop/
                                          {"uuid", required_argument, 0, 0},    // kOptionUuid
                                          {"msleep", required_argument, 0, 0},  // kOptionMsleep
                                          {"stderr", no_argument, 0, 0},        // kOptionStdErr
                                          {"flags", required_argument, 0, 0},   // kOptionFlags
                                          {"clear", no_argument, 0, 0},         // kOptionDevice
                                          {0, 0, 0, 0}};

const char* kShortArgs = "cd:l:u:";

}  // namespace

void bluetooth::test::headless::GetOpt::Usage() const {
  fprintf(stdout, "%s: Usage:\n", name_);
  fprintf(stdout, "%s  -c  Clear logcat logs\n", name_);
  fprintf(stdout, "%s  --device=<device,>  Comma separated list of remote devices\n", name_);
  fprintf(stdout, "%s  --uuid=<uuid,>      Comma separated list of uuids\n", name_);
  fprintf(stdout, "%s  --loop=<loop>       Number of loops\n", name_);
  fprintf(stdout, "%s  --msleep=<msecs>    Sleep msec between loops\n", name_);
  fprintf(stdout, "%s  --stderr            Dump stderr to stdout\n", name_);
  fflush(nullptr);
}

void bluetooth::test::headless::GetOpt::ParseValue(char* optarg,
                                                   std::list<std::string>& string_list) {
  log::assert_that(optarg != nullptr, "assert failed: optarg != nullptr");
  char* p = optarg;
  char* pp = optarg;
  while (*p != '\0') {
    if (*p == ',') {
      *p = 0;
      string_list.push_back(std::string(pp));
      pp = p + 1;
    }
    p++;
  }
  if (pp != p) {
    string_list.push_back(std::string(pp));
  }
}

std::vector<std::string> bluetooth::test::headless::GetOpt::Split(std::string s) {
  std::stringstream ss(s);
  std::vector<std::string> values;
  std::string item;
  while (std::getline(ss, item, '=')) {
    values.push_back(item);
  }
  return values;
}

void bluetooth::test::headless::GetOpt::ProcessOption(int option_index, char* optarg) {
  std::list<std::string> string_list;
  OptionType option_type = static_cast<OptionType>(option_index);

  switch (option_type) {
    case kOptionDevice:
      if (!optarg) {
        return;
      }
      ParseValue(optarg, string_list);
      for (auto& entry : string_list) {
        if (RawAddress::IsValidAddress(entry)) {
          RawAddress address;
          RawAddress::FromString(entry, address);
          device_.push_back(address);
        }
      }
      break;
    case kOptionLoop:
      loop_ = std::stoul(optarg, nullptr, 0);
      break;
    case kOptionUuid:
      if (!optarg) {
        return;
      }
      ParseValue(optarg, string_list);
      for (auto& entry : string_list) {
        uuid_.push_back(bluetooth::Uuid::From16Bit(std::stoul(entry.c_str(), nullptr, 0)));
      }
      break;
    case kOptionMsleep:
      if (!optarg) {
        return;
      }
      msec_ = std::stoul(optarg, nullptr, 0);
      break;
    case kOptionStdErr:
      close_stderr_ = false;
      break;
    case kOptionClear:
      clear_logcat_ = true;
      break;
    default:
      fflush(nullptr);
      valid_ = false;
      return;
      break;
  }
}

bluetooth::test::headless::GetOpt::GetOpt(int argc, char** argv) : name_(argv[0]) {
  while (1) {
    int option_index = 0;
    int c = getopt_long_only(argc, argv, kShortArgs, long_options, &option_index);
    if (c == -1) {
      break;
    }

    switch (c) {
      case 0:
        ProcessOption(static_cast<OptionType>(option_index), optarg);
        break;
      case '?':
        Usage();
        valid_ = false;
        return;
      default:
        printf("?? getopt returned character code 0%o ??\n", c);
    }
  }

  while (optind < argc) {
    non_options_.push_back(argv[optind++]);
  }

  fflush(nullptr);
}

bluetooth::test::headless::GetOpt::~GetOpt() {}
