#include "listfile.h"
#include <QtCore>
#include <QTextStream>

/*
 * This file handles the ShelXl lst file during and after the refinement
 *
 * TODO:
 * - Make running SHELXL more visual. e.g. progress bar (especially while writing cif file)
 *
 L.S.:
 wR2 =  0.165168 before cycle   1 for   29437 data and   2607 /   2607 parameters
 GooF = S =     1.509;     Restrained GooF =      1.434 for    3259 restraints
 Mean shift/esd =   0.013  Maximum =     1.375 for  U23 C54        at 09:31:33
 Max. shift = 0.008 A for C54        Max. dU =-0.010 for C54
 *
 CGLS:
 wR2 =  0.165155 before cycle   1 for   29437 data and   2607 /   2607 parameters
 GooF = S =     1.509;     Restrained GooF =      1.434 for    3257 restraints
 Max. shift = 0.004 A for C54        Max. dU =-0.007 for C54
 */

ListFileHandler::ListFileHandler(QString lfpath, QObject *parent) : QObject(parent)
{
  REL_RESTR_CARDS << "SAME"<< "SADI"<< "SIMU"<< "RIGU"<< "ISOR"<< "NCSY"<< "FLAT" << "DELU";
  my_lstpath = lfpath;
  //QFileSystemWatcher fw;  // The filesystem watcher doesn't work in my windows 8.1
  //fw.addPath(*my_lstpath);  // I am not shure why I added it? It works also without. DK
  success = false; // turns to true if refinement was a sucess.
  listfcontent = new QStringList;
  listfcontent->append(this->loadLst());
}


QStringList ListFileHandler::loadLst(){
  //! Reads the SHELXL lst file from disk
  //! and returns it as a QStringlist
  QFile file(my_lstpath);
  QStringList lstList;
  if (file.open(QFile::ReadOnly | QFile::Text)) {
    QTextStream textStream(&file);
    while (true) {
      QString line = textStream.readLine();
      if (line.isNull()) {
          emit lstFinished();
          break;
      }
      if (line.isEmpty()) {
        continue;
      } else {
        lstList.append(line);
      }
    }
  }
  file.close();
  return lstList;
}

QVector<QStringList> ListFileHandler::listfileparser() {
  /*!
  Gathers the residuals of the SHELXL lst file.
  It searches for the final cycle summary and then for " Disagreeable restraints".
  End is reached with ' Summary of restraints'.
  Ported from: https://github.com/dkratzert/DSR-db

 Disagreeable restraints before cycle    6

   Observed   Target    Error     Sigma     Restraint

                        0.1487    0.0400    SAME/SADI O1_1b C2_1b O1_1b C4_1b

  */
  if (listfcontent->isEmpty()) {
    QVector<QStringList> dummy;
    return dummy;
  }
  bool disag = false;
  bool final = false;
  QVector<QStringList> disargeelist;
  int num = 0;
  foreach(QString line, *listfcontent) {
    QStringList splitline;
    splitline = line.split(" ", QString::SkipEmptyParts);
    if (splitline.isEmpty()) {
      continue;
    }
    if (splitline[0].startsWith("Observed")) {
      continue;
    }
    if (line.startsWith(" Final Structure Factor")) {
      final = true;
    }
    if (final && line.startsWith(" Disagreeable restraints")) {
      disag = true;
      continue;
    }
    if (line.startsWith(" Summary of restraints applied")) {
      final = false;
      disag = false;
    }
    if (line.contains("RIGU")) {
      continue;  // leave out RIGU, it produces too many lines...
    }
    if (splitline.size() < 2) continue;
    if (disag) {
      // in this case, the desired line is found:
      disargeelist.append(lineformatter(splitline));
      num = num+1;
      if (num > 500) {
        // Cutting restraints list. Too many bad restraints...
        return disargeelist;
      }
    }
  }
  return disargeelist;
}


QStringList ListFileHandler::lineformatter(QStringList line) {
  /*!
  takes care of some extra things with different restraints. For example
  RIGU xy should be in one column.
  Ported from: https://github.com/dkratzert/DSR-db
  */
  int num = 0;
  int pos = 0;
  foreach(QString i, line) {
    i = i.replace('/', ' ');
    if (i[0].isLetter()){
      pos = num;
      break;
    }
    num += 1;
  }
  // remove the part symbol from e.g. F1_2a:
  /*
  num = 0;
  foreach (QString i, line) {
    line[num] = remove_partsymbol(i);
    num += 1;
  } */
  // joining columns without numbers:
  QStringList posline = line.mid(pos, -1);
  QString joinline = posline.join(" ");
  QStringList newline = line.mid(0, pos);
  line = newline;
  line.append(joinline);
  QString tline = line.join(" ");
  foreach (QString n, REL_RESTR_CARDS) {
    if (tline.contains(n, Qt::CaseInsensitive)) {
      // adding placeholders for empty fields:
      QStringList nline;
      nline.append("-");
      nline.append("-");
      nline.append(line);
      return nline;
    }
  }
  return line;
}


