//---------------------------------------------------------------------------
// Management class
//---------------------------------------------------------------------------
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include "QManager.h"
#include "QCompilerCntl.h"
#include "QCalcManager.h"
#include "QBitbox.h"
#include "StrUtils.h"
#include "QUndoManager.h"
#include "undo/QUndoRemove.h"
#include "undo/QUndoAdd.h"
#include "undo/QUndoModify.h"
#include "undo/QUndoQuitSelect.h"
//---------------------------------------------------------------------------
const int QManager::DEFAULT_LINE = 7;
const int QManager::DEFAULT_COLUMN = 15;
//---------------------------------------------------------------------------
QManager::QManager(void) {
  CircuitFlg = NULL;
  Init(DEFAULT_LINE,DEFAULT_COLUMN);
  qSelector = new QSelector();
  qUndoManager = new QUndoManager();
}
//---------------------------------------------------------------------------
QManager::~QManager(void) {
  InitCircuitFlg(0,0);
  DeleteAll();
  delete qSelector;
  delete qUndoManager;
}
//---------------------------------------------------------------------------
void
QManager::Init(int Line, int Column) {
  DeleteAll();
  InitCircuitFlg(Line, Column);
  LineNumber = Line;
  ColumnNumber = Column;
  StepLine = 0;
  for (int i=0;i<LineNumber;i++) {
    QBitbox *qbb = new QBitbox(i);
    Add(qbb);
  }
  SetAllCircuitFlg();
  Modified = false;
  FileName = "";
}
//---------------------------------------------------------------------------
// Management of QCircuits
//---------------------------------------------------------------------------
void
QManager::DeleteAll(void) {
  for (QList::iterator i=qList.begin();i!=qList.end();i++) {
    QCircuit *qc = (*i);
    delete qc;
  }
  qList.clear();
}
//---------------------------------------------------------------------------
int
QManager::GetQCircuitCount(void) {
  return qList.size();
}
//---------------------------------------------------------------------------
QCircuit*
QManager::GetQCircuitAt(int index) {
  return  qList[index];
};
//---------------------------------------------------------------------------
bool
QManager::Contains(QCircuit *qc) {
  for (QList::iterator i=qList.begin();i!=qList.end();i++) {
    QCircuit *q = (*i);
    if(q == qc) {
      return true;
    }
  }
  return false;
}
//---------------------------------------------------------------------------
void
QManager::Add(QCircuit *qc) {
  qList.push_back(qc);
}
//---------------------------------------------------------------------------
void
QManager::Remove(QCircuit *qc) {
  qList.erase(remove(qList.begin(), qList.end(), qc), qList.end());
  delete qc;
}
//---------------------------------------------------------------------------
void
QManager::AddCircuit(QCircuit *qc) {
  Add(qc);
  if (qc->GetType() != QC_BITBOX) {
    qUndoManager->Add(new QUndoAdd(qc));
  }
  SetAllCircuitFlg();
  Modified = true;
}
//---------------------------------------------------------------------------
void
QManager::RemoveCircuit(QCircuit *qc) {
  qUndoManager->Add(new QUndoRemove(qc));
  Remove(qc);
  SetAllCircuitFlg();
  Modified = true;
}
//---------------------------------------------------------------------------
// Methods for CircuitFlg
//---------------------------------------------------------------------------
void
QManager::InitCircuitFlg(int Line, int Column) {

  if (CircuitFlg != NULL) {
    for (int i = 0; i < this->ColumnNumber; i++) delete [] CircuitFlg[i];
    delete [] CircuitFlg;
  }
  CircuitFlg = new int*[Column];
  for (int i = 0; i < Column; i++) {
    CircuitFlg[i] = new int[Line];
    for (int j = 0; j < Line; j++) {
      CircuitFlg[i][j] = FALSE;
    }
  }
}
//---------------------------------------------------------------------------
QCircuit*
QManager::GetCircuitInclude(int x, int y) {

  if(x <0 || x >GetColumnNumber()){
    return NULL;
  }

  if(y <0 || y >GetLineNumber()){
    return NULL;
  }

  int index = CircuitFlg[x][y];
  if (index == QC_NULL) {
    return NULL;
  } else {
    return GetQCircuitAt(index);
  }
}
//---------------------------------------------------------------------------
void
QManager::SetAllCircuitFlg(void) {
  for (int x = 0; x < ColumnNumber; x++) {
    for (int y = 0; y < LineNumber; y++) {
      CircuitFlg[x][y] = QC_NULL;
    }
  }

  for (int i = 0; i < GetQCircuitCount(); i++) {
    QCircuit *qc = GetQCircuitAt(i);
    TRect rc = qc->GetOccupiedRect();
    for (int x = rc.left; x < rc.right; x++) {
      for (int y = rc.top; y < rc.bottom; y++) {
        CircuitFlg[x][y] = i;
      }
    }
  }
}
//---------------------------------------------------------------------------
void
QManager::ClrCircuitFlg(QCircuit* qc) {
  TRect rc = qc->GetOccupiedRect();
  for (int x = rc.left; x < rc.right; x++) {
    for (int y = rc.top; y < rc.bottom; y++) {
      CircuitFlg[x][y] = QC_NULL;
    }
  }
}
//---------------------------------------------------------------------------
bool
QManager::CanPutCircuit(QCircuit* qc) {
  TRect rc = qc->GetOccupiedRect();
  for (int x = rc.left; x < rc.right; x++) {
    for (int y = rc.top; y < rc.bottom; y++) {
      if (CircuitFlg[x][y] != QC_NULL) return FALSE;
    }
  }
  return TRUE;
}
//---------------------------------------------------------------------------
// Interfaces
//---------------------------------------------------------------------------
void
QManager::SaveToFile(char * filename) {

  ofstream ofs(filename);
  ofs << "#QCAD - SaveData" << endl;
  ofs << LineNumber << endl;
  ofs << ColumnNumber << endl;
  for (int i=0;i<GetQCircuitCount();i++) {
    QCircuit *qc = GetQCircuitAt(i);
    ofs << qc->GetSaveText().c_str() << endl;
  }
  ofs.close();
  //Save Intermediate code automatically
  SaveMidCode(filename);
  Modified = false;
  FileName = filename;
}
//---------------------------------------------------------------------------
void
QManager::LoadFromFile(const char *filename) {

  ifstream ifs;
  ifs.open(filename,ios::in);
  vector<string> v;
  string sVersion,sLine,sColumn;
  getline(ifs,sVersion);
  getline(ifs,sLine);
  getline(ifs,sColumn);
  int l = atoi(sLine.c_str());
  int c = atoi(sColumn.c_str());

  Init(l,c);
  DeleteAll();
  string line;
  while(getline(ifs,line)){
    v = StrUtils::split_str(line);
    int x = atoi(v[0].c_str());
    int y = atoi(v[1].c_str());
    string TypeStr = v[2];
    string Param = v[3].substr(1,v[3].length()-2);
    QCircuit *qc = QCircuit::Create(TypeStr,x,y,Param.c_str());
    Add(qc);
  }
  FileName = filename;
  Modified = false;
  SetAllCircuitFlg();
}
//---------------------------------------------------------------------------
/**
 * Save Intermediate code.
 */
