//---------------------------------------------------------------------------
// Form for Parameter
//---------------------------------------------------------------------------
#include <vcl.h>
#include <math.h>
#pragma hdrstop

#include "Param.h"
#include "CPanel.h"

//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TFParam *FParam;
//---------------------------------------------------------------------------
const double Pi = M_PI;
//---------------------------------------------------------------------------
class QPhaseController{
  private:
    int Size;
    int Resolution;
    double AngleStep;
    double Theta;
    TCanvas *Canvas;
  public:
  QPhaseController(TCanvas *c){
    Canvas = c;
    Resolution = 8;
    AngleStep = Pi/(double)Resolution;
    Size = 30;
    Draw();
  };
  void Draw(void){
    Canvas->Brush->Color = clBtnFace;
    Canvas->Pen->Color = clBlack;
    Canvas->Pen->Width = 2;
    int s = Size*2;
    Canvas->FillRect(Rect(0,0,s,s));
    Canvas->Brush->Color = clWhite;
    Canvas->Ellipse(0,0,Size*2,Size*2);
    int x = Size*cos(Theta);
    int y = -Size*sin(Theta);
    Canvas->Pen->Color = clRed;
    Canvas->MoveTo(Size,Size);
    Canvas->LineTo(Size+x,Size+y);
  };
  void SetTheta(double t){
    Theta = t/(double)180*Pi;
    Draw();
  };
  double GetTheta(int X, int Y){
    double x = (double)(X-Size);
    double y = (double)(Y-Size);
    Theta = atan2(-y,x);
    Theta = ((double)(int)((Theta + Pi)/AngleStep+AngleStep*0.5)*AngleStep-Pi);
    return Theta/Pi*180;
  };
};
//---------------------------------------------------------------------------
__fastcall
TFParam::TFParam(TComponent* Owner) : TForm(Owner) {
  qpcRot = new QPhaseController(ImageRotPhase->Canvas);
  qpcCRot = new QPhaseController(ImageCRotPhase->Canvas);
}
//---------------------------------------------------------------------------
void __fastcall
TFParam::FormDestroy(TObject *Sender) {
  delete qpcRot;
  delete qpcCRot;
}
//---------------------------------------------------------------------------
// Methods for Create Circuit
//---------------------------------------------------------------------------
void
TFParam::CreateCircuit(int type, int mx, int my, QDraw *qDraw, QManager *qManager) {

  if ( qManager->GetCircuitInclude(mx, my) != NULL) {
    return;
  }

  switch (type) {
  case CP_WALSH:
    qManager->AddCircuit(new QWalsh(mx,my));
    break;

  case CP_MESURE:
    qManager->AddCircuit(new QMeasure(mx,my));
    break;

  case CP_NOT:
    qManager->AddCircuit(new QNot(mx,my));
    break;

  case CP_PAULI_X:
    qManager->AddCircuit(new QPauliX(mx,my));
    break;

  case CP_PAULI_Y:
    qManager->AddCircuit(new QPauliY(mx,my));
    break;

  case CP_PAULI_Z:
    qManager->AddCircuit(new QPauliZ(mx,my));
    break;

  case CP_CNOT:
    CreateCNOT(mx, my, qDraw, qManager);
    break;

  case CP_CCNOT:
    CreateCCNOT(mx, my, qDraw, qManager);
    break;

  case CP_SWAP:
    CreateSWAP(mx, my, qDraw, qManager);
    break;

  case CP_ROT:
    CreateROT(mx, my, qDraw, qManager);
    break;

  case CP_CROT:
    CreateCROT(mx, my, qDraw, qManager);
    break;
  }
}
//---------------------------------------------------------------------------
void
TFParam::CreateCNOT(int mx, int my, QDraw *qDraw, QManager *qManager) {
  QCNot *qc = new QCNot(mx,my);
  qc->Draw(qDraw);
  if (!Execute(qc,qManager)) {
    delete qc;
  } else {
    qManager->AddCircuit(qc);
  }
}
//---------------------------------------------------------------------------
void
TFParam::CreateCCNOT(int mx, int my, QDraw *qDraw, QManager *qManager) {
  QCCNot *qc = new QCCNot(mx,my);
  qc->Draw(qDraw);
  if (!Execute(qc,qManager)) {
    delete qc;
  } else {
    qManager->AddCircuit(qc);
  }
}
//---------------------------------------------------------------------------
void
TFParam::CreateSWAP(int mx, int my, QDraw *qDraw, QManager *qManager) {
  QSwap *qc = new QSwap(mx,my);
  qc->Draw(qDraw);
  if (!Execute(qc,qManager)) {
    delete qc;
  } else {
    qManager->AddCircuit(qc);
  }
}
//---------------------------------------------------------------------------
void
TFParam::CreateROT(int mx, int my, QDraw *qDraw, QManager *qManager) {
  QRot *qc = new QRot(mx,my);
  qc->Draw(qDraw);
  if (!Execute(qc,qManager)) {
    delete qc;
  } else {
    qManager->AddCircuit(qc);
  }
}
//---------------------------------------------------------------------------
void
TFParam::CreateCROT(int mx, int my, QDraw *qDraw, QManager *qManager) {
  QCRot *qc = new QCRot(mx,my);
  qc->Draw(qDraw);
  if (!Execute(qc,qManager)) {
    delete qc;
  } else {
    qManager->AddCircuit(qc);
  }
}
//---------------------------------------------------------------------------
// Methods for Modify Circuit
//---------------------------------------------------------------------------
void
TFParam::ModifyCircuit(QCircuit *qc, QManager *qManager) {
  QCircuit *oldqc = qc->Clone();
  qManager->ClrCircuitFlg(qc);

  switch (qc->GetType()) {
  case QC_BITBOX:
    if (Execute((QBitbox*)qc)) {
      qManager->ModifyCircuit(oldqc, qc);
    }
    break;

  case QC_CNOT:
    if (Execute((QCNot*)qc, qManager)) {
      qManager->ModifyCircuit(oldqc, qc);
    }
    break;

  case QC_CCNOT:
    if (Execute((QCCNot*)qc, qManager)) {
      qManager->ModifyCircuit(oldqc, qc);
    }
    break;

  case QC_SWAP:
    if (Execute((QSwap*)qc, qManager)) {
      qManager->ModifyCircuit(oldqc, qc);
    }
    break;

  case QC_ROT:
    if (Execute((QRot*)qc, qManager)) {
      qManager->ModifyCircuit(oldqc, qc);
    }
    break;

  case QC_CROT:
    if (Execute((QCRot*)qc, qManager)) {
      qManager->ModifyCircuit(oldqc, qc);
    }
    break;
  }

  delete oldqc;
  qManager->SetAllCircuitFlg();
}
//---------------------------------------------------------------------------
bool
TFParam::Execute(QBitbox *qBitBox) {
  TSBitBox->TabVisible = true;
  CBBitState->Items->Clear();
  CBBitState->Items->Add("|0>");
  CBBitState->Items->Add("|1>");
  CBBitState->ItemIndex = qBitBox->GetState();
  CBEnabled->Checked = qBitBox->Enabled;
  int mr = ShowModal();
  TSBitBox->TabVisible = false;

  if (mr==mrCancel)return false;
  qBitBox->Enabled = CBEnabled->Checked;
  qBitBox->SetState((int)CBBitState->ItemIndex);
  return true;
}
//---------------------------------------------------------------------------
/**
 * CNot
 */
