/*---------------------------------------------------------------------\
|                          ____ _   __ __ ___                          |
|                         |__  / \ / / . \ . \                         |
|                           / / \ V /|  _/  _/                         |
|                          / /__ | | | | | |                           |
|                         /_____||_| |_| |_|                           |
|                                                                      |
\---------------------------------------------------------------------*/
/** \file	zypp/Capability.h
 *
*/
#ifndef ZYPP_CAPABILITY_H
#define ZYPP_CAPABILITY_H

#include <iosfwd>

#include <zypp-core/Globals.h>
#include <zypp/sat/detail/PoolMember.h>

#include <zypp/IdString.h>
#include <zypp/Edition.h>
#include <zypp/Rel.h>
#include <zypp/ResTraits.h>
#include <zypp/ResolverNamespace.h>
#include <zypp/CapMatch.h>

///////////////////////////////////////////////////////////////////
namespace zypp
{ /////////////////////////////////////////////////////////////////

  class Capability;
  class CapDetail;
  class Arch;

  using CapabilitySet = std::unordered_set<Capability>;

  ///////////////////////////////////////////////////////////////////
  //
  //	CLASS NAME : Capability
  //
  /** A sat capability.
   *
   * A Capability: <tt>"name[.arch] [op edition]"</tt> or a \c richdep[1]
   *
   * If a certain \ref ResKind is specified upon construction, the
   * capabilities name part is prefixed, unless it already contains a
   * well known kind spec. If no \ref ResKind is specified, it's assumed
   * you refer to a package or the name is already prefixed:
   * \code
   * Capability( "foo" )                   ==> 'foo'
   * Capability( "foo", ResKind::package ) ==> 'foo'
   * Capability( "foo", ResKind::pattern ) ==> 'pattern:foo'
   * Capability( "pattern:foo" )           ==> 'pattern:foo'
   * // in doubt an explicit name prefix wins:
   * Capability( "pattern:foo", ResKind::package ) ==> 'pattern:foo'
   * Capability( "package:foo", ResKind::pattern ) ==> 'foo'
   * \endcode
   *
   * [1] https://rpm-software-management.github.io/rpm/manual/boolean_dependencies.html
   * \see \ref CapDetail
   */
  class ZYPP_API Capability: protected sat::detail::PoolMember
  {
    public:
      enum CtorFlag { PARSED, UNPARSED };

    public:
      /** Default ctor, \ref Empty capability. */
      Capability() : _id( sat::detail::emptyId ) {}

      /** Ctor from id. */
      explicit Capability( sat::detail::IdType id_r ) : _id( id_r ) {}

      /** \name Ctors parsing a Capability: <tt>"name[.arch] [op edition]"</tt> or <tt>( arch, "name [op edition]")</tt>
      */
      //@{
      /** Ctor from string.
       * \a str_r is parsed to see if it forms a richdep. Subsequent arguments are meaningless
       * in that case. If it's no richdep we continue as described below.
       *
       * \a str_r is parsed to check whether it contains an <tt>[op edition]</tt> part,
       * unless the \ref PARSED flag is passed to the ctor. In that case <tt>"name[.arch]"</tt>
       * is assumed.
       *
       * \ref Capability legacy is to parse everything unknown into a \ref NAMED
       * cap. This is often used to turn user supplied search strings with an optional
       * edition range restriction into \ref PoolQuery arguments ('/gcc[0-9]+/ >= 8').
       */
      explicit Capability( const char * str_r, const ResKind & prefix_r = ResKind(), CtorFlag flag_r = UNPARSED );
      /** \overload */
      explicit Capability( const std::string & str_r, const ResKind & prefix_r = ResKind(), CtorFlag flag_r = UNPARSED );
      /** \overload Explicitly specify the \c arch. */
      Capability( const Arch & arch_r, const char * str_r, const ResKind & prefix_r = ResKind(), CtorFlag flag_r = UNPARSED );
      /** \overload Explicitly specify the \c arch. */
      Capability( const Arch & arch_r, const std::string & str_r, const ResKind & prefix_r = ResKind(), CtorFlag flag_r = UNPARSED );

      /** \overload Convenience for parsed (name only, no <tt>"[op edition]</tt>) packages: <tt>Capability( "glibc", PARSED ); */
      Capability( const char * str_r, CtorFlag flag_r, const ResKind & prefix_r = ResKind() );
      /** \overload */
      Capability( const std::string & str_r, CtorFlag flag_r, const ResKind & prefix_r = ResKind() );
      /** \overload Explicitly specify the \c arch. */
      Capability( const Arch & arch_r, const char * str_r, CtorFlag flag_r, const ResKind & prefix_r = ResKind() );
      /** \overload */
      Capability( const Arch & arch_r, const std::string & str_r, CtorFlag flag_r, const ResKind & prefix_r = ResKind() );
      //@}