void
QManager::SaveMidCode(char * _filename) {
  string filename = _filename;
  unsigned int p = filename.find_last_of(".");
  if(p!=filename.length()){
    filename.replace(p,4,".mcd");
  }else{
    filename+=".mcd";
  }

  ofstream ofs(filename.c_str());
  ofs << "# file name: \"" << filename << "\"" << endl;
  ofs << "# QCAD MIDCODE" << endl;
  ofs << GetCalcText() << endl;
  ofs.close();
}
//---------------------------------------------------------------------------
/**
 * Remove Selected Circuits
 */
void
QManager::RemoveSelectedCircuits(QDraw *qDraw) {
  QUndoRemove *qu = new QUndoRemove();
  for (int i=0;i<qSelector->GetSelectedCount();i++) {
    QCircuit *qc = qSelector->GetSelectedCircuit(i);
    if (qc->GetType() != QC_BITBOX) {
      qu->Add(qc);
      Remove(qc);
    }
  }
  qUndoManager->Add(qu);
  qSelector->DeleteAll();
  SetAllCircuitFlg();
  DrawAll(qDraw);
}
//---------------------------------------------------------------------------
QCircuit*
QManager::GetQCircuitAt(int x,int y) {
  for (int i=0;i<GetQCircuitCount();i++) {
    QCircuit *qc = GetQCircuitAt(i);
    if (x==qc->GetX() && y == qc->GetY()) {
      return qc;
    }
  }
  return NULL;
}
//---------------------------------------------------------------------------
void
QManager::Undo(QDraw *qDraw) {
  if (qUndoManager->GetUndoCount()==0) {
    return;
  }
  qUndoManager->Undo(qDraw,this);
  SetAllCircuitFlg();
  DrawAll(qDraw);
  Modified = true;
}
//---------------------------------------------------------------------------
void
QManager::ReverseAll(void) {
  for (int i=0;i<GetQCircuitCount();i++) {
    QCircuit *qc = GetQCircuitAt(i);
    qc->Reverse(LineNumber-1);
  }
}
//---------------------------------------------------------------------------
// Drawing Methods
//---------------------------------------------------------------------------
void
QManager::DrawGridPrinter(QDraw *qDraw) {
  int GridSize = qDraw->GetGridSize();

  int Width = ColumnNumber*GridSize;
  int Height = LineNumber*GridSize;

  qDraw->SetBrushColor(clWhite);
  qDraw->FillRect(0,0,Width,Height);

  // Draw holizontal lines of circuit
  qDraw->SetPenColor(clGray);
  for (int i=0;i<LineNumber;i++) {
    qDraw->DrawLine(0,i*GridSize+GridSize/2,Width,i*GridSize+GridSize/2);
  }

  // Draw vertical lines for each grid
  if (PrtSt.ShowVtLineFlg) {
    for (int i=0;i<LineNumber;i++) {
      int y = i*GridSize+GridSize/2;
      for (int j=0;j<ColumnNumber;j++) {
        int x = j*GridSize+ GridSize/2;
        qDraw->DrawLine(x,y-GridSize/4,x,y+GridSize/4);
      }
    }
  }

  //Write Bit number
  if (PrtSt.ShowIndexFlg) {
    for (int i = 0; i < LineNumber; i++) {
      ostringstream os;
      os << "Q" << (i+1);
      qDraw->TextOut(-GridSize/2,i*GridSize + GridSize/2,os.str());
    }
  }
}
//---------------------------------------------------------------------------
void
QManager::DrawStepBar(QDraw *qDraw) {
  int GridSize = qDraw->GetGridSize();
  int x1 = StepLine * GridSize;
  int Height = LineNumber*GridSize-1;
  int x2 = x1 + GridSize;

  qDraw->SetBrushColor(clRed);
  qDraw->DrawLine(x1,0,x1,Height);
  qDraw->DrawLine(x2,0,x2,Height);
  qDraw->DrawLine(x1,0,x2,0);
  qDraw->DrawLine(x1,Height,x2,Height);
}
//---------------------------------------------------------------------------
void
QManager::DrawAll(QDraw *qDraw) {
  qDraw->DrawGrid(ColumnNumber,LineNumber);
  for (int i=0;i<GetQCircuitCount();i++) {
    QCircuit *qc = GetQCircuitAt(i);
    qc->Draw(qDraw);
  }
  qSelector->Select(qDraw);
}
//---------------------------------------------------------------------------
void
QManager::DrawAllPrinter(QDraw *qDraw) {
  DrawGridPrinter(qDraw);
  for (int i=0;i<GetQCircuitCount();i++) {
    QCircuit *qc = GetQCircuitAt(i);
    qc->Draw(qDraw);
  }
}
//---------------------------------------------------------------------------
// Methods for Calculation
//---------------------------------------------------------------------------
string
QManager::GetCalcText(void) {

  ostringstream os;
  os << "INIT(" << LineNumber << ")" << endl;
  for (int i=0;i<ColumnNumber;i++) {
    for (int j=0;j<LineNumber;j++) {
      QCircuit *qc = GetQCircuitAt(i,j);
      if (qc == NULL || qc->GetCalcText() == "blank") {
        continue;
      }
      os << qc->GetCalcText().c_str() << endl;
    }
  }
  return os.str();
}
//---------------------------------------------------------------------------
void
QManager::CalcAll(QBits *qBits) {
  istringstream iss(GetCalcText().c_str());
  QCalcManager *qCalcManager = new QCalcManager(iss);
  qCalcManager->Calc(qBits);
  delete qCalcManager;
}
//---------------------------------------------------------------------------
/**
 * For step by step calculation
 */
