// Copyright (C) 2018 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 FIXED_ARGV_H_
#define FIXED_ARGV_H_

#include <array>
#include <tuple>
#include <utility>
#include <vector>

#include <assert.h>
#include <string.h>


class FixedArgvAccess;


class FixedArgv {
  friend FixedArgvAccess;

 private:
  std::vector<const char *> argv_;

 public:
  FixedArgv(int argc, const char **argv) : argv_(argv, argv + argc) {}

  int GetArgc() const {
    return argv_.size();
  }

  const char *const *GetArgv() const {
    return argv_.data();
  }

  void Resize(int argc) {
    assert(argc <= argv_.size());
    argv_.resize(argc);
  }

  template <typename... T>
  const char *GetLastArg(T&& ...options) const {
    std::array<const char *, sizeof...(options)> opts{
        std::forward<T&&>(options)...};
    for (std::vector<const char *>::const_reverse_iterator it = argv_.rbegin(),
         end = argv_.rend(); it != end; ++it) {
      for (const char *opt : opts) {
        if (::strcmp(*it, opt) == 0) {
          return opt;
        }
      }
    }
    return nullptr;
  }

  template <typename... T>
  bool IsLastArgEqualFirstOption(const char *expected, T&& ...others) const {
    const char *last = GetLastArg(expected, others...);
    // Since GetLastArg() returns the address in {expected, others...}, pointer
    // comparison is sufficient.
    return last == expected;
  }

  template<typename... T>
  void PushForwardArgs(T&& ...arguments) {
    std::array<const char *, sizeof...(arguments)> args{
        std::forward<T&&>(arguments)...};
    if (!GetLastArg("--")) {
      argv_.push_back("--");
    }
    argv_.insert(argv_.end(), args.begin(), args.end());
  }
};


class FixedArgvAccess {
 private:
  FixedArgv &fixed_argv_;

 public:
  int argc_;
  const char **argv_;

 public:
  explicit FixedArgvAccess(FixedArgv &fixed_argv)
      : fixed_argv_(fixed_argv), argc_(fixed_argv.GetArgc()),
        argv_(fixed_argv.argv_.data()) {
  }

  ~FixedArgvAccess() {
    fixed_argv_.Resize(argc_);
  }

 private:
  FixedArgvAccess(const FixedArgvAccess &) = delete;
  FixedArgvAccess& operator=(const FixedArgvAccess &rhs) = delete;
};


class FixedArgvRegistry {
 public:
  typedef void (Function)(FixedArgv &);

 private:
  static FixedArgvRegistry *head_;

  Function *func_;
  FixedArgvRegistry *next_;

 public:
  FixedArgvRegistry(Function *func) : func_(func), next_(head_) {
    head_ = this;
  }

  static void Apply(FixedArgv &fixed_argv) {
    for (FixedArgvRegistry *ptr = head_; ptr; ptr = ptr->next_) {
      ptr->func_(fixed_argv);
    }
  }
};


#endif  // FIXED_ARGV_H_