//----------------------------------------------------------------------------
//  CompilerCode.cpp
//  Compile intermediate code into native C++ code
//  $Date: 2003/02/20 00:24:16 $
//  $Revision: 1.4 $
//----------------------------------------------------------------------------
#ifdef __BORLANDC__
#include <vcl.h>
#pragma hdrstop
#endif //__BORLANDC__

#include <iostream>
#include <fstream>
#include "QCompilerCode.h"

#ifndef M_PI
#define M_PI 3.1415926535897932384626433832795028841971693993751
#endif//M_PI

//----------------------------------------------------------------------------
#ifdef __BORLANDC__
#pragma package(smart_init)
#endif //__BORLANDC__

//----------------------------------------------------------------------------
/**
 *  Constructor
 */
QCompilerCode::QCompilerCode(const char * const filename) : QCompiler() {
  mQParseInfo.clear();
  mTargetName = filename;
}

//----------------------------------------------------------------------------
/**
 *  Constructor with input stream
 */
QCompilerCode::QCompilerCode(std::istream &is, const char * const filename)
    : QCompiler(is) {
  mQParseInfo.clear();
  mTargetName = filename;
}

//----------------------------------------------------------------------------
/**
 *  Destructor
 */
QCompilerCode::~QCompilerCode() {
  mQParseInfo.clear();
}

//----------------------------------------------------------------------------
/**
 *  Save the compile result to output stream
 */
void
QCompilerCode::SaveToStream(std::ostream &os) {
  int vec_size = mQParseInfo.size();

  if (false == mState) {
    std::cerr << "QCompilerCode: compile has not been completed.\n";
    return;
  }

  WriteHeader(os);
  for (int i = 0; i < vec_size; i++) {
    const std::vector<int> &indices = mQParseInfo[i].getTargetIndices();
    switch (mQParseInfo[i].getOperator()) {
    case cn_cnot:
      WriteCNot(os, indices[0], indices[1]);
      break;
    case cn_crot:
      WriteCRot(os, indices[0], indices[1],
                (double)180 / (double)mQParseInfo[i].getRotation() * M_PI);
      break;
    case cn_h:
      WriteHadam(os, indices[0]);
      break;
    case cn_m:
      // do nothing
      break;
    case cn_swap:
      //TODO:
      break;
    case cn_x:
      //TODO:
      break;
    case cn_y:
      //TODO:
      break;
    case cn_z:
      //TODO:
      break;
    case cn_not:
      //TODO:
      break;
    case cn_ccnot:
      WriteCCNot(os, indices[0], indices[1], indices[2]);
      break;
    case cn_init:
      WriteQBits(os, indices[0]);
      break;
    default:
      break;
    }
  }
  WriteFooter(os);
}

//----------------------------------------------------------------------------
/**
 *  Save the compile result to a file whose name is specified with 'filename'
 */
void
QCompilerCode::SaveToFile(const char * const filename) {
  std::ofstream   ofs(filename);

  if (!ofs) {
    std::cerr << "QCompilerCode: failed in file open.\n";
    return;
  }

  SaveToStream(ofs);

  ofs.close();
}

//----------------------------------------------------------------------------
/**
 *  Implementation of a virtual function
 */
bool
QCompilerCode::CompileOneLine(const QParseInfo &pinfo) {
  if (false == pinfo.getParseResult()) return false;
  mQParseInfo.push_back(pinfo);
  return true;
}

//----------------------------------------------------------------------------
/**
 *  Implementation of a virtual function
 */
void
QCompilerCode::CatchError(const QParseInfo &pinfo, const int at) {
  std::cerr << "There is an error at line " << at << "\n";
  switch (pinfo.getErrorNo()) {
  case QParseInfo::er_syntax_error:
    std::cerr << "Syntax Error.\n";
    break;
  case QParseInfo::er_unknown_operation:
    std::cerr << "Unknown Operation.\n";
    break;
  case QParseInfo::er_lack_of_arguments:
    std::cerr << "Lack of Arguments.\n";
    break;
  case QParseInfo::er_too_many_arguments:
    std::cerr << "Too Many Arguments.\n";
    break;
  case QParseInfo::er_invalid_arguments:
    std::cerr << "Invalid Arguments.\n";
    break;
  default:
    std::cerr << "Unknown Error.\n";
    break;
  }
  mQParseInfo.clear();
}