void
QManager::CalcAt(QBits *qBits,int Line) {
  //TODO:
}
//---------------------------------------------------------------------------
/**
 * For step by step calculation
 */
void
QManager::CalcStep(QBits *qBits,QDraw *qDraw) {
  //TODO:
  DrawAll(qDraw);
  DrawStepBar(qDraw);
}
//---------------------------------------------------------------------------
// Export PostScript
//---------------------------------------------------------------------------
void
QManager::SaveAsEPS(const char *filename) {

  QPSDraw *psDraw = new QPSDraw();
  DrawAllPS(psDraw);
  ofstream ofs(filename);
  ofs << psDraw->GetText();
  ofs.close();
  delete psDraw;
}
//---------------------------------------------------------------------------
void
QManager::DrawAllPS(QPSDraw *psDraw) {

  int GridSize = psDraw->GetGridSize();
  int Width = ColumnNumber*GridSize;
  int Height = LineNumber*GridSize;
  psDraw->SetWidth(Width);
  psDraw->SetHeight(Height);

  ReverseAll();

  for (int i=0;i<LineNumber;i++) {
    QBitbox *qb = (QBitbox*)GetQCircuitAt(0,i);
    psDraw->DrawGrid(i,qb->Enabled);
  }

  for (int i=0;i<GetQCircuitCount();i++) {
    QCircuit *qc = GetQCircuitAt(i);
    qc->DrawPS(psDraw);
  }
  ReverseAll();
}
//---------------------------------------------------------------------------
void
QManager::AddSelect(QCircuit *qc, QDraw *qDraw) {
  if (qSelector->GetSelectedCount() == 0) {
    QUndoQuitSelect *qu = new QUndoQuitSelect(qSelector);
    qUndoManager->Add(qu);
  }
  qSelector->AddRemove(qc);
  DrawAll(qDraw);
}
//---------------------------------------------------------------------------
void
QManager::RemoveSelect(QDraw *qDraw) {
  if (qSelector->GetSelectedCount() != 0) {
    QUndoQuitSelect *qu = new QUndoQuitSelect(qSelector);
    qUndoManager->Add(qu);
    qSelector->DeleteAll();
    DrawAll(qDraw);
  }
}
//---------------------------------------------------------------------------
void
QManager::ModifyCircuit(QCircuit * qc0, QCircuit* qc1) {
  Modified = true;
  QUndoModify * qu = new QUndoModify(qc0, qc1);
  qUndoManager->Add(qu);
}
//---------------------------------------------------------------------------
void
QManager::Swap(QCircuit * qc0, QCircuit * qc1) {

  if(Contains(qc0)) {
    Remove(qc0);
  }
  Add(qc1);
  delete qc0;

}
//---------------------------------------------------------------------------
void
QManager::InsertColumn(int x, int width) {
  Modified = true;
  ColumnNumber = GetColumnNumber() + width;
  StepLine = 0;
  for (int i = 0; i < GetQCircuitCount(); i++) {
    QCircuit *qc = GetQCircuitAt(i);
    if (x <= qc->GetX()) {
      qc->SetX(qc->GetX() + width);
    }
  }
  SetAllCircuitFlg();
}
//---------------------------------------------------------------------------