      /** \name Ctors parsing a broken down Capability: <tt>( "name[.arch]", op, edition )</tt>
      */
      //@{
      /** Ctor from <tt>name[.arch] op edition</tt>. */
      Capability( const std::string & name_r, const std::string & op_r, const std::string & ed_r, const ResKind & prefix_r = ResKind() );
      /** \overload */
      Capability( const std::string & name_r, Rel op_r, const std::string & ed_r, const ResKind & prefix_r = ResKind() );
      /** \overload */
      Capability( const std::string & name_r, Rel op_r, const Edition & ed_r, const ResKind & prefix_r = ResKind() );
      //@}

      /** \name Ctors taking a broken down Capability: <tt>( arch, name, op, edition )</tt>
      */
      //@{
      /** Ctor from <tt>arch name op edition</tt>. */
      Capability( const std::string & arch_r, const std::string & name_r, const std::string & op_r, const std::string & ed_r, const ResKind & prefix_r = ResKind() );
      /** \overload */
      Capability( const std::string & arch_r, const std::string & name_r, Rel op_r, const std::string & ed_r, const ResKind & prefix_r = ResKind() );
      /** \overload */
      Capability( const std::string & arch_r, const std::string & name_r, Rel op_r, const Edition & ed_r, const ResKind & prefix_r = ResKind() );
      /** \overload */
      Capability( const Arch & arch_r, const std::string & name_r, const std::string & op_r, const std::string & ed_r, const ResKind & prefix_r = ResKind() );
      /** \overload */
      Capability( const Arch & arch_r, const std::string & name_r, Rel op_r, const std::string & ed_r, const ResKind & prefix_r = ResKind() );
      /** \overload */
      Capability( const Arch & arch_r, const std::string & name_r, Rel op_r, const Edition & ed_r, const ResKind & prefix_r = ResKind() );
      //@}

      /** \name Ctor creating a namespace: capability.
       * An empty \a value_r (std::string or IdString) will also be mapped to IdString::Null,
       * creating a namespace: capability which in most contexts matches all members of this namespace.
       */
      //@{
      Capability( ResolverNamespace namespace_r, IdString value_r = IdString::Null );
      Capability( ResolverNamespace namespace_r, const char * value_r )		: Capability( namespace_r, IdString(value_r) ) {}
      Capability( ResolverNamespace namespace_r, const std::string & value_r )	: Capability( namespace_r, IdString(value_r) ) {}
      //@}
    public:
      /** No or Null \ref Capability ( Id \c 0 ). */
      static const Capability Null;

      /** Empty Capability. */
      static const Capability Empty;

    public:
      /** Evaluate in a boolean context <tt>( ! empty() )</tt>. */
      explicit operator bool() const
      { return ! empty(); }

      /** Whether the \ref Capability is empty.
       * This is true for \ref Null and \ref Empty.
       */
      bool empty() const
      { return( _id == sat::detail::emptyId || _id == sat::detail::noId ); }

    public:
      /** Conversion to <tt>const char *</tt> */
      const char * c_str() const;

      /** \overload */
      std::string asString() const
      { return c_str(); }

    public:
      /** Helper providing more detailed information about a \ref Capability. */
      CapDetail detail() const;

    public:
      /** \name Match two simple capabilities.
       *
       * Two simple capabilities match if they have the same \c name
       * and their \c edition ranges overlap. Where no edition matches
       * ANY edition. \see \ref Edition::match.
       *
       * If a capability expression is involved, \ref matches returns
       * \ref CapMatch::irrelevant.
       */
      //@{
      static CapMatch matches( const Capability & lhs,  const Capability & rhs )     { return _doMatch( lhs.id(), rhs.id() ); }
      static CapMatch matches( const Capability & lhs,  const IdString & rhs )       { return _doMatch( lhs.id(), rhs.id() ); }
      static CapMatch matches( const Capability & lhs,  const std::string & rhs )    { return _doMatch( lhs.id(), Capability(rhs).id() ); }
      static CapMatch matches( const Capability & lhs,  const char * rhs )           { return _doMatch( lhs.id(), Capability(rhs).id() );}

      static CapMatch matches( const IdString & lhs,    const Capability & rhs )     { return _doMatch( lhs.id(), rhs.id() ); }
      static CapMatch matches( const IdString & lhs,    const IdString & rhs )       { return _doMatch( lhs.id(), rhs.id() ); }
      static CapMatch matches( const IdString & lhs,    const std::string & rhs )    { return _doMatch( lhs.id(), Capability(rhs).id() ); }
      static CapMatch matches( const IdString & lhs,    const char * rhs )           { return _doMatch( lhs.id(), Capability(rhs).id() ); }

