#include "plot.h"

#include <stdlib.h>
#include <sstream>
#include <iostream>

Plot::Plot(const std::string& pdfFile)
    : _curLineFd(-1),
      _pdfFile(pdfFile),
      _open(true),
      _xAxisText("x"),
      _yAxisText("y"),
      _zAxisText("z"),
      _xRangeHasMin(false),
      _xRangeHasMax(false),
      _yRangeHasMin(false),
      _yRangeHasMax(false),
      _zRangeHasMin(false),
      _zRangeHasMax(false),
      _cbRangeHasMin(false),
      _cbRangeHasMax(false),
      _xRangeMin(0.0),
      _xRangeMax(0.0),
      _yRangeMin(0.0),
      _yRangeMax(0.0),
      _zRangeMin(0.0),
      _zRangeMax(0.0),
      _cbRangeMin(0.0),
      _cbRangeMax(0.0),
      _clipZ(false),
      _logX(false),
      _logY(false),
      _logZ(false),
      _hasBoxes(false),
      _fontSize(24) {}

Plot::~Plot() { Close(); }

void Plot::Close() {
  if (_open) {
    CloseCurFd();
    char tmpPlotFile[] = "/tmp/plot.plt-XXXXXX";
    const int fd = mkstemp(tmpPlotFile);
    if (fd == -1) {
      std::cerr << "mkstemp returned -1" << std::endl;
      throw;
    }
    std::stringstream header;
    header << "set term postscript enhanced color";  // font \"Helvetica"; <--
                                                     // that did not work with
                                                     // some gnuplots.
    // if(_fontSize>0)
    //	header << "," << _fontSize;
    // header << "\"";
    header << "\nset title \"" << _title << '\"' << "\nset pm3d map"  // at s
           << "\nset palette rgbformulae 33,13,10"
           << "\nset xlabel \"" << _xAxisText << '\"' << "\nset ylabel \""
           << _yAxisText << '\"' << "\nset cblabel \"" << _zAxisText << '\"'
           << "\nset datafile missing \"?\""
           << "\nset origin -0.05,-0.1"
           << "\nset size 1.0,1.2"
           << "\nset output \"" << _pdfFile << "\"";
    for (std::vector<std::string>::const_iterator i = _extraHeaders.begin();
         i != _extraHeaders.end(); ++i)
      header << *i;
    if (_logX) header << "\nset log x";
    if (_logY) header << "\nset log y";
    if (_logZ) header << "\nset log z";
    if (_xRangeHasMin || _xRangeHasMax) {
      header << "\nset xrange [";
      if (_xRangeHasMin) header << _xRangeMin;
      header << ":";
      if (_xRangeHasMax) header << _xRangeMax;
      header << "]";
    }
    if (_yRangeHasMin || _yRangeHasMax) {
      header << "\nset yrange [";
      if (_yRangeHasMin) header << _yRangeMin;
      header << ":";
      if (_yRangeHasMax) header << _yRangeMax;
      header << "]";
    }
    if (_zRangeHasMin || _zRangeHasMax) {
      header << "\nset zrange [";
      if (_zRangeHasMin) header << _zRangeMin;
      header << ":";
      if (_zRangeHasMax) header << _zRangeMax;
      header << "]";
    }
    if (_cbRangeHasMin || _cbRangeHasMax) {
      header << "\nset cbrange [";
      if (_cbRangeHasMin) header << _cbRangeMin;
      header << ":";
      if (_cbRangeHasMax) header << _cbRangeMax;
      header << "]";
    }
    if (_hasBoxes) {
      header << "\nset boxwidth 0.3";
    }
    if (_lineTypes.size() > 0) {
      switch (_lineTypes[0]) {
        case Line:
        case Scatter:
        case Boxes:
          header << "\nplot \\";
          break;
        case Grid:
          header << "\nsplot \\";
          break;
      }
      for (unsigned i = 0; i < _lineFiles.size(); ++i) {
        if (i != 0) header << ",\\";
        switch (_lineTypes[i]) {
          case Line:
            header << "\n\"" << _lineFiles[i] << "\" using 1:2 title \""
                   << _lineTitles[i] << "\" with lines lw 3";
            break;
          case Scatter:
            header << "\n\"" << _lineFiles[i] << "\" using 1:2 title \""
                   << _lineTitles[i] << "\" with points";
            break;
          case Boxes:
            header << "\n\"" << _lineFiles[i] << "\" using 1:2 title \""
                   << _lineTitles[i] << "\" with boxes fs solid 1.0";
            break;
          case Grid:
            header << "\n\"" << _lineFiles[i]
                   << "\" using ($1):($2):($3) title \"" << _lineTitles[i]
                   << "\" with pm3d lw 3";
            break;
        }
      }
      header << "\n";
      Write(fd, header.str());
      std::cout << "gnuplot" << std::endl;
      ExecuteCmd((std::string("gnuplot ") + tmpPlotFile).c_str());
      std::cout << "mv" << std::endl;
      ExecuteCmd(
          (std::string("mv ") + _pdfFile + " " + _pdfFile + ".ps").c_str());
      std::cout << "ps2pdf" << std::endl;
      ExecuteCmd(
          (std::string("ps2pdf ") + _pdfFile + ".ps " + _pdfFile).c_str());
      std::cout << "rm" << std::endl;
      ExecuteCmd((std::string("rm ") + _pdfFile + ".ps").c_str());
    }
    _open = false;
  }
}

