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

using namespace mcld;

TargetRegistry::TargetListTy mcld::TargetRegistry::s_TargetList;

//===----------------------------------------------------------------------===//
// TargetRegistry
//===----------------------------------------------------------------------===//
void TargetRegistry::RegisterTarget(Target& pTarget,
                                    const char* pName,
                                    Target::TripleMatchQualityFnTy pQualityFn)
{
  pTarget.Name = pName;
  pTarget.TripleMatchQualityFn = pQualityFn;

  s_TargetList.push_back(&pTarget);
}

const Target* TargetRegistry::lookupTarget(const std::string &pTriple,
                                           std::string &pError)
{
  if (empty()) {
    pError = "Unable to find target for this triple (no target are registered)";
    return NULL;
  }

  llvm::Triple triple(pTriple);
  Target* best = NULL, *ambiguity = NULL;
  unsigned int highest = 0;

  for (iterator target = begin(), ie = end(); target != ie; ++target) {
    unsigned int quality = (*target)->getTripleQuality(triple);
    if (quality > 0) {
      if (NULL == best || highest < quality) {
        highest = quality;
        best = *target;
        ambiguity = NULL;
      }
      else if (highest == quality) {
        ambiguity = *target;
      }
    }
  }

  if (NULL == best) {
    pError = "No availaible targets are compatible with this triple.";
    return NULL;
  }

  if (NULL != ambiguity) {
    pError = std::string("Ambiguous targets: \"") +
             best->name() + "\" and \"" + ambiguity->name() + "\"";
    return NULL;
  }

  return best;
}

const Target* TargetRegistry::lookupTarget(const std::string& pArchName,
                                           llvm::Triple& pTriple,
                                           std::string& pError)
{
  const Target* result = NULL;
  if (!pArchName.empty()) {
    for (mcld::TargetRegistry::iterator it = mcld::TargetRegistry::begin(),
           ie = mcld::TargetRegistry::end(); it != ie; ++it) {
      if (pArchName == (*it)->name()) {
        result = *it;
        break;
      }
    }

    if (NULL == result) {
      pError = std::string("invalid target '") + pArchName + "'.\n";
      return NULL;
    }

    // Adjust the triple to match (if known), otherwise stick with the
    // module/host triple.
    llvm::Triple::ArchType type =
                               llvm::Triple::getArchTypeForLLVMName(pArchName);
    if (llvm::Triple::UnknownArch != type)
      pTriple.setArch(type);
  }
  else {
    std::string error;
    result = lookupTarget(pTriple.getTriple(), error);
    if (NULL == result) {
      pError = std::string("unable to get target for `") +
               pTriple.getTriple() + "'\n" +
               "(Detail: " + error + ")\n";
      return NULL;
    }
  }
  return result;
}