//===- OutputSectDesc.h ---------------------------------------------------===//
//
//                     The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef MCLD_SCRIPT_OUTPUTSECTDESC_H_
#define MCLD_SCRIPT_OUTPUTSECTDESC_H_

#include "mcld/Script/ScriptCommand.h"

#include <string>
#include <vector>

#include <cassert>

namespace mcld {

class RpnExpr;
class StringList;

/** \class OutputSectDesc
 *  \brief This class defines the interfaces to output section description.
 */

class OutputSectDesc : public ScriptCommand {
 public:
  enum Type {
    LOAD,  // ALLOC
    NOLOAD,
    DSECT,
    COPY,
    INFO,
    OVERLAY
  };

  enum Constraint { NO_CONSTRAINT, ONLY_IF_RO, ONLY_IF_RW };

  struct Prolog {
    bool hasVMA() const { return m_pVMA != NULL; }
    const RpnExpr& vma() const {
      assert(hasVMA());
      return *m_pVMA;
    }
    RpnExpr& vma() {
      assert(hasVMA());
      return *m_pVMA;
    }

    void setType(Type pType) { m_Type = pType; }

    Type type() const { return m_Type; }

    bool hasLMA() const { return m_pLMA != NULL; }
    const RpnExpr& lma() const {
      assert(hasLMA());
      return *m_pLMA;
    }
    RpnExpr& lma() {
      assert(hasLMA());
      return *m_pLMA;
    }

    bool hasAlign() const { return m_pAlign != NULL; }
    const RpnExpr& align() const {
      assert(hasAlign());
      return *m_pAlign;
    }

    bool hasSubAlign() const { return m_pSubAlign != NULL; }
    const RpnExpr& subAlign() const {
      assert(hasSubAlign());
      return *m_pSubAlign;
    }

    Constraint constraint() const { return m_Constraint; }

    bool operator==(const Prolog& pRHS) const {
      /* FIXME: currently I don't check the real content */
      if (this == &pRHS)
        return true;
      if (m_pVMA != pRHS.m_pVMA)
        return false;
      if (m_Type != pRHS.m_Type)
        return false;
      if (m_pLMA != pRHS.m_pLMA)
        return false;
      if (m_pAlign != pRHS.m_pAlign)
        return false;
      if (m_pSubAlign != pRHS.m_pSubAlign)
        return false;
      if (m_Constraint != pRHS.m_Constraint)
        return false;
      return true;
    }

    RpnExpr* m_pVMA;
    Type m_Type;
    RpnExpr* m_pLMA;
    RpnExpr* m_pAlign;
    RpnExpr* m_pSubAlign;
    Constraint m_Constraint;
  };

  struct Epilog {
    bool hasRegion() const { return m_pRegion != NULL; }
    const std::string& region() const {
      assert(hasRegion());
      return *m_pRegion;
    }

    bool hasLMARegion() const { return m_pLMARegion != NULL; }
    const std::string& lmaRegion() const {
      assert(hasLMARegion());
      return *m_pLMARegion;
    }

    bool hasPhdrs() const { return m_pPhdrs != NULL; }
    const StringList& phdrs() const {
      assert(hasPhdrs());
      return *m_pPhdrs;
    }

    bool hasFillExp() const { return m_pFillExp != NULL; }
    const RpnExpr& fillExp() const {
      assert(hasFillExp());
      return *m_pFillExp;
    }

    bool operator==(const Epilog& pRHS) const {
      /* FIXME: currently I don't check the real content */
      if (this == &pRHS)
        return true;
      if (m_pRegion != pRHS.m_pRegion)
        return false;
      if (m_pLMARegion != pRHS.m_pLMARegion)
        return false;
      if (m_pPhdrs != pRHS.m_pPhdrs)
        return false;
      if (m_pFillExp != pRHS.m_pFillExp)
        return false;
      return true;
    }

    const std::string* m_pRegion;
    const std::string* m_pLMARegion;
    StringList* m_pPhdrs;
    RpnExpr* m_pFillExp;
  };

  typedef std::vector<ScriptCommand*> OutputSectCmds;
  typedef OutputSectCmds::const_iterator const_iterator;
  typedef OutputSectCmds::iterator iterator;
  typedef OutputSectCmds::const_reference const_reference;
  typedef OutputSectCmds::reference reference;

 public:
  OutputSectDesc(const std::string& pName, const Prolog& pProlog);
  ~OutputSectDesc();

  const_iterator begin() const { return m_OutputSectCmds.begin(); }
  iterator begin() { return m_OutputSectCmds.begin(); }
  const_iterator end() const { return m_OutputSectCmds.end(); }
  iterator end() { return m_OutputSectCmds.end(); }

  const_reference front() const { return m_OutputSectCmds.front(); }
  reference front() { return m_OutputSectCmds.front(); }
  const_reference back() const { return m_OutputSectCmds.back(); }
  reference back() { return m_OutputSectCmds.back(); }

  const std::string& name() const { return m_Name; }

  size_t size() const { return m_OutputSectCmds.size(); }

  bool empty() const { return m_OutputSectCmds.empty(); }

  void dump() const;

  static bool classof(const ScriptCommand* pCmd) {
    return pCmd->getKind() == ScriptCommand::OUTPUT_SECT_DESC;
  }

  void activate(Module& pModule);

  void push_back(ScriptCommand* pCommand);

  void setEpilog(const Epilog& pEpilog);

  const Prolog& prolog() const { return m_Prolog; }

  const Epilog& epilog() const { return m_Epilog; }

 private:
  OutputSectCmds m_OutputSectCmds;
  std::string m_Name;
  Prolog m_Prolog;
  Epilog m_Epilog;
};

}  // namespace mcld

#endif  // MCLD_SCRIPT_OUTPUTSECTDESC_H_