QString ListFileHandler::table_maker(QVector<QStringList> tabledata) {
    /*!
    Builds a html table out of a datalist from the final
    cycle summary of a shelxl list file.
    */
    QStringList table;
    QString header;
    QString footer;
    QString html;
    table.clear();
    foreach(QStringList line, tabledata) {
      table.append(this->row(line));
    }
    header = QString(
        "<body style=\"font-family:SansSerif; font-size:11pt;\">"
        "<table>"
        "<tr>"
          "<td width=60% align=left>"
          "<b>List of most disagreeable restraints:</b> &nbsp;"
          "</td>"
          "<td width=40% align=right>"
          "</td>"
        "</tr>"
        "</table>");
    footer = "</body>";
    html = QString(
      "%1"
      "<table border=0 cellpadding=0 cellspacing=6 width=100% >"
        "<tr>"
           "<td align='center'> Observed </td>"
           "<td align='center'> Target   </td>"
           "<td align='center'> Error    </td>"
           "<td align='center'> Sigma    </td>"
           "<td align='left'> Restraint  </td>"
        "</tr>"
        "%2"
      "</table>"
        "%3").arg(header, table.join("\n"), footer);

    if(table.isEmpty()) {
      //return header+empty_data;
      return QString(header+"All restraint deviations are within their standard deviation.");
      //return QString("");
    }
    return html;
}

QString ListFileHandler::row(QStringList rowdata) {
    /*!
    creates a table row for the restraints list.
    :type rowdata: list
    */
    QStringList td;
    td.clear();
    QString row = "";
    QString bgcolor = "";
    if (rowdata.size() >= 5) {
      bool ok1 = false;
      bool ok2 = false;
      rowdata[2].toFloat(&ok1);
      rowdata[3].toFloat(&ok2);
      if (ok1 && ok2) {
        if (qAbs(rowdata.at(2).toDouble()) > 2.5*rowdata[3].toDouble()) {
          bgcolor = QString("bgcolor='#ffec99'"); // bgcolor='#FFD100'  // yellow
        }
        if (qAbs(rowdata.at(2).toDouble()) > 3.5*rowdata[3].toDouble()) {
          bgcolor = QString("bgcolor='#ff9fac'"); // bgcolor='#FF1030'  // red
        }
      }
    }
    int num = 0;
    foreach (QString item, rowdata) {
      bool ok = false;
      item.toDouble(&ok);
      if (ok) {
        // align right for numbers:
        if (num < 2) {
          // do not colorize the first two columns:
          td.append(QString("<td align='right'> %1 </td>").arg(item));
        } else {
          td.append(QString("<td align='right' %1> %2 </td>").arg(bgcolor, item));
        }
      } else {
        if (item.startsWith('-')) {
          // only a minus sign
          td.append(QString("<td align='center'> %1 </td>").arg(item));
        } else {
          if (num < 4) {
            td.append(QString("<td align='right'> %1 </td>").arg(item));
            continue;
          }
          // align left for words:
          td.append(QString("<td align='left'> %1 </td>").arg(item));
        }
      }
      num += 1;
    }
    if (td.isEmpty()) {
      row = "<tr> No (disagreeable) restraints found in .lst file. </tr>";
    } else {
      row = QString("<tr> %1 </tr>").arg(td.join(""));
    }
    td.clear();
    return row;
}

QString ListFileHandler::restraintsTable() {
  QString txt;
  txt.append(table_maker(listfileparser()));
  return txt;
}

bool ListFileHandler::getSuccess() const {
    return success;
  }

void ListFileHandler::setSuccess(bool value) {
    success = value;
  }

QString ListFileHandler::thetaFromLst() {
  //! Max. 2-theta =   55.30
  //!   -8 =< h =<  8,    -21 =< k =< 21,    -21 =< l =< 21,   Max. 2-theta =   55.30
  //! I might need this later
  QString theta = "";
  foreach(QString line, *listfcontent) {
    if (line.contains("Max. 2-theta")) {
      theta = line.section("theta", 1, 1).split(" ", QString::SkipEmptyParts).at(1);
    }
  }
  return theta;
}


// ##########################################################################