bool
TFParam::Execute(QCNot *qCNot,QManager *qManager) {

  int *LineIndex = new int[qManager->GetLineNumber()];
  int index = 0;

  CBTarget->Items->Clear();
  for (int i=0;i<qManager->GetLineNumber();i++) {
    QCNot testqc(qCNot->GetX(), qCNot->GetY());
    testqc.SetTarget(i);
    if (i != qCNot->GetY() &&
        qManager->CanPutCircuit(&testqc)) {
      CBTarget->Items->Add("q"+IntToStr(i+1));
      LineIndex[index] = i;
      index++;
    }
  }
  if(index == 0){
    MessageBeep(MB_OK);
    delete []LineIndex;
    return false;
  }

  CBTarget->ItemIndex = 0;
  for (int i=0;i<CBTarget->Items->Count;i++) {
    if(qCNot->GetTarget()==LineIndex[i]){
      CBTarget->ItemIndex = i;
    }
  }

  TSCnot->TabVisible = true;
  int mr = ShowModal();
  TSCnot->TabVisible = false;

  if (mr==mrCancel) {
    delete []LineIndex;
    return false;
  }

  qCNot->SetTarget(LineIndex[CBTarget->ItemIndex]);

  delete [] LineIndex;
  return true;
}
//---------------------------------------------------------------------------
/**
 * CCNot
 */
