/*
 * libjingle
 * Copyright 2006, Google Inc.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice, 
 *     this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *  3. The name of the author may not be used to endorse or promote products 
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

// Originally comes from shared/commandlineflags/flags.h

// Flags are defined and declared using DEFINE_xxx and DECLARE_xxx macros,
// where xxx is the flag type. Flags are referred to via FLAG_yyy,
// where yyy is the flag name. For intialization and iteration of flags,
// see the FlagList class. For full programmatic access to any
// flag, see the Flag class.
//
// The implementation only relies and basic C++ functionality
// and needs no special library or STL support.

#ifndef TALK_BASE_FLAGS_H__
#define TALK_BASE_FLAGS_H__

#include <assert.h>

#include "talk/base/checks.h"
#include "talk/base/common.h"

// Internal use only.
union FlagValue {
  // Note: Because in C++ non-bool values are silently converted into
  // bool values ('bool b = "false";' results in b == true!), we pass
  // and int argument to New_BOOL as this appears to be safer - sigh.
  // In particular, it prevents the (not uncommon!) bug where a bool
  // flag is defined via: DEFINE_bool(flag, "false", "some comment");.
  static FlagValue New_BOOL(int b) {
    FlagValue v;
    v.b = (b != 0);
    return v;
  }

  static FlagValue New_INT(int i) {
    FlagValue v;
    v.i = i;
    return v;
  }

  static FlagValue New_FLOAT(float f) {
    FlagValue v;
    v.f = f;
    return v;
  }

  static FlagValue New_STRING(const char* s) {
    FlagValue v;
    v.s = s;
    return v;
  }

  bool b;
  int i;
  double f;
  const char* s;
};

// Each flag can be accessed programmatically via a Flag object.
class Flag {
 public:
  enum Type { BOOL, INT, FLOAT, STRING };

  // Internal use only.
  Flag(const char* file, const char* name, const char* comment,
       Type type, void* variable, FlagValue default_);

  // General flag information
  const char* file() const  { return file_; }
  const char* name() const  { return name_; }
  const char* comment() const  { return comment_; }

  // Flag type
  Type type() const  { return type_; }

  // Flag variables
  bool* bool_variable() const {
    assert(type_ == BOOL);
    return &variable_->b;
  }
  
  int* int_variable() const {
    assert(type_ == INT);
    return &variable_->i;
  }
  
  double* float_variable() const {
    assert(type_ == FLOAT);
    return &variable_->f;
  }
  
  const char** string_variable() const {
    assert(type_ == STRING);
    return &variable_->s;
  }

  // Default values
  bool bool_default() const {
    assert(type_ == BOOL);
    return default_.b;
  }
  
  int int_default() const {
    assert(type_ == INT);
    return default_.i;
  }
  
  double float_default() const {
    assert(type_ == FLOAT);
    return default_.f;
  }
  
  const char* string_default() const {
    assert(type_ == STRING);
    return default_.s;
  }

  // Resets a flag to its default value
  void SetToDefault();

  // Iteration support
  Flag* next() const  { return next_; }

  // Prints flag information. The current flag value is only printed
  // if print_current_value is set.
  void Print(bool print_current_value);

 private:
  const char* file_;
  const char* name_;
  const char* comment_;

  Type type_;
  FlagValue* variable_;
  FlagValue default_;

  Flag* next_;

  friend class FlagList;  // accesses next_
};

// Internal use only.
#define DEFINE_FLAG(type, c_type, name, default, comment) \
  /* define and initialize the flag */                    \
  c_type FLAG_##name = (default);                         \
  /* register the flag */                                 \
  static Flag Flag_##name(__FILE__, #name, (comment),   \
                          Flag::type, &FLAG_##name,       \
                          FlagValue::New_##type(default))

// Internal use only.
#define DECLARE_FLAG(c_type, name)              \
  /* declare the external flag */               \
  extern c_type FLAG_##name

// Use the following macros to define a new flag:
#define DEFINE_bool(name, default, comment) \
  DEFINE_FLAG(BOOL, bool, name, default, comment)
#define DEFINE_int(name, default, comment) \
  DEFINE_FLAG(INT, int, name, default, comment)
#define DEFINE_float(name, default, comment) \
  DEFINE_FLAG(FLOAT, double, name, default, comment)
#define DEFINE_string(name, default, comment) \
  DEFINE_FLAG(STRING, const char*, name, default, comment)

// Use the following macros to declare a flag defined elsewhere:
#define DECLARE_bool(name)  DECLARE_FLAG(bool, name)
#define DECLARE_int(name)  DECLARE_FLAG(int, name)
#define DECLARE_float(name)  DECLARE_FLAG(double, name)
#define DECLARE_string(name)  DECLARE_FLAG(const char*, name)

// The global list of all flags.
class FlagList {
 public:
  FlagList();

  // The NULL-terminated list of all flags. Traverse with Flag::next().
  static Flag* list()  { return list_; }

  // If file != NULL, prints information for all flags defined in file;
  // otherwise prints information for all flags in all files. The current
  // flag value is only printed if print_current_value is set.
  static void Print(const char* file, bool print_current_value);

  // Lookup a flag by name. Returns the matching flag or NULL.
  static Flag* Lookup(const char* name);

  // Helper function to parse flags: Takes an argument arg and splits it into
  // a flag name and flag value (or NULL if they are missing). is_bool is set
  // if the arg started with "-no" or "--no". The buffer may be used to NUL-
  // terminate the name, it must be large enough to hold any possible name.
  static void SplitArgument(const char* arg,
                            char* buffer, int buffer_size,
                            const char** name, const char** value,
                            bool* is_bool);

  // Set the flag values by parsing the command line. If remove_flags
  // is set, the flags and associated values are removed from (argc,
  // argv). Returns 0 if no error occurred. Otherwise, returns the
  // argv index > 0 for the argument where an error occurred. In that
  // case, (argc, argv) will remain unchanged indepdendent of the
  // remove_flags value, and no assumptions about flag settings should
  // be made.
  //
  // The following syntax for flags is accepted (both '-' and '--' are ok):
  //
  //   --flag        (bool flags only)
  //   --noflag      (bool flags only)
  //   --flag=value  (non-bool flags only, no spaces around '=')
  //   --flag value  (non-bool flags only)
  static int SetFlagsFromCommandLine(int* argc,
                                     const char** argv,
                                     bool remove_flags);
  static inline int SetFlagsFromCommandLine(int* argc,
                                            char** argv,
                                            bool remove_flags) {
    return SetFlagsFromCommandLine(argc, const_cast<const char**>(argv),
                                   remove_flags);
  }

  // Registers a new flag. Called during program initialization. Not
  // thread-safe.
  static void Register(Flag* flag);

 private:
  static Flag* list_;
};

#ifdef WIN32
// A helper class to translate Windows command line arguments into UTF8,
// which then allows us to just pass them to the flags system.
// This encapsulates all the work of getting the command line and translating
// it to an array of 8-bit strings; all you have to do is create one of these,
// and then call argc() and argv().
class WindowsCommandLineArguments {
 public:
  WindowsCommandLineArguments();
  ~WindowsCommandLineArguments();

  int argc() { return argc_; }
  char **argv() { return argv_; }
 private:
  int argc_;
  char **argv_;

 private:
  DISALLOW_EVIL_CONSTRUCTORS(WindowsCommandLineArguments);
};
#endif  // WIN32

#endif  // SHARED_COMMANDLINEFLAGS_FLAGS_H__