XLOutputParser::XLOutputParser(QObject *parent) : QObject(parent) {
  //! Handles the content of the SHELXL list file.
  //! Methods with "current" in the name return values of the current state
  //! in the continuously growing SHELXL list file.
  //! Methods with "final" return the values after successful refinement.
  //!
  // The constructor:
  ls_cycles = 0;
  listf_data.cmdline = "";
  listf_data.xlversion = "";
  listf_data.threads = "";
  listf_data.procs = "";
  listf_data.rint = "";
  listf_data.rsigma = "";
  listf_data.wr2 = "";
  listf_data.cycle = "";
  listf_data.data = "0";
  listf_data.current_parameters = "0";
  listf_data.all_parameters = "0";
  listf_data.curGoof = " ";
  listf_data.curRGoof = " ";
  listf_data.R1_4sig = "";
  listf_data.finalwR2 = "";
  listf_data.maxshift_esd = "";
  listf_data.maxshiftfor = "";
  listf_data.meanshift = "";
  listf_data.maxshift = "";
  listf_data.dU = "";
  listf_data.dUfor = "";
  listf_data.peak = "";
  listf_data.hole = "";
  listf_data.peakpos = "";
  listf_data.holepos = "";
  listf_data.peakdist = "";
  listf_data.holedist = "";
  listf_data.warning = "";
  listf_data.warnings.clear();
  listf_data.time = "";

  rgr.Goof = 0.0;
  rgr.cycle = 0.0;
  rgr.dU = 0.0;
  rgr.dUfor = "";
  rgr.wR2 = 0.0;
  rgr.final_wR2 = 0.0;
  rgr.max_shift_esd_for = "";
  rgr.maxshift = 0.0;
  rgr.maxshift_esd = 0.0;
  rgr.maxshiftfor = "";
  rgr.meanshift = 0.0;
  rgr.warnings.clear();
  rgr.time = 0.0;
  warnings.clear();
}


RGraph XLOutputParser::residualsGraphDisplay() {
  //! Returns a data structure that collects the values of
  //! wR2, R1, Goof, ... during refinement to display them as
  //! a graph.
  // collect data from each line and append it to RGraph
  rgr.Goof = listf_data.curGoof.toDouble();
  rgr.wR2 = listf_data.wr2.toDouble();
  rgr.maxshift = listf_data.maxshift.toDouble();
  rgr.meanshift = listf_data.meanshift.toDouble();
  rgr.cycle = listf_data.cycle.toInt();
  rgr.final_wR2 = listf_data.finalwR2.toDouble();
  rgr.warnings = listf_data.warnings;
  return rgr;
}


void XLOutputParser::fillListFileData(QString line) {
  //! Fills listf_data with data but it is only filled
  //! if the respective strings contain data
  QStringList tproc = xl_threads(line);
  QString cmdline = xl_cmdline(line);
  QString version = xl_version(line);
  QStringList rintsig = xl_rint_rsigma(line);
  QStringList wr2data = xl_current_wr2_data_param(line);
  QString R1_4sig = xl_final_R1_4sig(line);
  QString R1_all = xl_final_R1all(line);
  QString finalwR2 = xl_final_wR2(line);
  QStringList goofs = xl_current_goof(line);
  QStringList peakvalues = xl_peak(line);
  QStringList holevalues = xl_hole(line);
  //QStringList fgoofs = xl_final_goofs(line);
  QStringList maxshifts = xl_current_max_shift(line);
  QStringList meanshifts = xl_current_meanshift_esd(line);
  xl_collect_warnings(line);
  QStringList flackvals = xl_flacksvals(line);
  QString timeval = xl_timeval(line);

  // Only fill listf_data with values if there are any:
  if (!cmdline.isEmpty()) {
    listf_data.cmdline = cmdline;
  }
  if (!version.isEmpty()) {
    listf_data.xlversion = version;
  }
  if ((!tproc[0].isEmpty()) && (!tproc[1].isEmpty())) {
    listf_data.threads = tproc[0];
    listf_data.procs = tproc[1];
  }
  if ((!rintsig[0].isEmpty()) && (!rintsig[1].isEmpty())) {
    listf_data.rint = rintsig[0];
    listf_data.rsigma = rintsig[1];
  }
  if (!wr2data.at(0).isEmpty()) {
    listf_data.wr2 = wr2data.at(0);
  }
  if (!wr2data.at(1).isEmpty()) {
    emit newLScycle();
    listf_data.cycle = wr2data.at(1);
  }
  if (!wr2data.at(2).isEmpty()) {
    listf_data.data = wr2data.at(2);
  }
  if (!wr2data.at(3).isEmpty()) {
    listf_data.current_parameters = wr2data.at(3);
  }
  if (!wr2data.at(4).isEmpty()) {
    listf_data.all_parameters = wr2data.at(4);
  }
  if (!goofs[0].isEmpty() && !goofs[1].isEmpty()) {
    listf_data.curGoof = goofs[0];
    listf_data.curRGoof = goofs[1];
  }
  if (!goofs[2].isEmpty()) {
    listf_data.nrestraints = goofs[2];
  }
  if (!R1_4sig.isEmpty()) {
    listf_data.R1_4sig = R1_4sig;
  }
  if (!R1_all.isEmpty()) {
    listf_data.R1_all = R1_all;
  }
  if (!finalwR2.isEmpty()) {
    listf_data.finalwR2 = finalwR2;
  }
  if (!maxshifts[0].isEmpty() && !maxshifts[1].isEmpty()) {
    //shifts.append(maxshift);
    //shifts.append(maxshiftfor);
    //shifts.append(dU);
    //shifts.append(dUfor);
    listf_data.maxshift = maxshifts[0];
    listf_data.maxshiftfor = maxshifts[1];
    listf_data.dU = maxshifts[2];
    listf_data.dUfor = maxshifts[3];
  }
  if (!meanshifts[0].isEmpty() && !meanshifts[2].isEmpty()) {
    //shifts.append(meanshift);
    //shifts.append(maxshift_esd);
    //shifts.append(max_esd_shiftfor);
    listf_data.meanshift = meanshifts[0];
    listf_data.maxshift_esd = meanshifts[1];
    listf_data.max_shift_esd_for = meanshifts[2];
  }
  if (!holevalues[0].isEmpty() && !holevalues[1].isEmpty() && !holevalues[2].isEmpty()
      && !holevalues[3].isEmpty()) {
    listf_data.hole = holevalues[0];
    listf_data.holepos = holevalues[1];
    listf_data.holedist = holevalues[2];
    listf_data.holefrom = holevalues[3];
  }
  if (!peakvalues[0].isEmpty() && !peakvalues[1].isEmpty() && !peakvalues[2].isEmpty()
      && !peakvalues[3].isEmpty()) {
    listf_data.peak = peakvalues[0];
    listf_data.peakpos = peakvalues[1];
    listf_data.peakdist = peakvalues[2];
    listf_data.peakfrom = peakvalues[3];
  }
  if (!flackvals[0].isEmpty()) {
    listf_data.flack = flackvals.at(0);
  }
  if (!flackvals[1].isEmpty()) {
    listf_data.parson = flackvals.at(1);
  }
  if (!timeval.isEmpty()) {
    listf_data.time = timeval;
  }
}

