//===--- Option.h - Abstract Driver Options ---------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#ifndef CLANG_DRIVER_OPTION_H_
#define CLANG_DRIVER_OPTION_H_

#include "clang/Driver/OptTable.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/ErrorHandling.h"
#include "clang/Basic/LLVM.h"

namespace clang {
namespace driver {
  class Arg;
  class ArgList;

namespace options {
  enum DriverFlag {
    DriverOption     = (1 << 0),
    HelpHidden       = (1 << 1),
    LinkerInput      = (1 << 2),
    NoArgumentUnused = (1 << 3),
    NoForward        = (1 << 4),
    RenderAsInput    = (1 << 5),
    RenderJoined     = (1 << 6),
    RenderSeparate   = (1 << 7),
    Unsupported      = (1 << 8),
    CC1Option        = (1 << 9)
  };
}

  /// Option - Abstract representation for a single form of driver
  /// argument.
  ///
  /// An Option class represents a form of option that the driver
  /// takes, for example how many arguments the option has and how
  /// they can be provided. Individual option instances store
  /// additional information about what group the option is a member
  /// of (if any), if the option is an alias, and a number of
  /// flags. At runtime the driver parses the command line into
  /// concrete Arg instances, each of which corresponds to a
  /// particular Option instance.
  class Option {
  public:
    enum OptionClass {
      GroupClass = 0,
      InputClass,
      UnknownClass,
      FlagClass,
      JoinedClass,
      SeparateClass,
      CommaJoinedClass,
      MultiArgClass,
      JoinedOrSeparateClass,
      JoinedAndSeparateClass
    };

    enum RenderStyleKind {
      RenderCommaJoinedStyle,
      RenderJoinedStyle,
      RenderSeparateStyle,
      RenderValuesStyle
    };

  private:
    const OptTable::Info *Info;

    /// The option ID.
    OptSpecifier ID;

    /// Group this option is a member of, if any.
    const Option *Group;

    /// Option that this is an alias for, if any.
    const Option *Alias;

  public:
    Option(const OptTable::Info *Info, OptSpecifier ID,
           const Option *Group, const Option *Alias);
    ~Option();

    unsigned getID() const { return ID.getID(); }
    OptionClass getKind() const { return OptionClass(Info->Kind); }
    StringRef getName() const { return Info->Name; }
    const Option *getGroup() const { return Group; }
    const Option *getAlias() const { return Alias; }

    unsigned getNumArgs() const { return Info->Param; }

    bool isUnsupported() const { return Info->Flags & options::Unsupported; }

    bool isLinkerInput() const { return Info->Flags & options::LinkerInput; }

    bool hasNoOptAsInput() const { return Info->Flags & options::RenderAsInput;}

    RenderStyleKind getRenderStyle() const {
      if (Info->Flags & options::RenderJoined)
        return RenderJoinedStyle;
      if (Info->Flags & options::RenderSeparate)
        return RenderSeparateStyle;
      switch (getKind()) {
      case GroupClass:
      case InputClass:
      case UnknownClass:
        return RenderValuesStyle;
      case JoinedClass:
      case JoinedAndSeparateClass:
        return RenderJoinedStyle;
      case CommaJoinedClass:
        return RenderCommaJoinedStyle;
      case FlagClass:
      case SeparateClass:
      case MultiArgClass:
      case JoinedOrSeparateClass:
        return RenderSeparateStyle;
      }
      llvm_unreachable("Unexpected kind!");
    }

    bool isDriverOption() const { return Info->Flags & options::DriverOption; }

    bool hasNoArgumentUnused() const {
      return Info->Flags & options::NoArgumentUnused;
    }

    bool hasNoForward() const { return Info->Flags & options::NoForward; }

    bool isCC1Option() const { return Info->Flags & options::CC1Option; }

    bool hasForwardToGCC() const {
      return !hasNoForward() && !isDriverOption() && !isLinkerInput();
    }

    /// getUnaliasedOption - Return the final option this option
    /// aliases (itself, if the option has no alias).
    const Option *getUnaliasedOption() const {
      if (Alias) return Alias->getUnaliasedOption();
      return this;
    }

    /// getRenderName - Return the name to use when rendering this
    /// option.
    StringRef getRenderName() const {
      return getUnaliasedOption()->getName();
    }

    /// matches - Predicate for whether this option is part of the
    /// given option (which may be a group).
    ///
    /// Note that matches against options which are an alias should never be
    /// done -- aliases do not participate in matching and so such a query will
    /// always be false.
    bool matches(OptSpecifier ID) const;

    /// accept - Potentially accept the current argument, returning a
    /// new Arg instance, or 0 if the option does not accept this
    /// argument (or the argument is missing values).
    ///
    /// If the option accepts the current argument, accept() sets
    /// Index to the position where argument parsing should resume
    /// (even if the argument is missing values).
    Arg *accept(const ArgList &Args, unsigned &Index) const;

    void dump() const;
  };

} // end namespace driver
} // end namespace clang

#endif