bool
TFParam::Execute(QCCNot *qCCNot,QManager *qManager) {

  int *LineIndex = new int[qManager->GetLineNumber()];
  int index = 0;

  CBTarget1->Items->Clear();
  CBTarget2->Items->Clear();

  for (int i=0;i<qManager->GetLineNumber();i++) {
    QCCNot testqc(qCCNot->GetX(), qCCNot->GetY());
    testqc.SetTarget1(i);
    testqc.SetTarget2(i);
    if (i != qCCNot->GetY() && qManager->CanPutCircuit(&testqc)) {
      CBTarget1->Items->Add("q"+IntToStr(i+1));
      CBTarget2->Items->Add("q"+IntToStr(i+1));
      LineIndex[index] = i;
      index++;
    }
  }
  if(index < 2){
    MessageBeep(MB_OK);
    delete []LineIndex;
    return false;
  }

  CBTarget1->ItemIndex = 0;
  for (int i=0;i<CBTarget1->Items->Count;i++) {
    if(qCCNot->GetTarget1()==LineIndex[i]){
      CBTarget1->ItemIndex = i;
    }
  }
  CBTarget2->ItemIndex = 0;
  for (int i=0;i<CBTarget2->Items->Count;i++) {
    if(qCCNot->GetTarget2()==LineIndex[i]){
      CBTarget2->ItemIndex = i;
    }
  }

  int mr;
  TSCCNot->TabVisible = true;
  while (1) {
    mr = ShowModal();
    if (mr == mrCancel) break;
    if (CBTarget1->ItemIndex == CBTarget2->ItemIndex) {
      ShowMessage("Select different lines.");
    } else {
      break;
    }
  }
  TSCCNot->TabVisible = false;

  if (mr==mrCancel) {
    delete [] LineIndex;
    return false;
  }

  qCCNot->SetTarget1(LineIndex[CBTarget1->ItemIndex]);
  qCCNot->SetTarget2(LineIndex[CBTarget2->ItemIndex]);

  delete [] LineIndex;
  return true;
}

//---------------------------------------------------------------------------
/**
 * Swap
 **/
