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

#include <mcld/Config/Config.h>
#include <mcld/Fragment/RegionFragment.h>
#include <mcld/Fragment/NullFragment.h>
#include <mcld/Support/Allocators.h>

#include <vector>

namespace mcld {

class LDSection;
class SectionData;

/** \class EhFrame
 *  \brief EhFrame represents .eh_frame section
 */
class EhFrame
{
private:
  friend class Chunk<EhFrame, MCLD_SECTIONS_PER_INPUT>;

  EhFrame();
  explicit EhFrame(LDSection& pSection);

  ~EhFrame();

  EhFrame(const EhFrame&);            // DO NOT IMPLEMENT
  EhFrame& operator=(const EhFrame&); // DO NOT IMPLEMENT

public:
  /** \class CIE
   *  \brief Common Information Entry.
   *  The CIE structure refers to LSB Core Spec 4.1, chap.10.6. Exception Frames.
   */
  class CIE : public RegionFragment
  {
  public:
    CIE(MemoryRegion& pRegion);

    void setFDEEncode(uint8_t pEncode) { m_FDEEncode = pEncode; }
    uint8_t getFDEEncode() const { return m_FDEEncode; }

  private:
    uint8_t m_FDEEncode;
  };

  /** \class FDE
   *  \brief Frame Description Entry
   *  The FDE structure refers to LSB Core Spec 4.1, chap.10.6. Exception Frames.
   */
  class FDE : public RegionFragment
  {
  public:
    FDE(MemoryRegion& pRegion,
        const CIE& pCIE,
        uint32_t pDataStart);

    const CIE& getCIE() const { return m_CIE; }

    uint32_t getDataStart() const { return m_DataStart; }

  private:
    const CIE& m_CIE;
    uint32_t m_DataStart;
  };

  typedef std::vector<CIE*> CIEList;

  // cie_iterator and const_cie_iterator must be a kind of random access iterator
  typedef CIEList::iterator cie_iterator;
  typedef CIEList::const_iterator const_cie_iterator;

  typedef std::vector<FDE*> FDEList;

  // fde_iterator and const_fde_iterator must be a kind of random access iterator
  typedef FDEList::iterator fde_iterator;
  typedef FDEList::const_iterator const_fde_iterator;

public:
  static EhFrame* Create(LDSection& pSection);

  static void Destroy(EhFrame*& pSection);

  static void Clear();

  /// merge - move all data from pOther to this object.
  EhFrame& merge(EhFrame& pOther);

  const LDSection& getSection() const;
  LDSection&       getSection();

  const SectionData* getSectionData() const { return m_pSectionData; }
  SectionData*       getSectionData()       { return m_pSectionData; }

  // -----  fragment  ----- //
  /// addFragment - when we start treating CIEs and FDEs as regular fragments,
  /// we call this function instead of addCIE() and addFDE().
  void addFragment(RegionFragment& pFrag);

  void addFragment(NullFragment& pFrag);

  /// addCIE - add a CIE entry in EhFrame
  void addCIE(CIE& pCIE);

  /// addFDE - add a FDE entry in EhFrame
  void addFDE(FDE& pFDE);

  // -----  CIE  ----- //
  const_cie_iterator cie_begin() const { return m_CIEs.begin(); }
  cie_iterator       cie_begin()       { return m_CIEs.begin(); }
  const_cie_iterator cie_end  () const { return m_CIEs.end(); }
  cie_iterator       cie_end  ()       { return m_CIEs.end(); }

  const CIE& cie_front() const { return *m_CIEs.front(); }
  CIE&       cie_front()       { return *m_CIEs.front(); }
  const CIE& cie_back () const { return *m_CIEs.back(); }
  CIE&       cie_back ()       { return *m_CIEs.back(); }

  size_t numOfCIEs() const { return m_CIEs.size(); }

  // -----  FDE  ----- //
  const_fde_iterator fde_begin() const { return m_FDEs.begin(); }
  fde_iterator       fde_begin()       { return m_FDEs.begin(); }
  const_fde_iterator fde_end  () const { return m_FDEs.end(); }
  fde_iterator       fde_end  ()       { return m_FDEs.end(); }

  const FDE& fde_front() const { return *m_FDEs.front(); }
  FDE&       fde_front()       { return *m_FDEs.front(); }
  const FDE& fde_back () const { return *m_FDEs.back(); }
  FDE&       fde_back ()       { return *m_FDEs.back(); }

  size_t numOfFDEs() const { return m_FDEs.size(); }

private:
  LDSection* m_pSection;
  SectionData* m_pSectionData;

  CIEList m_CIEs;
  FDEList m_FDEs;
};

} // namespace of mcld

#endif