QString XLOutputParser::residualsTextDisplay(QString &line, int ls_cycles) {
  //! returns a html string to display the refinement results in a QLabel
  this->ls_cycles = ls_cycles;
  fillListFileData(line);
  QString html_output = "";
  QString html_final = "";
  QString wr2line = "";
  QString shiftyellow = "";
  QString shift_esd_yellow = "";
  QString dUyellow = "";
  QString cycleText = "";
  QString progressbar = "";
  float restr_ratio = 0.0;
  float data_to_param_ratio = 0.0;
  if (!listf_data.data.isEmpty() && !listf_data.nrestraints.isEmpty() && !listf_data.all_parameters.isEmpty()){
    restr_ratio = (listf_data.data.toDouble()+listf_data.nrestraints.toInt())/listf_data.all_parameters.toDouble();
    data_to_param_ratio = listf_data.data.toFloat()/listf_data.all_parameters.toFloat();
  }
  if (!listf_data.R1_4sig.isEmpty() && !listf_data.finalwR2.isEmpty()) { // final values:
    html_final = QString("<tr><td align=right><b><i>wR</i><sub>2</sub> = </b></td> <td><b> %2% </b>(all data) </td>"
                         "    <td align=right><b><i>R</i><sub>1</sub></b> [<i>I</i> > 4&sigma;<i>I</i>] = </td> <td><b>%1%</b> (%9% all data)</td></tr>"
                         "<tr><td align=right>Highest peak = </td> <td> %3 e&Aring;<sup>-3</sup> at %4 &Aring; from %7 &nbsp;&nbsp;&nbsp;&nbsp;</td>"
                         "    <td align=right>Deepest hole = </td> <td> %5 e&Aring;<sup>-3</sup> at %6 &Aring; from %8 </td></tr>"
                         )
          .arg(listf_data.R1_4sig.toDouble()*100, 4, 'f', 2, 0)      // 1
          .arg(listf_data.finalwR2.toDouble()*100, 4, 'f', 2, 0)     // 2
          .arg(listf_data.peak)           // 3
          .arg(listf_data.peakdist)       // 4
          .arg(listf_data.hole)           // 5
          .arg(listf_data.holedist)       // 6
          .arg(listf_data.peakfrom)       // 7
          .arg(listf_data.holefrom)       // 8
          .arg(listf_data.R1_all.toDouble()*100, 4, 'f', 2, 0)       // 9
        ;
    // One of flack or parson exists:
    if (!listf_data.flack.isEmpty() || !listf_data.parson.isEmpty()) {
      // both exist:
      if (!listf_data.flack.isEmpty() && !listf_data.parson.isEmpty()) {
        wr2line = QString("<tr><td align=right> <b>Flack X </b>= </td> <td> %1 (Parsons' method)</td> <td>  </td> </tr> "
                          "<tr><td align=right> </td> <td> %2 (classical fit)</td> <td>  </td> </tr>").arg(listf_data.parson).arg(listf_data.flack);
      }
      // Only classical exists:
      if (!listf_data.flack.isEmpty() && listf_data.parson.isEmpty()) {
        wr2line = QString("<tr><td align=right> <b>Flack X </b>= </td> <td> %1 (classical fit)</td> "
                                  "<td>  </td> </tr>").arg(listf_data.flack);
      }
      // Only Parsons exists:
      if (!listf_data.parson.isEmpty() && listf_data.flack.isEmpty()){
        wr2line = QString("<tr><td align=right> <b>Flack X </b>= </td> <td> %1 (Parsons' method)</td> "
                           "<td>  </td> </tr>").arg(listf_data.parson);
      }
    } else { // no flack parameter:
      wr2line = ""; // No Table row to prevent empty line
    }
  } else {  // shelxl is currently runnig:
    html_final = "";
    wr2line = QString("<tr><td align=right> <i>wR</i><sub>2</sub> =   </td> <td> %7% </td></tr>")
                      .arg(listf_data.wr2.toDouble()*100, 4, 'f', 2);
  }
  if (qAbs(listf_data.maxshift.toDouble()) > 0.005) {
    shiftyellow = QString("bgcolor=#ffec99");
  }
  if (qAbs(listf_data.maxshift_esd.toDouble()) > 0.01) {
    shift_esd_yellow = QString("bgcolor=#ffec99");
  }
  if (qAbs(listf_data.dU.toDouble()) > 0.005) {
    dUyellow = QString("bgcolor=#ffec99");
  }
  int repeat = 12;
  if (listf_data.cycle.toInt() < ls_cycles) {
    int progress;
    progress = int((listf_data.cycle.toFloat() / ls_cycles)*10);
    progressbar = QString("<td bgcolor=#69CB3F >%1</td>").arg(QString("&nbsp;").repeated(repeat)).repeated(progress)
                  +QString("<td bgcolor=#D0D0D0 >%1</td>").arg(QString("&nbsp;").repeated(repeat)).repeated(10-progress);
    cycleText = QString("<tr><td> <b>Cycle %1 of %2</b> </td></tr>")
        .arg(listf_data.cycle) // 1
        .arg(ls_cycles)        // 2
        ;
  } else {
    cycleText = QString("<tr><td> <b>Final Cycle of %1</b></td></tr>")
        .arg(ls_cycles);       // 1
    progressbar = QString("<td bgcolor=#69CB3F>%1</td>").arg(QString("&nbsp;").repeated(repeat)).repeated(10);
  }
  html_output = QString(
        "<body style=\"font-family:SansSerif; font-size:11pt;\">"
        "<H2>Running SHELXL %1</H2> "
        "<table cellpadding=3% align=left>"
        "<tr><td colspan=2 align=left> <table><tr> %2 </tr></table> </td></tr>"
        "<tr><td> %3 threads on %4 processors </td></tr>"
        "<tr><td> Using %5 data and %6 of %7 parameters (ratio %8; %11 with restraints) </td></tr>"
        "<tr><td><i>R</i><sub>int</sub> = %9%, <i>R</i><sub>&sigma;</sub> = %10% </td></tr>"
        "</table>")
      .arg(listf_data.xlversion)            // 1
      .arg(progressbar)                     // 2
      .arg(listf_data.threads)              // 3
      .arg(listf_data.procs)                // 4
      .arg(listf_data.data)                 // 5
      .arg(listf_data.current_parameters)   // 6
      .arg(listf_data.all_parameters)       // 7
      .arg(data_to_param_ratio, 10, 'f', 1) // 8  data/restr. ratio
      .arg(listf_data.rint.toFloat()*100, 10, 'f', 2)     // 9
      .arg(listf_data.rsigma.toFloat()*100, 10, 'f', 2)   // 10
      .arg(restr_ratio, 10, 'f', 1) // 11  (data+restraints)/parameter
      ;

  QString maxshift_row = "";

  if (!listf_data.maxshift_esd.isEmpty()) {
    maxshift_row = QString("<tr><td align=right> Max. shift = </td> <td %1> %2 @  %3 </td> </tr>")
      .arg(shift_esd_yellow)                 // 1 color of max shifting param.
      .arg(listf_data.maxshift_esd)          // 2
      .arg(listf_data.max_shift_esd_for)     // 3
      ;
  }

  QString xyz_shift_row = "";

  if (!listf_data.maxshift.isEmpty()) {
    xyz_shift_row = QString("<tr><td align=right> Max. xyz shift =     </td> <td %1> %2 &Aring; @ %3     </td>"
                          "    <td align=right> Max. &Delta;U =      </td> <td %4> %5 @ %6             </td></tr>")
        .arg(shiftyellow)                // 1
        .arg(listf_data.maxshift)        // 2
        .arg(listf_data.maxshiftfor)     // 3
        .arg(dUyellow)                   // 4
        .arg(listf_data.dU)              // 5
        .arg(listf_data.dUfor)           // 6
      ;
  }

  QString html_output2 = QString("<table cellpadding=2%>"
          "%1"
          "%2"
          "%3"
          "<tr><td align=right> <i>GooF</i> =             </td> <td> %4                     </td>"
          "    <td align=right  width=18%> Restr. <i>GooF</i> =  </td> <td> %5              </td></tr>"
          "%6"
          "%7"
          "</table>"
          "<table><tr><td bgcolor=#ffec99><b> %8 </b></td></tr></table>"
          "</body>")
          .arg(cycleText)                        // 1
          .arg(html_final)                       // 2
          .arg(wr2line)                          // 3 //wr2
          .arg(listf_data.curGoof)               // 4
          .arg(listf_data.curRGoof)              // 5
          .arg(xyz_shift_row)                    // 6
          .arg(maxshift_row)                     // 7
          .arg(listf_data.warnings.join("<br>")) // 8

      ;
  if (!listf_data.wr2.isEmpty()) {
    html_output += html_output2;
  } else {
    // In this case, no refinement happened:
    if (!listf_data.warnings.isEmpty()) {
      // even if no refinenemt happened, I want to display the warnings:
      html_output += QString("<table><tr><td bgcolor=#ffec99> %2 </td></tr></table>")
          .arg(listf_data.warnings.join("<br>"));
    } else {
      // no warning, but still waiting for output from SHLEXL:
      html_output += QString("<b>Waiting for output from SHELXL...</b>");
    }
  }
  if (!listf_data.time.isEmpty() && listf_data.wr2.isEmpty()) {
    // SHELXL finished regularly (time elapsed output), but no refinement happened (wr2 is empty)
    html_output += QString("<br><b>SHELXL failed to run! Please inspect the instructions.</b>");
  }
  return html_output;
}


