//===- InputBuilder.cpp ---------------------------------------------------===//
//
//                     The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "mcld/MC/InputBuilder.h"

#include "mcld/LinkerConfig.h"
#include "mcld/Config/Config.h"
#include "mcld/MC/ContextFactory.h"
#include "mcld/MC/InputFactory.h"
#include "mcld/Support/MemoryAreaFactory.h"
#include "mcld/Support/Path.h"

namespace mcld {

InputBuilder::InputBuilder(const LinkerConfig& pConfig)
    : m_Config(pConfig),
      m_pCurrentTree(NULL),
      m_pMove(NULL),
      m_Root(),
      m_bOwnFactory(true) {
  m_pInputFactory = new InputFactory(MCLD_NUM_OF_INPUTS, pConfig);
  m_pContextFactory = new ContextFactory(MCLD_NUM_OF_INPUTS);
  m_pMemFactory = new MemoryAreaFactory(MCLD_NUM_OF_INPUTS);
}

InputBuilder::InputBuilder(const LinkerConfig& pConfig,
                           InputFactory& pInputFactory,
                           ContextFactory& pContextFactory,
                           MemoryAreaFactory& pMemoryFactory,
                           bool pDelegate)
    : m_Config(pConfig),
      m_pInputFactory(&pInputFactory),
      m_pMemFactory(&pMemoryFactory),
      m_pContextFactory(&pContextFactory),
      m_pCurrentTree(NULL),
      m_pMove(NULL),
      m_Root(),
      m_bOwnFactory(pDelegate) {
}

InputBuilder::~InputBuilder() {
  if (m_bOwnFactory) {
    delete m_pInputFactory;
    delete m_pContextFactory;
    delete m_pMemFactory;
  }
}

Input* InputBuilder::createInput(const std::string& pName,
                                 const sys::fs::Path& pPath,
                                 unsigned int pType,
                                 off_t pFileOffset) {
  return m_pInputFactory->produce(pName, pPath, pType, pFileOffset);
}

InputTree& InputBuilder::enterGroup() {
  assert(m_pCurrentTree != NULL && m_pMove != NULL);

  m_pCurrentTree->enterGroup(m_Root, *m_pMove);
  m_pMove->move(m_Root);
  m_ReturnStack.push(m_Root);
  m_pMove = &InputTree::Downward;

  return *m_pCurrentTree;
}

InputTree& InputBuilder::exitGroup() {
  assert(m_pCurrentTree != NULL && m_pMove != NULL);

  m_Root = m_ReturnStack.top();
  m_ReturnStack.pop();
  m_pMove = &InputTree::Afterward;

  return *m_pCurrentTree;
}

bool InputBuilder::isInGroup() const {
  return !m_ReturnStack.empty();
}

const InputTree& InputBuilder::getCurrentTree() const {
  assert(m_pCurrentTree != NULL && m_pMove != NULL);
  return *m_pCurrentTree;
}

InputTree& InputBuilder::getCurrentTree() {
  assert(m_pCurrentTree != NULL && m_pMove != NULL);
  return *m_pCurrentTree;
}

void InputBuilder::setCurrentTree(InputTree& pInputTree) {
  m_pCurrentTree = &pInputTree;
  m_Root = m_pCurrentTree->root();
  m_pMove = &InputTree::Downward;
}

bool InputBuilder::setContext(Input& pInput, bool pCheck) {
  // The object files in an archive have common path. Every object files in an
  // archive needs a individual context. We identify the object files in an
  // archive by its file offset. Their file offsets are not zero.
  LDContext* context = NULL;
  if (pInput.fileOffset() != 0 || !pCheck) {
    // pInput is an object in an archive file. Produce a new context in this
    // case.
    context = m_pContextFactory->produce();
  } else {
    // Using pInput.path() to avoid from creating context for identical file
    // twice.
    context = m_pContextFactory->produce(pInput.path());
  }

  pInput.setContext(context);
  return true;
}

bool InputBuilder::setMemory(Input& pInput,
                             FileHandle::OpenMode pMode,
                             FileHandle::Permission pPerm) {
  MemoryArea* memory = m_pMemFactory->produce(pInput.path(), pMode, pPerm);
  pInput.setMemArea(memory);
  return true;
}

bool InputBuilder::setMemory(Input& pInput, void* pMemBuffer, size_t pSize) {
  MemoryArea* memory = m_pMemFactory->produce(pMemBuffer, pSize);
  pInput.setMemArea(memory);
  return true;
}

const AttrConstraint& InputBuilder::getConstraint() const {
  return m_Config.attribute().constraint();
}

const AttributeProxy& InputBuilder::getAttributes() const {
  return m_pInputFactory->attr();
}

AttributeProxy& InputBuilder::getAttributes() {
  return m_pInputFactory->attr();
}

}  // namespace mcld