//----------------------------------------------------------------------------
/**
 *  Subcontract function of SaveToStream()
 */
void
QCompilerCode::WriteHeader(std::ostream &os) {
  os << "//----------------------------------------------------------------------------\n"
  << "//  QCAD compiled code\n"
  << "//----------------------------------------------------------------------------\n"
  << "#include <iostream>\n"
  << "#include \"QC_all.h\"\n"
  << "#include \"qclib.h\"\n\n"
  << "int main(void)\n{\n"
  << "    std::cerr << \"\\nCopyright (c) 2003 QCAD project.\\n\"\n"
  << "              << \"This application is automatically generated by qcc.\\n\""
  << "              << \"The result will be stored into \\\""
  << mTargetName << "\\\".\\n\\n\";"
  << "\n";
}

//----------------------------------------------------------------------------
/**
 *  Subcontract function of SaveToStream()
 */
void
QCompilerCode::WriteQBits(std::ostream &os, const int &t1) {
  os << "    QBits qbits(" << t1 << ");\n"
  << "    std::cerr << \"Calcuate with " << t1 << " qubits.\\n\";\n"
  << "    qc::allocWith(qbits);\n";
}

//----------------------------------------------------------------------------
/**
 *  Subcontract function of SaveToStream()
 */
void
QCompilerCode::WriteFooter(std::ostream &os) {
  os << "    qc::getQBits()->SaveToFile(\"" << mTargetName << "\");\n"
  << "    qc::release();\n\n"
  << "    std::cerr << \"done.\\n\";\n"
  << "    return 0;\n}\n";
}

//----------------------------------------------------------------------------
/**
 *  Subcontract function of SaveToStream()
 */
void
QCompilerCode::WriteCNot(std::ostream &os, const int &t1, const int &c1) {
  os << "    QC_cnot::calc(" << t1 << ", " << c1 << ",qbits.GetBitsR(),qbits.GetBitsI(),qbits.GetNumberOfQBits());\n"
  << "    std::cerr << \"Controlled-Not(" << t1 << ", " << c1 << ")\\n\";\n";
}

//----------------------------------------------------------------------------
/**
 *  Subcontract function of SaveToStream()
 */
void
QCompilerCode::WriteCCNot (std::ostream &os, const int &t1,
                           const int &c1, const int &c2) {
  os << "    QC_ccnot::calc(" << t1 << ", " << c1 << ", " << c2 << ",qbits.GetBitsR(),qbits.GetBitsI(),qbits.GetNumberOfQBits());\n"
  << "    std::cerr << \"Toffoli("
  << t1 << ", " << c1 << ", " << c2 << ")\\n\";\n";
}

//----------------------------------------------------------------------------
/**
 *  Subcontract function of SaveToStream()
 */
void
QCompilerCode::WriteCRot  (std::ostream &os, const int &t1,
                           const int &c1, const double rad) {
  os << "    QC_crot::calc(" << t1 << ", " << c1 << ", " << rad << ",qbits.GetBitsR(),qbits.GetBitsI(),qbits.GetNumberOfQBits());\n"
  << "    std::cerr << \"Controlled-Rot("
  << t1 << ", " << c1 << ")\\n\";\n";
}

//----------------------------------------------------------------------------
/**
 *  Subcontract function of SaveToStream()
 */
void
QCompilerCode::WriteHadam (std::ostream &os, const int &t1) {
  os << "    QC_hadamard::calc(" << t1 << ",qbits.GetBitsR(),qbits.GetBitsI(),qbits.GetNumberOfQBits());\n"
  << "    std::cerr << \"Hadamard(" << t1 << ")\\n\";\n";
}

