//===- BranchIsland.cpp ---------------------------------------------------===//
//
//                     The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <mcld/LD/BranchIsland.h>
#include <mcld/LD/ResolveInfo.h>
#include <mcld/LD/LDSection.h>
#include <mcld/Fragment/Stub.h>
#include <mcld/Fragment/AlignFragment.h>

#include <sstream>

using namespace mcld;

//==========================
// BranchIsland

BranchIsland::BranchIsland(Fragment& pEntryFrag,
                           size_t pMaxSize,
                           size_t pIndex)
 : m_Entry(pEntryFrag),
   m_pExit(pEntryFrag.getNextNode()),
   m_pRear(NULL),
   m_MaxSize(pMaxSize),
   m_Name("island-")
{
  // island name
  std::ostringstream index;
  index << pIndex;
  m_Name.append(index.str());
}

BranchIsland::~BranchIsland()
{
}

/// fragment iterators of the island
SectionData::iterator BranchIsland::begin()
{
  return ++iterator(&m_Entry);
}

SectionData::const_iterator BranchIsland::begin() const
{
  return ++iterator(&m_Entry);
}

SectionData::iterator BranchIsland::end()
{
  if (NULL != m_pExit)
    return iterator(m_pExit);
  return m_Entry.getParent()->end();
}

SectionData::const_iterator BranchIsland::end() const
{
  if (NULL != m_pExit)
    return iterator(m_pExit);
  return m_Entry.getParent()->end();
}

uint64_t BranchIsland::offset() const
{
  return m_Entry.getOffset() + m_Entry.size();
}

size_t BranchIsland::size() const
{
  size_t size = 0x0;
  if (0x0 != numOfStubs()) {
    size = m_pRear->getOffset() + m_pRear->size() -
           m_Entry.getNextNode()->getOffset();
  }
  return size;
}

size_t BranchIsland::maxSize() const
{
  return m_MaxSize;
}

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

size_t BranchIsland::numOfStubs() const
{
  return m_StubMap.numOfEntries();
}

/// findStub - return true if there is a stub built from the given prototype
///            for the given relocation
Stub* BranchIsland::findStub(const Stub* pPrototype, const Relocation& pReloc)
{
  Key key(pPrototype, pReloc.symInfo()->outSymbol(), pReloc.addend());
  StubMapType::iterator it = m_StubMap.find(key);
  if (it != m_StubMap.end()) {
    assert(NULL != it.getEntry()->value());
    return it.getEntry()->value();
  }
  return NULL;
}

/// addStub - add a stub into the island
bool BranchIsland::addStub(const Stub* pPrototype,
                           const Relocation& pReloc,
                           Stub& pStub)
{
  bool exist = false;
  Key key(pPrototype, pReloc.symInfo()->outSymbol(), pReloc.addend());
  StubEntryType* entry = m_StubMap.insert(key, exist);
  if (!exist) {
    entry->setValue(&pStub);
    m_pRear = &pStub;
    SectionData* sd = m_Entry.getParent();

    // insert alignment fragment
    // TODO: check if we can reduce this alignment fragment for some cases
    AlignFragment* align_frag = new AlignFragment(pStub.alignment(),
                                                  0x0,
                                                  1u,
                                                  pStub.alignment() - 1);
    align_frag->setParent(sd);
    sd->getFragmentList().insert(end(), align_frag);
    align_frag->setOffset(align_frag->getPrevNode()->getOffset() +
                          align_frag->getPrevNode()->size());

    // insert stub fragment
    pStub.setParent(sd);
    sd->getFragmentList().insert(end(), &pStub);
    pStub.setOffset(pStub.getPrevNode()->getOffset() +
                    pStub.getPrevNode()->size());
  }
  return !exist;
}

/// addRelocation - add a relocation into island
bool BranchIsland::addRelocation(Relocation& pReloc)
{
  m_Relocations.push_back(&pReloc);
  return true;
}