#pragma once

#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <type_traits>

namespace opt
{
  class Opt
  {
  private:
    int argc;
    char** argv;

  public:
    Opt(int argc, char** argv) : argc(argc), argv(argv) {}

    bool has(const char* opt)
    {
      for (int i = 1; i < argc; i++)
      {
        if (!strcmp(opt, argv[i]))
          return true;
      }

      return false;
    }

    template<class T>
    T is(const char* opt, T def)
    {
      size_t len = strlen(opt);

      for (int i = 1; i < argc; i++)
      {
        const char* p = param(opt, len, i);

        if (p != nullptr)
        {
          char* end = nullptr;
          T r;

          if (std::is_unsigned<T>::value)
            r = (T)strtoull(p, &end, 10);
          else
            r = (T)strtoll(p, &end, 10);

          if ((r == 0) && (end == p))
            return def;

          return r;
        }
      }

      return def;
    }

    const char* is(const char* opt, const char* def)
    {
      size_t len = strlen(opt);

      for (int i = 1; i < argc; i++)
      {
        const char* p = param(opt, len, i);

        if (p != nullptr)
          return p;
      }

      return def;
    }

  private:
    const char* param(const char* opt, size_t len, int i)
    {
      if (strncmp(opt, argv[i], len))
        return nullptr;

      switch (argv[i][len])
      {
        case '\0':
          return (i < (argc - 1)) ? argv[i + 1] : nullptr;
        case '=':
          return &argv[i][len + 1];
        default:
          return nullptr;
      }
    }
  };
}