QString XLOutputParser::xl_version(QString line) {
  //! Returns the running shelxl version
  //! " +  Copyright(C) George M. Sheldrick 1993-2014     Version 2014/7  +"
  QRegExp versionregex = QRegExp("Version\\s+\\d+\\/\\d+");
  QString version;
  QStringList linesplit;
  if (line.contains(versionregex)) {
    linesplit = line.split(" ", QString::SkipEmptyParts);
    int index = linesplit.indexOf("Version");
    version = linesplit.at(index+1);
  }
  return version;
}

QStringList XLOutputParser::xl_threads(QString line) {
  //! Returns on how many threads and processors shelxl is running
  //! "Running  6 threads on  8 processors"
  QStringList threadproc;
  QString threads = "";
  QString processors = "";
  QStringList linesplit;
  QRegExp tprocregex = QRegExp("Running\\s+\\d\\s+threads\\s+on\\s+\\d\\s+processors");
  if (line.contains(tprocregex)) {
    linesplit = line.split(" ", QString::SkipEmptyParts);
    threads = linesplit.at(1);
    processors = linesplit.at(4);
  }
  threadproc.append(threads);
  threadproc.append(processors);
  return threadproc;
}

QString XLOutputParser::xl_cmdline(QString line) {
  //! Returns the command line options shelxl was started with
  //! " Command line parameters: p21c -a50000 -b3000 -c624 -t6"
  QString cmdline;
  if (line.contains("Command line parameters:")) {
    cmdline = line;
  }
  return cmdline;
}

