/*
 * Formatting.cpp
 */

#include "llvm/Constants.h"
#include "llvm/DerivedTypes.h"
#include "llvm/Module.h"

#include "llbrowse/Formatting.h"

using namespace llvm;

void printType(wxTextOutputStream & out, const Module* module,
    const Type* ty, uint32_t maxDepth) {
  std::string typeName = module->getTypeName(ty);
  if (!typeName.empty()) {
    out << wxString::From8BitData(&*typeName.begin(), typeName.size());
    return;
  }

  printTypeExpansion(out, module, ty, maxDepth);
}

void printTypeExpansion(wxTextOutputStream & out, const Module* module,
    const Type* ty, uint32_t maxDepth) {
  switch (ty->getTypeID()) {
    case Type::VoidTyID: out << _("void"); break;
    case Type::FloatTyID: out << _("float"); break;
    case Type::DoubleTyID: out << _("double"); break;
    case Type::X86_FP80TyID: out << _("x86_fp80"); break;
    case Type::FP128TyID: out << _("fp128"); break;
    case Type::PPC_FP128TyID: out << _("ppc_fp128"); break;
    #if HAVE_LLVM_TYPE_X86_FP80TY_ID
      case Type::X86_MMXTyID: out << _("x86mmx"); break;
    #endif
    case Type::MetadataTyID: out << _("metadata"); break;
    case Type::LabelTyID: out << _("label"); break;

    case Type::IntegerTyID: {
      const IntegerType* iTy = cast<IntegerType>(ty);
      out << _("int") << iTy->getBitWidth();
      break;
    }

    case Type::FunctionTyID: {
      const FunctionType* fTy = cast<FunctionType>(ty);
      printType(out, module, fTy->getReturnType(), maxDepth);
      out << _(" (");
      unsigned numParams = fTy->getNumParams();
      for (unsigned i = 0; i < numParams; ++i) {
        if (i != 0) {
          out << _(", ");
        }
        printType(out, module, fTy->getParamType(i), maxDepth);
      }
      out << _(")");
      break;
    }

    case Type::StructTyID: {
      const StructType* sTy = cast<StructType>(ty);
      if (maxDepth == 0) {
        out << _("{...}");
      } else {
        out << (sTy->isPacked() ? _("<{") : _("{"));
        for (int i = 0, ct = sTy->getNumContainedTypes(); i < ct; ++i) {
          if (i != 0) {
            out << _(", ");
          }

          if (maxDepth == 1 && i > 3) {
            out << _("...");
            break;
          }
          printType(out, module, sTy->getContainedType(i), maxDepth - 1);
        }
        out << (sTy->isPacked() ? _("}>") : _("}"));
      }
      (void) sTy;
      break;
    }

    case Type::ArrayTyID: {
      const ArrayType* aTy = cast<ArrayType>(ty);
      out << _("[") << int(aTy->getNumElements()) << _(" x ");
      printType(out, module, aTy->getContainedType(0), maxDepth);
      out << _("]");
      break;
    }

    case Type::PointerTyID: {
      const PointerType* pTy = cast<PointerType>(ty);
      printType(out, module, ty->getContainedType(0), maxDepth);
      if (pTy->getAddressSpace() != 0) {
        out << _(" addrspace (") << pTy->getAddressSpace() << _(")");
      }
      out << _("*");
      break;
    }

    case Type::VectorTyID: {
      const VectorType* vTy = cast<VectorType>(ty);
      out << _("<") << int(vTy->getNumElements()) << _(" x ");
      printType(out, module, vTy->getContainedType(0), maxDepth);
      out << _(">");
      break;
    }

    case Type::OpaqueTyID:
      out << _("opaque");
      break;

    default:
      out << _("???");
      break;
  }
}

