//===- MCLDAttribute.h ----------------------------------------------------===//
//
//                     The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef MCLD_ATTRIBUTE_H
#define MCLD_ATTRIBUTE_H
#ifdef ENABLE_UNITTEST
#include <gtest.h>
#endif
#include <vector>
#include <string>

namespace mcld
{
class AttributeFactory;

/** \class AttributeBase
 *  \brief AttributeBase provides the real storage for attributes of options.
 *
 *  Attributes are options affecting the link editing of input files.
 *  Some options affects the input files mentioned on the command line after
 *  them. For example, --whole-archive option affects archives mentioned on
 *  the command line after the --whole-archve option. We call such options
 *  "attributes of input files"
 *
 *  AttributeBase is the storage for attributes of input files. Each input
 *  file (@see mcld::Input in MCLinker) has a pointer of an attribute. Since
 *  most attributes of input files are identical, our design lets input files
 *  which have identical attributes share common attribute. AttributeBase is
 *  the shared storage for attribute.
 */
class AttributeBase
{
public:
  AttributeBase()
  : m_WholeArchive(false),
    m_AsNeeded(false),
    m_AddNeeded(true),
    m_Static(false)
  { }

  AttributeBase(const AttributeBase& pBase)
  : m_WholeArchive(pBase.m_WholeArchive),
    m_AsNeeded(pBase.m_AsNeeded),
    m_AddNeeded(pBase.m_AddNeeded),
    m_Static(pBase.m_Static)
  { }

  virtual ~AttributeBase()
  { }

  // ----- observers  ----- //
  // represent GNU ld --whole-archive/--no-whole-archive options
  bool isWholeArchive() const
  { return m_WholeArchive; }

  // represent GNU ld --as-needed/--no-as-needed options
  bool isAsNeeded() const
  { return m_AsNeeded; }

  // represent GNU ld --add-needed/--no-add-needed options
  bool isAddNeeded() const
  { return m_AddNeeded; }

  // represent GNU ld -static option
  bool isStatic() const
  { return m_Static; }

  // represent GNU ld -call_shared option
  bool isDynamic() const
  { return !m_Static; }
public:
  bool m_WholeArchive : 1;
  bool m_AsNeeded : 1;
  bool m_AddNeeded : 1;
  bool m_Static : 1;
};

/** \class Attribute
 *  \brief The base class of attributes. Providing the raw operations of an
 *  attributes
 *
 *  For conventience and producing less bugs, we move the stoarges of attributes
 *  onto AttributeBase, and modifiers remains with the class Attribute.
 */
class Attribute : public AttributeBase
{
public:
  // -----  modifiers  ----- //
  void setWholeArchive()
  { m_WholeArchive = true; }

  void unsetWholeArchive()
  { m_WholeArchive = false; }

  void setAsNeeded()
  { m_AsNeeded = true; }

  void unsetAsNeeded()
  { m_AsNeeded = false; }

  void setAddNeeded()
  { m_AddNeeded = true; }

  void unsetAddNeeded()
  { m_AddNeeded = false; }

  void setStatic()
  { m_Static = true; }

  void setDynamic()
  { m_Static = false; }
};

/** \class AttrConstraint
 *  \brief AttrConstarint is the constraint of a system.
 *
 *  Some systems can not enable certain attributes of a input file.
 *  For example, systems which have no shared libraries can not enable
 *  --call_shared options. We call the ability of enabling attributes
 *  as the constraint of attributes of a system.
 *
 *  Systems enable attributes at the target implementation of SectLinker.
 *
 *  @see SectLinker
 */
class AttrConstraint : public AttributeBase
{
public:
  void enableWholeArchive()
  { m_WholeArchive = true; }

  void disableWholeArchive()
  { m_WholeArchive = false; }

  void enableAsNeeded()
  { m_AsNeeded = true; }

  void disableAsNeeded()
  { m_AsNeeded = false; }

  void enableAddNeeded()
  { m_AddNeeded = true; }

  void disableAddNeeded()
  { m_AddNeeded = false; }

  void setSharedSystem()
  { m_Static = false; }

  void setStaticSystem()
  { m_Static = true; }

  bool isSharedSystem() const
  { return !m_Static; }

  bool isStaticSystem() const
  { return m_Static; }
  
  bool isLegal(const Attribute& pAttr) const;
};

/** \class AttributeProxy
 *  \brief AttributeProxys is the illusion of private attribute of each
 *  input file.
 *
 *  We designers want to hide the details of sharing common attributes
 *  between input files. We want input files under the illusion that they
 *  have their own private attributes to simplify the linking algorithms.
 *
 *  AttributeProxy hides the reality of sharing. An input file can change
 *  its attribute without explicit searching of existing attributes
 *  as it has a private ownership of the attribute. AttributeProxy does
 *  the searching in the AttributeFactory and changes the pointer of
 *  the attribute of the input file. If the searching fails, AttributeProxy
 *  requests a new attribute from the AttributeFactory.
 */
class AttributeProxy
{
private:
  friend class AttributeFactory;

  explicit AttributeProxy(AttributeFactory& pParent, Attribute& pBase);
  ~AttributeProxy();

public:
  // ----- observers  ----- //
  bool isWholeArchive() const;

  bool isAsNeeded() const;

  bool isAddNeeded() const;

  bool isStatic() const;

  bool isDynamic() const;

  Attribute* attr()
  { return m_pBase; }

  const Attribute* attr() const
  { return m_pBase; }

  // -----  modifiers  ----- //
  void setWholeArchive();
  void unsetWholeArchive();
  void setAsNeeded();
  void unsetAsNeeded();
  void setAddNeeded();
  void unsetAddNeeded();
  void setStatic();
  void setDynamic();

private:
  AttributeProxy* clone() const;

  void change(Attribute* pBase)
  { m_pBase = pBase; }

private:
  AttributeFactory &m_AttrPool;
  Attribute *m_pBase;
};


// -----  comparisons  ----- //
inline bool operator== (const Attribute& pLHS, const Attribute& pRHS)
{
  return ((pLHS.isWholeArchive() == pRHS.isWholeArchive()) &&
    (pLHS.isAsNeeded() == pRHS.isAsNeeded()) && 
    (pLHS.isAddNeeded() == pRHS.isAddNeeded()) && 
    (pLHS.isStatic() == pRHS.isStatic()));
}

inline bool operator!= (const Attribute& pLHS, const Attribute& pRHS)
{
  return !(pLHS == pRHS);
}

} // namespace of mcld

#endif