QStringList XLOutputParser::xl_current_wr2_data_param(QString line) {
  //! Returns the number of data (reflections) shelxl is refining against.
  //! This is without sigma cutoff:
  //! " wR2 = 0.4016 before cycle   1 for   10786 data and    737 /    737 parameters"
  //! returns list of [wR2, cycles, data, current_parameters, all_parameters]
  QStringList linesplit;
  QString wr2 = "";
  QString cycle = "";
  QString data = "";
  QString current_parameters = "";
  QString all_parameters = "";
  QStringList alldata;
  if (line.contains("wR2") && line.contains("cycle") && line.contains("data")
      && (line.split(" ", QString::SkipEmptyParts).size() >= 12) ) {
    linesplit = line.split(" ", QString::SkipEmptyParts);
    wr2 = linesplit.at(2);
    cycle = linesplit.at(5);
    data = linesplit.at(7);
    current_parameters = linesplit.at(10);
    all_parameters = linesplit.at(12);
  }
  alldata.append(wr2);
  alldata.append(cycle);
  alldata.append(data);
  alldata.append(current_parameters);
  alldata.append(all_parameters);
  return alldata;
}

QStringList XLOutputParser::xl_rint_rsigma(QString line) {
  //! Returns Rint and Rsigma of the dataset.
  //! " R(int) = 0.0504     R(sigma) = 0.0585      Friedel opposites merged"
  //chuebsch: this crashed:
  //Data:      19 unique,      0 suppressed   R(int) = 0.0000   R(sigma) =13.2352
  //
  QString rint = "";
  QString rsigma = "";
  QStringList linesplit, rvals;
  QString l=line.remove('=');
  if (line.contains("R(sigma)")) {
    linesplit = l.split(" ", QString::SkipEmptyParts);
    int RintInd = linesplit.indexOf(QRegExp("R\\(int\\)"));
    if ((RintInd != -1) && linesplit.length() > RintInd+1) {//>= leads to segmentation fault
      rint = linesplit.at(RintInd+1);
    }
    int RsigInd = linesplit.indexOf(QRegExp("R\\(sigma\\)"));
    if ((RsigInd != -1) && linesplit.length() > RsigInd+1) {//>= leads to segmentation fault
      rsigma = linesplit.at(RsigInd+1);
    }//else qDebug()<<linesplit<<RsigInd;

  }
  rvals.append(rint);
  rvals.append(rsigma);
  return rvals;
}