      static CapMatch matches( const std::string & lhs, const Capability & rhs )     { return _doMatch( Capability(lhs).id(), rhs.id() );}
      static CapMatch matches( const std::string & lhs, const IdString & rhs )       { return _doMatch( Capability(lhs).id(), rhs.id() ); }
      static CapMatch matches( const std::string & lhs, const std::string & rhs )    { return _doMatch( Capability(lhs).id(), Capability(rhs).id() ); }
      static CapMatch matches( const std::string & lhs, const char * rhs )           { return _doMatch( Capability(lhs).id(), Capability(rhs).id() ); }

      static CapMatch matches( const char * lhs,        const Capability & rhs )     { return _doMatch( Capability(lhs).id(), rhs.id() );}
      static CapMatch matches( const char * lhs,        const IdString & rhs )       { return _doMatch( Capability(lhs).id(), rhs.id() ); }
      static CapMatch matches( const char * lhs,        const std::string & rhs )    { return _doMatch( Capability(lhs).id(), Capability(rhs).id() ); }
      static CapMatch matches( const char * lhs,        const char * rhs )           { return _doMatch( Capability(lhs).id(), Capability(rhs).id() ); }

      CapMatch matches( const Capability & rhs )  const { return _doMatch( id(), rhs.id() ); }
      CapMatch matches( const IdString & rhs )    const { return _doMatch( id(), rhs.id() ); }
      CapMatch matches( const std::string & rhs ) const { return _doMatch( id(), Capability(rhs).id() ); }
      CapMatch matches( const char * rhs )        const { return _doMatch( id(), Capability(rhs).id() ); }
      //@}

      /** \ref matches functor.
       */
      struct Matches
      {
        CapMatch operator()( const Capability & lhs, const Capability & rhs ) const
        { return Capability::matches( lhs, rhs ); }
      };

    public:
      /** Test for a filename that is likely being REQUIRED.
       * Files below \c /bin , \c /sbin ,  \c /lib etc. Scanning a
       * packages filelist, an \e interesting filename might be worth
       * being remembered in PROVIDES.
       */
      static bool isInterestingFileSpec( const IdString & name_r )    { return isInterestingFileSpec( name_r.c_str() ); }
      static bool isInterestingFileSpec( const std::string & name_r ) { return isInterestingFileSpec( name_r.c_str() ); }
      static bool isInterestingFileSpec( const char * name_r );

      /** \ref Capability parser also guessing \c "libzypp-1.2.3-4.5.x86_64" formats.
       *
       * The argument might be in the form \c "libzypp-devel-1.2.3.x86_64".
       * Passed to the Capability ctor, this would correctly be parsed as name
       * capability, because actually the edition part had to be separated by a
       * \c '=', and the architecture had to be appended to the name.
       * So this is how it actually had to look like: \c "libzypp-devel.x86_64=1.2.3"
       *
       * Obviously we have to guess if, and where to split name and edition. In
       * fact \c "devel" could also be the version and \c "1.2.3" would be the
       * release then.
       *
       * Assuming this Capability should be provided by some package in
       * the \ref ResPool, we check this. If unprovided, we substitute the last,
       * (or one but last) \c '-' by a \c '='. If the name part (without version)
       * of the resulting Capability matches a package name (not provides!) in
       * the \ref ResPool, this Capability is returned.
       *
       * Otherwise we return the Capability originally created from
       * \a str_r.
       *
       * \note: As this method will access the global pool, the returned
       * result depends on the pools content.
       */
      static Capability guessPackageSpec( const std::string & str_r );
      /** \overload Taking an additional bool indicating whether \c str_r made
       * a valid \ref Capability (\c true) or the result was was guessed by
       * rewiting a \c '-' to \c '='. (\c false).
       */
      static Capability guessPackageSpec( const std::string & str_r, bool & rewrote_r );

    public:
      /** Expert backdoor. */
      sat::detail::IdType id() const
      { return _id; }
    private:
      /** Match two Capabilities */
      static CapMatch _doMatch( sat::detail::IdType lhs,  sat::detail::IdType rhs );
    private:
      sat::detail::IdType _id;
  };
  ///////////////////////////////////////////////////////////////////

  /** \relates Capability Stream output */
  std::ostream & operator<<( std::ostream & str, const Capability & obj ) ZYPP_API;

  /** \relates Capability Detailed stream output */
  std::ostream & dumpOn( std::ostream & str, const Capability & obj ) ZYPP_API;

  /** \relates Capability */
  inline bool operator==( const Capability & lhs, const Capability & rhs )
  { return lhs.id() == rhs.id(); }

  /** \relates Capability */
  inline bool operator!=( const Capability & lhs, const Capability & rhs )
  { return lhs.id() != rhs.id(); }

