//===- 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/Support/Path.h>
#include <mcld/MC/InputFactory.h>
#include <mcld/MC/ContextFactory.h>
#include <mcld/Support/MemoryAreaFactory.h>

using 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(NULL != m_pCurrentTree && NULL != m_pMove);

  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(NULL != m_pCurrentTree && NULL != m_pMove);

  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(NULL != m_pCurrentTree && NULL != m_pMove);
  return *m_pCurrentTree;
}

InputTree& InputBuilder::getCurrentTree()
{
  assert(NULL != m_pCurrentTree && NULL != m_pMove);
  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 (0 != pInput.fileOffset() || !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);

  if (!memory->handler()->isGood())
    return false;

  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();
}