QStringList XLOutputParser::xl_current_goof(QString line) {
  //! Returns the current Goof of the refinement. This value is
  //! the last value in the continuesly growing lst file.
  //! "GooF = S =     4.130;     Restrained GooF =      5.432 for    1662 restraints"
  QString goof;
  QString rgoof;
  QString restraints;
  QStringList linesplit;
  QStringList goofs;
  if (line.contains("Restrained GooF") && line.contains("restraints")
      && (line.split(" ", QString::SkipEmptyParts).size() >= 8)) {
    linesplit = line.split(" ", QString::SkipEmptyParts);
    goof = linesplit.at(4);
    goof.remove(QChar(';'));
    rgoof = linesplit.at(8);
    restraints = linesplit.at(10);
  }
  goofs.append(goof);
  goofs.append(rgoof);
  goofs.append(restraints);
  return goofs;
}

QStringList XLOutputParser::xl_final_goofs(QString line) {
  //! Returns the final Goof after refinement finished.
  //! "wR2 =  0.2277,  GooF = S =   2.275,  Restrained GooF =    2.122  for all data"
  QString goof;
  QString rgoof;
  QStringList linesplit;
  QStringList goofs;
  if (line.contains("for all data") && (line.split(" ", QString::SkipEmptyParts).size() >= 11) ) {
    linesplit = line.split(" ", QString::SkipEmptyParts);
    goof = linesplit.at(7);
    goof.remove(QChar(','));
    rgoof = linesplit.at(11);
  }
  goofs.append(goof);
  goofs.append(rgoof);
  return goofs;
}


QStringList XLOutputParser::xl_current_meanshift_esd(QString line) {
  //! Returns the current shift per esd.
  //! "Mean shift/esd =   0.016  Maximum =    -8.976 for  U11 C6         at 09:36:30
  QStringList shifts;
  QString meanshift = "";
  QString maxshift_esd = "";
  QString max_esd_shiftfor = "";
  QStringList linesplit;
  if (line.contains("shift/esd") && line.contains("Maximum")
      && (line.split(" ", QString::SkipEmptyParts).size() >= 6) ) {
    linesplit = line.split(" ", QString::SkipEmptyParts);
    meanshift = linesplit.at(3);
    maxshift_esd = linesplit.at(6);
    max_esd_shiftfor = line.split("for", QString::SkipEmptyParts).at(1).trimmed()
        .split("at", QString::SkipEmptyParts).at(0).trimmed();
  }
  shifts.append(meanshift);
  shifts.append(maxshift_esd);
  shifts.append(max_esd_shiftfor);
  return shifts;
}


QStringList XLOutputParser::xl_current_max_shift(QString line) {
  //! Returns the maximum shift during the refinement.
  //! "Max. shift = 0.000 A for H26C      Max. dU =-0.007 for C6"
  //! "=-0.007 for C6"
  QString maxshift;
  QString maxshiftfor;
  QString dU;
  QString dUfor;
  QStringList linesplit;
  QStringList shifts;
  if (line.contains("Max. shift") && line.contains("dU") && line.contains("for")
      && (line.split(" ", QString::SkipEmptyParts).size() >= 6) ) {
    linesplit = line.split(" ", QString::SkipEmptyParts);
    maxshift = linesplit.at(3);
    maxshiftfor = linesplit.at(6);
    dU = line.split("dU").at(1).trimmed().remove("=").split(" ", QString::SkipEmptyParts).at(0);
    dUfor = line.split("for").at(2).trimmed();
  }
  shifts.append(maxshift);
  shifts.append(maxshiftfor);
  shifts.append(dU);
  shifts.append(dUfor);
  return shifts;
}


QString XLOutputParser::xl_current_warning(QString line) {
  //! Returns a Warning if there is any in the current line.
  QRegExp warnregexp = QRegExp("\\*\\*");  //.*\\*\\*");
  QString warn = "";
  if ( line.contains(warnregexp) ) {
    warn = line.simplified();
  }
  return warn;
}

void XLOutputParser::xl_collect_warnings(QString line) {
  //! Returns all warning that occoured during refinement.
  QRegExp warnregexp = QRegExp("\\*\\*");  //.*\\*\\*");
  if ( line.contains(warnregexp) ) {
    warnings.append(line.simplified());
  }
  if (line.contains("CANNOT OPEN FILE")) {
    warnings.append(line.simplified());
  }
  listf_data.warnings = warnings;
  rgr.warnings = warnings;
}