  /** \relates Capability Arbitrary order. */
  inline bool operator<( const Capability & lhs, const Capability & rhs )
  { return lhs.id() < rhs.id(); }

  ///////////////////////////////////////////////////////////////////
  //
  //	CLASS NAME : CapDetail
  //
  /** Helper providing more detailed information about a \ref Capability.
   *
   * Capabilities are classified to be either \c SIMPLE:
   * \code
   *   name[.arch] [op edition]
   *   with op := <|<=|=|>=|>|!=
   * \endcode
   * or formed by some \c EXPRESSION:
   * \code
   *   ( left_cap op right_cap )
   *   with op := and|or|if|unless|else|with|without
   * \endcode
   *
   */
  class ZYPP_API CapDetail: protected sat::detail::PoolMember
  {
    public:
      enum Kind
      {
        NOCAP      = 0x00,
        NAMED      = 0x01,
        VERSIONED  = 0x02,
        EXPRESSION = 0x04
      };

      /** Enum values corresponding with libsolv defines.
       * \note MPL check in PoolImpl.cc
      */
      enum CapRel
      {
        REL_NONE      = 0,  ///< Not an expression
        CAP_AND       = 16, ///< \c and
        CAP_OR        = 17, ///< \c or

        CAP_COND      = 22, ///< \c if
        CAP_UNLESS    = 29, ///< \c unless
        CAP_ELSE      = 26, ///< \c else

        CAP_WITH      = 18, ///< \c with
        CAP_WITHOUT   = 28, ///< \c without

        CAP_NAMESPACE = 19, ///<
        CAP_ARCH      = 20, ///< Used internally
      };

    public:
      CapDetail()
      : _kind( NOCAP ), _lhs( 0 ), _rhs( 0 ), _flag( 0 ), _archIfSimple( 0 )
      {}
      explicit CapDetail( const Capability & cap_r )
      : _kind( NOCAP ), _lhs( cap_r.id() ), _rhs( 0 ), _flag( 0 ), _archIfSimple( 0 )
      { _init(); }
      explicit CapDetail( sat::detail::IdType id_r )
      : _kind( NOCAP ), _lhs( id_r ), _rhs( 0 ), _flag( 0 ), _archIfSimple( 0 )
      { _init(); }

    public:
      Kind kind()         const { return _kind; }
      bool isNull()       const { return _kind == NOCAP; }
      bool isNamed()      const { return _kind == NAMED; }
      bool isVersioned()  const { return _kind == VERSIONED; }
      bool isSimple()     const { return _kind & (NAMED|VERSIONED); }
      bool isExpression() const { return _kind == EXPRESSION; }

      /** \name Is simple: <tt>name[.arch] [op edition]</tt> */
      //@{
      bool     hasArch()  const { return _archIfSimple; }
      IdString arch()     const { return _archIfSimple ? IdString( _archIfSimple ) : IdString(); }
      IdString name()     const { return isSimple()    ? IdString( _lhs ) : IdString(); }
      Rel      op()       const { return isVersioned() ? Rel( _flag )     : isSimple() ? Rel::ANY : Rel::NONE; }
      Edition  ed()       const { return isVersioned() ? Edition( _rhs )  : Edition(); }
      //@}

      /** \name Is expression <tt>cap op cap</tt> */
      //@{
      Capability lhs()    const { return isExpression() ? Capability( _lhs ) : Capability::Null; }
      CapRel     capRel() const { return isExpression() ? CapRel(_flag)      : REL_NONE; }
      Capability rhs()    const { return isExpression() ? Capability( _rhs ) : Capability::Null; }
     //@}

    private:
      void _init();
    private:
      Kind                _kind;
      sat::detail::IdType _lhs;
      sat::detail::IdType _rhs;
      unsigned            _flag;
      sat::detail::IdType _archIfSimple;
  };
  ///////////////////////////////////////////////////////////////////

  /** \relates CapDetail Stream output */
  std::ostream & operator<<( std::ostream & str, const CapDetail & obj ) ZYPP_API;

  /** \relates CapDetail Stream output */
  std::ostream & operator<<( std::ostream & str, CapDetail::Kind obj ) ZYPP_API;

  /** \relates CapDetail Stream output */
  std::ostream & operator<<( std::ostream & str, CapDetail::CapRel obj ) ZYPP_API;

  ///////////////////////////////////////////////////////////////////

  inline CapDetail Capability::detail() const { return CapDetail( _id ); }

  /////////////////////////////////////////////////////////////////
} // namespace zypp
///////////////////////////////////////////////////////////////////

ZYPP_DEFINE_ID_HASHABLE( ::zypp::Capability );

#endif // ZYPP_CAPABILITY_H