bool
TFParam::Execute(QSwap *qSwap, QManager *qManager) {

  int *LineIndex = new int[qManager->GetLineNumber()];
  int index = 0;

  CBSwapTarget1->Items->Clear();
  CBSwapTarget2->Items->Clear();

  for (int i=0;i<qManager->GetLineNumber();i++) {
    if (qManager->GetCircuitInclude(qSwap->GetX(), i) == NULL) {
      CBSwapTarget1->Items->Add("q"+IntToStr(i+1));
      CBSwapTarget2->Items->Add("q"+IntToStr(i+1));
      LineIndex[index] = i;
      index++;
    }
  }
  CBSwapTarget1->ItemIndex = qSwap->GetTarget1();
  CBSwapTarget2->ItemIndex = qSwap->GetTarget2();

  int mr;
  TSSwap->TabVisible = true;
  while (1) {
    mr = ShowModal();
    if (mr == mrCancel) break;
    QSwap testqc(qSwap->GetX(), qSwap->GetY());
    testqc.SetTarget(LineIndex[CBSwapTarget1->ItemIndex],LineIndex[CBSwapTarget2->ItemIndex]);
    if (!qManager->CanPutCircuit(&testqc)) {
      ShowMessage("Circuits are overlapped!");
    } else if (CBSwapTarget1->ItemIndex == CBSwapTarget2->ItemIndex) {
      ShowMessage("Select different lines.");
    } else {
      break;
    }
  }

  TSSwap->TabVisible = false;

  if (mr==mrCancel) {
    delete [] LineIndex;
    return false;
  }

  qSwap->SetTarget(LineIndex[CBSwapTarget1->ItemIndex],LineIndex[CBSwapTarget2->ItemIndex]);

  delete [] LineIndex;
  return true;
}
//---------------------------------------------------------------------------
/**
 * Controled Rotator
**/
bool
TFParam::Execute(QCRot * qCRot, QManager * qManager) {

  EdCRotPhase->Text = FloatToStr(qCRot->GetPhase());
  int *LineIndex = new int[qManager->GetLineNumber()];
  int index = 0;

  CBCRTarget->Items->Clear();
  for (int i=0;i<qManager->GetLineNumber();i++) {
    QCRot testqc(qCRot->GetX(), qCRot->GetY());
    testqc.SetTarget(i);
    if (i != qCRot->GetY() && qManager->CanPutCircuit(&testqc)) {
      CBCRTarget->Items->Add("q"+IntToStr(i+1));
      LineIndex[index] = i;
      index++;
    }
  }
  if(index == 0){
    MessageBeep(MB_OK);
    delete []LineIndex;
    return false;
  }

  CBCRTarget->ItemIndex = 0;
  for (int i=0;i<CBCRTarget->Items->Count;i++) {
    if(qCRot->GetTarget()==LineIndex[i]){
      CBCRTarget->ItemIndex = i;
    }
  }

  qpcCRot->SetTheta(qCRot->GetPhase());
  TSCRot->TabVisible = true;
  int mr = mrNone;
  while(mr == mrNone){
    mr = ShowModal();
    if (mr!=mrOk) {
      TSCRot->TabVisible = false;
      delete []LineIndex;
      return false;
    }
    try {
      StrToFloat(EdCRotPhase->Text);
    } catch (EConvertError &e) {
      MessageBox(this->Handle,"Please input valid value.", "QCAD - Alert",MB_ICONERROR);
      mr = mrNone;
    }
  }

  TSCRot->TabVisible = false;
  qCRot->SetTarget(LineIndex[CBCRTarget->ItemIndex]);
  qCRot->SetPhase(StrToFloat(EdCRotPhase->Text));

  delete [] LineIndex;
  return true;
}
//---------------------------------------------------------------------------
/**
 * Rotator
**/
bool
TFParam::Execute(QRot * qRot, QManager * qManager) {
  EdRotPhase->Text = FloatToStr(qRot->GetPhase());
  qpcRot->SetTheta(qRot->GetPhase());
  TSRot->TabVisible = true;
  int mr = mrNone;
  while(mr == mrNone){
    mr = ShowModal();
    if (mr!=mrOk) {
      TSRot->TabVisible = false;
      return false;
    }
    try {
      StrToFloat(EdRotPhase->Text);
    } catch (EConvertError &e) {
      MessageBox(this->Handle,"Please input valid value.", "QCAD - Alert",MB_ICONERROR);
      mr = mrNone;
    }
  }
  qRot->SetPhase(StrToFloat(EdRotPhase->Text));
  TSRot->TabVisible = false;
  return true;
}
//---------------------------------------------------------------------------
void __fastcall TFParam::ImageRotPhaseMouseMove(TObject *Sender,
      TShiftState Shift, int X, int Y)
{
  if(!Shift.Contains(ssLeft)){
    return;
  }
  EdRotPhase->Text = FloatToStr(qpcRot->GetTheta(X,Y));
  qpcRot->Draw();
}
//---------------------------------------------------------------------------

void __fastcall TFParam::ImageCRotPhaseMouseMove(TObject *Sender,
      TShiftState Shift, int X, int Y)
{
  if(!Shift.Contains(ssLeft)){
    return;
  }
  EdCRotPhase->Text = FloatToStr(qpcCRot->GetTheta(X,Y));
  qpcCRot->Draw();
}
//---------------------------------------------------------------------------