QString XLOutputParser::xl_final_R1_4sig(QString line) {
  // R1 =  0.0401 for    7085 Fo > 4sig(Fo)  and  0.0796 for all   10786 data
  QString r1_4sigma = "";
  QStringList linesplit;
  if (line.contains("R1") && line.contains("4sig") && line.contains("for all")) {
    linesplit = line.split(" ", QString::SkipEmptyParts);
    r1_4sigma = linesplit.at(2);
    r1_4sigma.remove(QChar(','));
  }
  return r1_4sigma;
}

QString XLOutputParser::xl_final_R1all(QString line) {
  // R1 =  0.0766 for  10786 unique reflections after merging for Fourier
  QString r1all = "";
  QStringList linesplit;
  if (line.contains("R1") && line.contains("merging for Fourier") && line.contains("for")) {
    linesplit = line.split(" ", QString::SkipEmptyParts);
    r1all = linesplit.at(2);
    r1all.remove(QChar(','));
  }
  return r1all;
}


QString XLOutputParser::xl_final_wR2(QString line) {
  QString finalwr2;
  QStringList linesplit;
  if (line.contains("wR2") && line.contains("for all data")) {
    linesplit = line.split(" ", QString::SkipEmptyParts);
    finalwr2 = linesplit.at(2);
    finalwr2.remove(QChar(','));
  }
  return finalwr2;
}

QStringList XLOutputParser::xl_peak(QString line) {
  //! Returns a list of the highest peak and the position
  // Highest peak    8.15  at  0.3160  0.7496  0.4960  [  1.30 A from F12 ]
  QStringList values;
  QString peak = "";
  QString coord = "";
  QString dist = "";
  QString from = "";
  QStringList linesplit;
  if (line.contains("Highest peak") && line.contains("from")
      && (line.split(" ", QString::SkipEmptyParts).size() >= 6) ) {
    linesplit = line.split(" ", QString::SkipEmptyParts);
    peak = linesplit.at(2);
    coord = linesplit.at(4)+" "+linesplit.at(5)+" "+linesplit.at(6);
    dist = line.split("[").at(1).split("]").at(0).trimmed().split(" ").at(0);
    from = line.split("[").at(1).split("]").at(0).trimmed().split(" ").at(3);
  }
  values.append(peak);
  values.append(coord);
  values.append(dist);
  values.append(from);
  return values;
}

QStringList XLOutputParser::xl_hole(QString line) {
  //! Returns a list of the deepest hole and the position
  // Deepest hole   -0.74  at  0.4337  0.4402  0.7821  [  0.84 A from GA1 ]
  QStringList values;
  QString hole = "";
  QString coord = "";
  QString dist = "";
  QString from = "";
  QStringList linesplit;
  if (line.contains("Deepest hole") && line.contains("from")
      && (line.split(" ", QString::SkipEmptyParts).size() >= 12) ) {
    linesplit = line.split(" ", QString::SkipEmptyParts);
    hole = linesplit.at(2);
    coord = linesplit.at(4)+" "+linesplit.at(5)+" "+linesplit.at(6);
    dist = line.split("[").at(1).split("]").at(0).trimmed().split(" ").at(0);
    from = line.split("[").at(1).split("]").at(0).trimmed().split(" ").at(3);
  }
  values.append(hole);
  values.append(coord);
  values.append(dist);
  values.append(from);
  return values;
}

QStringList XLOutputParser::xl_flacksvals(QString line) {
  //! Flack x =   -0.449(792) by classical fit to all intensities
  //! Flack x =   -0.315(296) from 3107 selected quotients (Parsons' method)
  QStringList values;
  QString flack = "";
  QString parson = "";
  QStringList linesplit;
  if (line.contains("Flack x") && line.contains("classical fit")
      && (!line.contains("No quotients"))
      && (line.split(" ", QString::SkipEmptyParts).size() >= 3) ) {
    linesplit = line.split(" ", QString::SkipEmptyParts);
    flack = linesplit.at(3);
  }
  if (line.contains("Flack x") && line.contains("Parsons' method")
      && (line.split(" ", QString::SkipEmptyParts).size() >= 3) ) {
    linesplit = line.split(" ", QString::SkipEmptyParts);
    parson = linesplit.at(3);

  }
  values.append(flack);
  values.append(parson);
  return values;
}

QString XLOutputParser::xl_timeval(QString line) {
  //! +  pn_a          finished at 12:33:29   Total elapsed time:      0.00 secs  +
  //! returns the elapsed time of a SHELXL run
  QStringList linesplit;
  QString time = "";
  if (line.contains("finished") && line.contains("time")) {
    linesplit = line.split(" ", QString::SkipEmptyParts);
    time = linesplit.at(linesplit.indexOf("time:")+1);
  }
  return time;
}