void Plot::Show() { ExecuteCmd((std::string("./kpdf ") + _pdfFile).c_str()); }

void Plot::CloseCurFd() {
  if (_curLineFd != -1) {
    close(_curLineFd);
  }
}

void Plot::StartLine(const std::string& lineTitle) {
  CloseCurFd();
  char tmpLineFile[] = "/tmp/line.txt-XXXXXX";
  _curLineFd = mkstemp(tmpLineFile);
  if (_curLineFd == -1) {
    std::cerr << "mkstemp returned -1" << std::endl;
    throw;
  }
  _lineFiles.push_back(tmpLineFile);
  _lineTitles.push_back(lineTitle);
  _lineTypes.push_back(Line);
  _curType = Line;
}

void Plot::StartScatter(const std::string& lineTitle) {
  CloseCurFd();
  char tmpLineFile[] = "/tmp/line.txt-XXXXXX";
  _curLineFd = mkstemp(tmpLineFile);
  if (_curLineFd == -1) {
    std::cerr << "mkstemp returned -1" << std::endl;
    throw;
  }
  _lineFiles.push_back(tmpLineFile);
  _lineTitles.push_back(lineTitle);
  _lineTypes.push_back(Scatter);
  _curType = Scatter;
}

void Plot::StartBoxes(const std::string& lineTitle) {
  _hasBoxes = true;
  CloseCurFd();
  char tmpLineFile[] = "/tmp/line.txt-XXXXXX";
  _curLineFd = mkstemp(tmpLineFile);
  if (_curLineFd == -1) {
    std::cerr << "mkstemp returned -1" << std::endl;
    throw;
  }
  _lineFiles.push_back(tmpLineFile);
  _lineTitles.push_back(lineTitle);
  _lineTypes.push_back(Boxes);
  _curType = Boxes;
}

void Plot::StartGrid(const std::string& lineTitle) {
  CloseCurFd();
  char tmpLineFile[] = "/tmp/line.txt-XXXXXX";
  _curLineFd = mkstemp(tmpLineFile);
  if (_curLineFd == -1) {
    std::cerr << "mkstemp returned -1" << std::endl;
    throw;
  }
  _lineFiles.push_back(tmpLineFile);
  _lineTitles.push_back(lineTitle);
  _lineTypes.push_back(Grid);
  _curType = Grid;
}

void Plot::PushDataPoint(long double x, long double y) {
  if (_curLineFd == -1) throw;
  std::stringstream s;
  s << x << "\t" << y << std::endl;
  Write(_curLineFd, s.str());
}

void Plot::PushDataPoint(long double x, long double y, long double z) {
  if (_curLineFd == -1) throw;
  if (_clipZ) {
    if (_zRangeHasMin && z < _zRangeMin) z = _zRangeMin;
    if (_zRangeHasMax && z > _zRangeMax) z = _zRangeMax;
  }
  std::stringstream s;
  s << x << "\t" << y << "\t" << z << std::endl;
  Write(_curLineFd, s.str());
}

void Plot::PushUnknownDataPoint(long double x, long double y) {
  if (_curLineFd == -1) throw;
  std::stringstream s;
  s << x << "\t" << y << "\t?" << std::endl;
  Write(_curLineFd, s.str());
}

void Plot::PushDataBlockEnd() { Write(_curLineFd, "\n"); }

void Plot::AddRectangle(long double x1, double y1, double x2, double y2) {
  std::stringstream s;
  s << "\nset object " << (_extraHeaders.size() + 1)
    << " "
       "rectangle from "
    << x1 << "," << y1 << " to " << x2 << "," << y2
    << " front lw 0 fc rgb \"#FF00FF\" fillstyle solid 1.0 noborder";
  _extraHeaders.push_back(s.str());
}

void Plot::ExecuteCmd(const std::string& cmd) const {
  if (system(cmd.c_str()) != 0)
    throw std::runtime_error("system() returned non-zero");
}