/// Pretty-print an LLVM constant expression to a stream.
/// 'maxDepth' limits expansion of complex expressions.
void printConstant(wxTextOutputStream & out, const llvm::Module* module,
    const llvm::Constant* val, uint32_t maxDepth) {
  if (const ConstantInt* ci = dyn_cast<ConstantInt>(val)) {
    SmallVector<char, 16> intVal;
    ci->getValue().toString(intVal, 10, true);
    out << toWxStr(StringRef(intVal.data(), intVal.size()));
  } else if (const ConstantFP* cf = dyn_cast<ConstantFP>(val)) {
    // TODO: Implement
    (void) cf;
    out << _("FP?");
  } else if (const ConstantAggregateZero* cz =
      dyn_cast<ConstantAggregateZero>(val)) {
    (void) cz;
    out << _("constantzero");
  } else if (const ConstantArray* ca = dyn_cast<ConstantArray>(val)) {
    if (maxDepth > 0) {
      out << _("[");
      printConstantList(out, module, ca, maxDepth);
      out << _("]");
    } else {
      out << _("[...]");
    }
  } else if (const ConstantStruct* cs = dyn_cast<ConstantStruct>(val)) {
    if (maxDepth > 0) {
      out << _("{");
      printConstantList(out, module, cs, maxDepth);
      out << _("}");
    } else {
      out << _("{...}");
    }
  } else if (const ConstantVector* cv = dyn_cast<ConstantVector>(val)) {
    if (maxDepth > 0) {
      out << _("<");
      printConstantList(out, module, cv, maxDepth);
      out << _(">");
    } else {
      out << _("<...>");
    }
  } else if (isa<ConstantPointerNull>(val)) {
    printType(out, module, val->getType(), maxDepth);
    out << _(" null");
  } else if (const BlockAddress* ba = dyn_cast<BlockAddress>(val)) {
    // TODO: Implement
    (void) ba;
    out << _("BA?");
  } else if (const ConstantExpr* ce = dyn_cast<ConstantExpr>(val)) {
    out << wxString::From8BitData(ce->getOpcodeName());
    if (maxDepth > 0) {
      out << _("(");
      printConstantList(out, module, ce, maxDepth);
      out << _(")");
    } else {
      out << _("(...)");
    }
    (void) ce;
  } else if (isa<UndefValue>(val)) {
    out << _("undef");
  } else if (const GlobalValue* gv = dyn_cast<GlobalValue>(val)) {
    out << toWxStr(gv->getName());
  } else {
    out << _("???");
  }
}

void printConstantList(wxTextOutputStream & out, const llvm::Module* module,
    const llvm::Constant* parent, uint32_t maxDepth) {
  unsigned ct = parent->getNumOperands();
  for (unsigned i = 0; i < ct; ++i) {
    if (i != 0) {
      out << _(", ");
    }

    if (i > 3) {
      out << _("...");
      break;
    }

    printConstant(out, module, cast<Constant>(parent->getOperand(i)),
        maxDepth - 1);
  }
}

void printMetadata(wxTextOutputStream & out, const llvm::Value* nodeVal,
    uint32_t maxDepth) {
  if (const MDString* ms = dyn_cast<MDString>(nodeVal)) {
    out << _("\"") << toWxStr(ms->getString()) << _("\"");
  } else if (const MDNode* m = dyn_cast<MDNode>(nodeVal)) {
    if (maxDepth > 0) {
      out << _("{");
      for (unsigned i = 0; i < m->getNumOperands(); ++i) {
        if (i != 0) {
          out << _(", ");
        }
        if (maxDepth == 1 && i > 3) {
          out << _("...");
          break;
        }

        printMetadata(out, m->getOperand(i), maxDepth - 1);
      }
      out << _("}");
    } else {
      out << _("{...}");
    }
  } else if (const GlobalVariable* gv = dyn_cast<GlobalVariable>(nodeVal)) {
    out << toWxStr(gv->getName());
  } else if (const Function* fn = dyn_cast<Function>(nodeVal)) {
    out << toWxStr(fn->getName());
  } else if (const ConstantInt* ci = dyn_cast<ConstantInt>(nodeVal)) {
    SmallVector<char, 16> intVal;
    ci->getValue().toString(intVal, 10, true);
    out << toWxStr(StringRef(intVal.data(), intVal.size()));
  } else {
    out << _("???");
  }
}

