//===- GroupReader.cpp ----------------------------------------------------===//
//
//                     The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <mcld/LD/Archive.h>
#include <mcld/LD/ArchiveReader.h>
#include <mcld/LD/DynObjReader.h>
#include <mcld/LD/GroupReader.h>
#include <mcld/LD/ObjectReader.h>
#include <mcld/LD/BinaryReader.h>
#include <mcld/LinkerConfig.h>
#include <mcld/MC/Attribute.h>
#include <mcld/Support/MsgHandling.h>

using namespace mcld;

GroupReader::GroupReader(Module& pModule,
                         ObjectReader& pObjectReader,
                         DynObjReader& pDynObjReader,
                         ArchiveReader& pArchiveReader,
                         BinaryReader& pBinaryReader)
  : m_Module(pModule),
    m_ObjectReader(pObjectReader),
    m_DynObjReader(pDynObjReader),
    m_ArchiveReader(pArchiveReader),
    m_BinaryReader(pBinaryReader)
{
}

GroupReader::~GroupReader()
{
}

bool GroupReader::readGroup(Module::input_iterator pRoot,
                            InputBuilder& pBuilder,
                            const LinkerConfig& pConfig)
{
  // record the number of total objects included in this sub-tree
  size_t cur_obj_cnt = 0;
  size_t last_obj_cnt = 0;
  size_t non_ar_obj_cnt = 0;

  // record the archive files in this sub-tree
  typedef std::vector<ArchiveListEntry*> ArchiveListType;
  ArchiveListType ar_list;

  Module::input_iterator input = --pRoot;

  // Since the end of a sub-tree is the same node to the end of whole tree, we
  // take the end of the whole input tree for conventience.
  Module::input_iterator input_end = m_Module.input_end();

  // first time read the sub-tree
  while (input != input_end) {
    // already got type - for example, bitcode or external OIR (object
    // intermediate representation)
    if ((*input)->type() == Input::Script ||
        (*input)->type() == Input::Archive ||
        (*input)->type() == Input::External) {
      ++input;
      continue;
    }

    if (Input::Object == (*input)->type()) {
      m_Module.getObjectList().push_back(*input);
      continue;
    }

    if (Input::DynObj == (*input)->type()) {
      m_Module.getLibraryList().push_back(*input);
      continue;
    }

    // is an archive
    if (m_ArchiveReader.isMyFormat(**input)) {
      (*input)->setType(Input::Archive);
      // record the Archive used by each archive node
      Archive* ar = new Archive(**input, pBuilder);
      ArchiveListEntry* entry = new ArchiveListEntry(*ar, input);
      ar_list.push_back(entry);
      // read archive
      m_ArchiveReader.readArchive(*ar);
      cur_obj_cnt += ar->numOfObjectMember();
    }
    // read input as a binary file
    else if (pConfig.options().isBinaryInput()) {
      (*input)->setType(Input::Object);
      m_BinaryReader.readBinary(**input);
      m_Module.getObjectList().push_back(*input);
    }
    // is a relocatable object file
    else if (m_ObjectReader.isMyFormat(**input)) {
      (*input)->setType(Input::Object);
      m_ObjectReader.readHeader(**input);
      m_ObjectReader.readSections(**input);
      m_ObjectReader.readSymbols(**input);
      m_Module.getObjectList().push_back(*input);
      ++cur_obj_cnt;
      ++non_ar_obj_cnt;
    }
    // is a shared object file
    else if (m_DynObjReader.isMyFormat(**input)) {
      (*input)->setType(Input::DynObj);
      m_DynObjReader.readHeader(**input);
      m_DynObjReader.readSymbols(**input);
      m_Module.getLibraryList().push_back(*input);
    }
    else {
      fatal(diag::err_unrecognized_input_file) << (*input)->path()
                                               << pConfig.targets().triple().str();
    }
    ++input;
  }

  // after read in all the archives, traverse the archive list in a loop until
  // there is no unresolved symbols added
  ArchiveListType::iterator it = ar_list.begin();
  ArchiveListType::iterator end = ar_list.end();
  while (cur_obj_cnt != last_obj_cnt) {
    last_obj_cnt = cur_obj_cnt;
    cur_obj_cnt = non_ar_obj_cnt;
    for (it = ar_list.begin(); it != end; ++it) {
      Archive& ar = (*it)->archive;
      // if --whole-archive is given to this archive, no need to read it again
      if ( ar.getARFile().attribute()->isWholeArchive())
        continue;
      m_ArchiveReader.readArchive(ar);
      cur_obj_cnt += ar.numOfObjectMember();
    }
  }

  // after all needed member included, merge the archive sub-tree to main
  // InputTree
  for (it = ar_list.begin(); it != end; ++it) {
    Archive& ar = (*it)->archive;
    if (ar.numOfObjectMember() > 0) {
      m_Module.getInputTree().merge<InputTree::Inclusive>((*it)->input,
                                                          ar.inputs());
    }
  }

  // cleanup ar_list
  for (it = ar_list.begin(); it != end; ++it) {
    delete &((*it)->archive);
    delete (*it);
  }
  ar_list.clear();

  return true;
}