#include "qualitytablesformatter.h"

#include <stdexcept>
#include <set>

#include <casacore/ms/MeasurementSets/MSColumns.h>

#include <casacore/tables/Tables/ArrColDesc.h>
#include <casacore/tables/Tables/ScaColDesc.h>
#include <casacore/tables/Tables/SetupNewTab.h>
#include <casacore/tables/Tables/TableRecord.h>
#include <casacore/tables/Tables/TableUtil.h>

#include <casacore/measures/TableMeasures/TableMeasDesc.h>

#include <casacore/measures/Measures/MEpoch.h>

#include "statisticalvalue.h"

const std::string QualityTablesFormatter::_kindToNameTable[] = {
    "Count",
    "Sum",
    "Mean",
    "RFICount",
    "RFISum",
    "RFIMean",
    "RFIRatio",
    "RFIPercentage",
    "FlaggedCount",
    "FlaggedRatio",
    "SumP2",
    "SumP3",
    "SumP4",
    "Variance",
    "VarianceOfVariance",
    "StandardDeviation",
    "Skewness",
    "Kurtosis",
    "SignalToNoise",
    "DSum",
    "DMean",
    "DSumP2",
    "DSumP3",
    "DSumP4",
    "DVariance",
    "DVarianceOfVariance",
    "DStandardDeviation",
    "DCount",
    "BadSolutionCount",
    "CorrectCount",
    "CorrectedMean",
    "CorrectedSumP2",
    "CorrectedDCount",
    "CorrectedDMean",
    "CorrectedDSumP2",
    "FTSum",
    "FTSumP2"};

const std::string QualityTablesFormatter::_tableToNameTable[] = {
    "QUALITY_KIND_NAME", "QUALITY_TIME_STATISTIC",
    "QUALITY_FREQUENCY_STATISTIC", "QUALITY_BASELINE_STATISTIC",
    "QUALITY_BASELINE_TIME_STATISTIC"};

const enum QualityTablesFormatter::QualityTable
    QualityTablesFormatter::_dimensionToTableTable[] = {
        TimeStatisticTable, FrequencyStatisticTable, BaselineStatisticTable,
        BaselineTimeStatisticTable};

const std::string QualityTablesFormatter::ColumnNameAntenna1 = "ANTENNA1";
const std::string QualityTablesFormatter::ColumnNameAntenna2 = "ANTENNA2";
const std::string QualityTablesFormatter::ColumnNameFrequency = "FREQUENCY";
const std::string QualityTablesFormatter::ColumnNameKind = "KIND";
const std::string QualityTablesFormatter::ColumnNameName = "NAME";
const std::string QualityTablesFormatter::ColumnNameTime = "TIME";
const std::string QualityTablesFormatter::ColumnNameValue = "VALUE";

enum QualityTablesFormatter::StatisticKind QualityTablesFormatter::NameToKind(
    const std::string& kindName) {
  for (unsigned i = 0; i < 37; ++i) {
    if (kindName == _kindToNameTable[i])
      return (QualityTablesFormatter::StatisticKind)i;
  }
  throw std::runtime_error("Statistics kind not known");
}

unsigned QualityTablesFormatter::QueryKindIndex(enum StatisticKind kind) {
  unsigned kindIndex;
  if (!QueryKindIndex(kind, kindIndex))
    throw std::runtime_error(
        "getKindIndex(): Requested statistic kind not available.");
  return kindIndex;
}

bool QualityTablesFormatter::QueryKindIndex(enum StatisticKind kind,
                                            unsigned& destKindIndex) {
  openKindNameTable(false);
  const casacore::ROScalarColumn<int> kindColumn(*_kindNameTable,
                                                 ColumnNameKind);
  const casacore::ROScalarColumn<casacore::String> nameColumn(*_kindNameTable,
                                                              ColumnNameName);
  const casacore::String nameToFind(KindToName(kind));

  const unsigned nrRow = _kindNameTable->nrow();

  for (unsigned i = 0; i < nrRow; ++i) {
    if (nameColumn(i) == nameToFind) {
      destKindIndex = kindColumn(i);
      return true;
    }
  }
  return false;
}

bool QualityTablesFormatter::IsStatisticAvailable(
    enum StatisticDimension dimension, enum StatisticKind kind) {
  const QualityTable table = DimensionToTable(dimension);
  if (!TableExists(KindNameTable) || !TableExists(table)) return false;
  unsigned kindIndex;
  if (!QueryKindIndex(kind, kindIndex)) return false;
  return hasOneEntry(table, kindIndex);
}

void QualityTablesFormatter::InitializeEmptyStatistic(
    enum StatisticDimension dimension, enum StatisticKind kind,
    unsigned polarizationCount) {
  if (!TableExists(KindNameTable)) createKindNameTable();

  const QualityTable table = DimensionToTable(dimension);
  if (!TableExists(table)) {
    InitializeEmptyTable(table, polarizationCount);
  } else {
    removeStatisticFromStatTable(table, kind);
  }
}

void QualityTablesFormatter::InitializeEmptyTable(enum QualityTable table,
                                                  unsigned polarizationCount) {
  if (TableExists(table))
    removeEntries(table);
  else
    createTable(table, polarizationCount);
}

void QualityTablesFormatter::RemoveTable(enum QualityTable table) {
  if (TableExists(table)) {
    Close();
    openMainTable(true);
    if (_measurementSet->keywordSet().isDefined(TableToName(table)))
      _measurementSet->rwKeywordSet().removeField(TableToName(table));
    if (_measurementSet->isReadable(TableToFilename(table)))
      casacore::TableUtil::deleteTable(TableToFilename(table));
  }
}

bool QualityTablesFormatter::hasOneEntry(enum QualityTable table,
                                         unsigned kindIndex) {
  const casacore::Table& casaTable = getTable(table, false);
  const casacore::ROScalarColumn<int> kindColumn(casaTable, ColumnNameKind);

  const unsigned nrRow = casaTable.nrow();

  for (unsigned i = 0; i < nrRow; ++i) {
    if (kindColumn(i) == (int)kindIndex) return true;
  }
  return false;
}

/**
 * Will add an empty table to the measurement set named "QUALITY_KIND_NAME" and
 * initialize its default column.
 * This table can hold a list of quality statistic types that are referred to in
 * the statistic value tables.
 */
void QualityTablesFormatter::createKindNameTable() {
  casacore::TableDesc tableDesc("QUALITY_KIND_NAME_TYPE",
                                QUALITY_TABLES_VERSION_STR,
                                casacore::TableDesc::Scratch);
  tableDesc.comment() =
      "Couples the KIND column in the other quality tables to the name of a "
      "statistic (e.g. Mean)";
  tableDesc.addColumn(casacore::ScalarColumnDesc<int>(
      ColumnNameKind, "Index of the statistic kind"));
  tableDesc.addColumn(casacore::ScalarColumnDesc<casacore::String>(
      ColumnNameName, "Name of the statistic"));

  casacore::SetupNewTable newTableSetup(TableToFilename(KindNameTable),
                                        tableDesc, casacore::Table::New);
  const casacore::Table newTable(newTableSetup);
  openMainTable(true);
  _measurementSet->rwKeywordSet().defineTable(TableToName(KindNameTable),
                                              newTable);
}

/**
 * Add the time column to the table descriptor. Used by create..Table() methods.
 * It holds "Measure"s of time, which is what casacore defines as a value
 * including a unit and a reference frame.
 */
void QualityTablesFormatter::addTimeColumn(casacore::TableDesc& tableDesc) {
  const casacore::ScalarColumnDesc<double> timeDesc(
      ColumnNameTime, "Central time of statistic");
  tableDesc.addColumn(timeDesc);

  const casacore::TableMeasRefDesc measRef(casacore::MEpoch::UTC);
  const casacore::TableMeasValueDesc measVal(tableDesc, ColumnNameTime);
  casacore::TableMeasDesc<casacore::MEpoch> mepochCol(measVal, measRef);
  mepochCol.write(tableDesc);
}

/**
 * Add the frequency column to the table descriptor. Used by create..Table()
 * methods. It holds "Quantum"s of frequency, which is what casacore defines as
 * a value including a unit (Hertz).
 */
void QualityTablesFormatter::addFrequencyColumn(
    casacore::TableDesc& tableDesc) {
  const casacore::ScalarColumnDesc<double> freqDesc(
      ColumnNameFrequency, "Central frequency of statistic bin");
  tableDesc.addColumn(freqDesc);

  const casacore::Unit hertzUnit("Hz");

  casacore::TableQuantumDesc quantDesc(tableDesc, ColumnNameFrequency,
                                       hertzUnit);
  quantDesc.write(tableDesc);
}

/**
 * Add value column to the table descriptor. Used by create..Table() methods.
 * It consist of an array of statistics, each element holds a polarization.
 */
void QualityTablesFormatter::addValueColumn(casacore::TableDesc& tableDesc,
                                            unsigned polarizationCount) {
  casacore::IPosition shape(1);
  shape[0] = polarizationCount;
  const casacore::ArrayColumnDesc<casacore::Complex> valDesc(
      ColumnNameValue, "Value of statistic", shape,
      casacore::ColumnDesc::Direct);
  tableDesc.addColumn(valDesc);
}

/**
 * Will add an empty table to the measurement set named "QUALITY_TIME_STATISTIC"
 * and initialize its default column. This table can hold several statistic
 * kinds per time step.
 * @param polarizationCount specifies the nr polarizations. This is required for
 * the shape of the value column.
 */
void QualityTablesFormatter::createTimeStatisticTable(
    unsigned polarizationCount) {
  casacore::TableDesc tableDesc("QUALITY_TIME_STATISTIC_TYPE",
                                QUALITY_TABLES_VERSION_STR,
                                casacore::TableDesc::Scratch);
  tableDesc.comment() = "Statistics over time";

  addTimeColumn(tableDesc);
  addFrequencyColumn(tableDesc);
  tableDesc.addColumn(casacore::ScalarColumnDesc<int>(
      ColumnNameKind, "Index of the statistic kind"));
  addValueColumn(tableDesc, polarizationCount);

  casacore::SetupNewTable newTableSetup(TableToFilename(TimeStatisticTable),
                                        tableDesc, casacore::Table::New);
  const casacore::Table newTable(newTableSetup);
  openMainTable(true);
  _measurementSet->rwKeywordSet().defineTable(TableToName(TimeStatisticTable),
                                              newTable);
}

/**
 * Will add an empty table to the measurement set named
 * "QUALITY_FREQUENCY_STATISTIC" and initialize its default column. This table
 * can hold several statistic kinds per time step.
 * @param polarizationCount specifies the nr polarizations. This is required for
 * the shape of the value column.
 */
void QualityTablesFormatter::createFrequencyStatisticTable(
    unsigned polarizationCount) {
  casacore::TableDesc tableDesc("QUALITY_FREQUENCY_STATISTIC_TYPE",
                                QUALITY_TABLES_VERSION_STR,
                                casacore::TableDesc::Scratch);
  tableDesc.comment() = "Statistics over frequency";

  addFrequencyColumn(tableDesc);
  tableDesc.addColumn(casacore::ScalarColumnDesc<int>(
      ColumnNameKind, "Index of the statistic kind"));
  addValueColumn(tableDesc, polarizationCount);

  casacore::SetupNewTable newTableSetup(
      TableToFilename(FrequencyStatisticTable), tableDesc,
      casacore::Table::New);
  const casacore::Table newTable(newTableSetup);
  openMainTable(true);
  _measurementSet->rwKeywordSet().defineTable(
      TableToName(FrequencyStatisticTable), newTable);
}

/**
 * Will add an empty table to the measurement set named
 * "QUALITY_BASELINE_STATISTIC" and initialize its default column. This table
 * can hold several statistic kinds per time step.
 * @param polarizationCount specifies the nr polarizations. This is required for
 * the shape of the value column.
 */
void QualityTablesFormatter::createBaselineStatisticTable(
    unsigned polarizationCount) {
  casacore::TableDesc tableDesc("QUALITY_BASELINE_STATISTIC_TYPE",
                                QUALITY_TABLES_VERSION_STR,
                                casacore::TableDesc::Scratch);
  tableDesc.comment() = "Statistics per baseline";

  tableDesc.addColumn(casacore::ScalarColumnDesc<int>(
      ColumnNameAntenna1, "Index of first antenna"));
  tableDesc.addColumn(casacore::ScalarColumnDesc<int>(
      ColumnNameAntenna2, "Index of second antenna"));
  addFrequencyColumn(tableDesc);
  tableDesc.addColumn(casacore::ScalarColumnDesc<int>(
      ColumnNameKind, "Index of the statistic kind"));
  addValueColumn(tableDesc, polarizationCount);

  casacore::SetupNewTable newTableSetup(TableToFilename(BaselineStatisticTable),
                                        tableDesc, casacore::Table::New);
  const casacore::Table newTable(newTableSetup);
  openMainTable(true);
  _measurementSet->rwKeywordSet().defineTable(
      TableToName(BaselineStatisticTable), newTable);
}

void QualityTablesFormatter::createBaselineTimeStatisticTable(
    unsigned polarizationCount) {
  casacore::TableDesc tableDesc("QUALITY_BASELINE_TIME_STATISTIC_TYPE",
                                QUALITY_TABLES_VERSION_STR,
                                casacore::TableDesc::Scratch);
  tableDesc.comment() = "Statistics per baseline";

  addTimeColumn(tableDesc);
  tableDesc.addColumn(casacore::ScalarColumnDesc<int>(
      ColumnNameAntenna1, "Index of first antenna"));
  tableDesc.addColumn(casacore::ScalarColumnDesc<int>(
      ColumnNameAntenna2, "Index of second antenna"));
  addFrequencyColumn(tableDesc);
  tableDesc.addColumn(casacore::ScalarColumnDesc<int>(
      ColumnNameKind, "Index of the statistic kind"));
  addValueColumn(tableDesc, polarizationCount);

  casacore::SetupNewTable newTableSetup(
      TableToFilename(BaselineTimeStatisticTable), tableDesc,
      casacore::Table::New);
  const casacore::Table newTable(newTableSetup);
  openMainTable(true);
  _measurementSet->rwKeywordSet().defineTable(
      TableToName(BaselineTimeStatisticTable), newTable);
}

unsigned QualityTablesFormatter::StoreKindName(const std::string& name) {
  // This should be done atomically, but two quality writers in the same table
  // would be a weird thing to do anyway, plus I don't know how the casa tables
  // can be made atomic (and still have good performance).

  openKindNameTable(true);

  const unsigned kindIndex = findFreeKindIndex(*_kindNameTable);

  const unsigned newRow = _kindNameTable->nrow();
  _kindNameTable->addRow();
  casacore::ScalarColumn<int> kindColumn(*_kindNameTable, ColumnNameKind);
  casacore::ScalarColumn<casacore::String> nameColumn(*_kindNameTable,
                                                      ColumnNameName);
  kindColumn.put(newRow, kindIndex);
  nameColumn.put(newRow, name);
  return kindIndex;
}

unsigned QualityTablesFormatter::findFreeKindIndex(casacore::Table& kindTable) {
  int maxIndex = 0;

  const casacore::ROScalarColumn<int> kindColumn(kindTable, ColumnNameKind);

  const unsigned nrRow = kindTable.nrow();

  for (unsigned i = 0; i < nrRow; ++i) {
    if (kindColumn(i) > maxIndex) maxIndex = kindColumn(i);
  }
  return maxIndex + 1;
}

void QualityTablesFormatter::openTable(
    QualityTable table, bool needWrite,
    std::unique_ptr<casacore::Table>& tablePtr) {
  if (tablePtr == nullptr) {
    openMainTable(false);
    tablePtr.reset(new casacore::Table(
        _measurementSet->keywordSet().asTable(TableToName(table))));
    if (needWrite) tablePtr->reopenRW();
  } else {
    if (needWrite && !tablePtr->isWritable()) tablePtr->reopenRW();
  }
}

void QualityTablesFormatter::StoreTimeValue(double time, double frequency,
                                            const StatisticalValue& value) {
  openTimeTable(true);

  const unsigned newRow = _timeTable->nrow();
  _timeTable->addRow();

  // TODO maybe the columns need to be cached to avoid having to look them up
  // for each store...
  casacore::ScalarColumn<double> timeColumn(*_timeTable, ColumnNameTime);
  casacore::ScalarColumn<double> frequencyColumn(*_timeTable,
                                                 ColumnNameFrequency);
  casacore::ScalarColumn<int> kindColumn(*_timeTable, ColumnNameKind);
  casacore::ArrayColumn<casacore::Complex> valueColumn(*_timeTable,
                                                       ColumnNameValue);

  timeColumn.put(newRow, time);
  frequencyColumn.put(newRow, frequency);
  kindColumn.put(newRow, value.KindIndex());
  casacore::Vector<casacore::Complex> data(value.PolarizationCount());
  for (unsigned i = 0; i < value.PolarizationCount(); ++i)
    data[i] = value.Value(i);
  valueColumn.put(newRow, data);
}

void QualityTablesFormatter::StoreFrequencyValue(
    double frequency, const StatisticalValue& value) {
  openFrequencyTable(true);

  const unsigned newRow = _frequencyTable->nrow();
  _frequencyTable->addRow();

  casacore::ScalarColumn<double> frequencyColumn(*_frequencyTable,
                                                 ColumnNameFrequency);
  casacore::ScalarColumn<int> kindColumn(*_frequencyTable, ColumnNameKind);
  casacore::ArrayColumn<casacore::Complex> valueColumn(*_frequencyTable,
                                                       ColumnNameValue);

  frequencyColumn.put(newRow, frequency);
  kindColumn.put(newRow, value.KindIndex());
  casacore::Vector<casacore::Complex> data(value.PolarizationCount());
  for (unsigned i = 0; i < value.PolarizationCount(); ++i)
    data[i] = value.Value(i);
  valueColumn.put(newRow, data);
}

void QualityTablesFormatter::StoreBaselineValue(unsigned antenna1,
                                                unsigned antenna2,
                                                double frequency,
                                                const StatisticalValue& value) {
  openBaselineTable(true);

  const unsigned newRow = _baselineTable->nrow();
  _baselineTable->addRow();

  casacore::ScalarColumn<int> antenna1Column(*_baselineTable,
                                             ColumnNameAntenna1);
  casacore::ScalarColumn<int> antenna2Column(*_baselineTable,
                                             ColumnNameAntenna2);
  casacore::ScalarColumn<double> frequencyColumn(*_baselineTable,
                                                 ColumnNameFrequency);
  casacore::ScalarColumn<int> kindColumn(*_baselineTable, ColumnNameKind);
  casacore::ArrayColumn<casacore::Complex> valueColumn(*_baselineTable,
                                                       ColumnNameValue);

  antenna1Column.put(newRow, antenna1);
  antenna2Column.put(newRow, antenna2);
  frequencyColumn.put(newRow, frequency);
  kindColumn.put(newRow, value.KindIndex());
  casacore::Vector<casacore::Complex> data(value.PolarizationCount());
  for (unsigned i = 0; i < value.PolarizationCount(); ++i)
    data[i] = value.Value(i);
  valueColumn.put(newRow, data);
}

void QualityTablesFormatter::StoreBaselineTimeValue(
    unsigned antenna1, unsigned antenna2, double time, double frequency,
    const StatisticalValue& value) {
  openBaselineTimeTable(true);

  const unsigned newRow = _baselineTimeTable->nrow();
  _baselineTimeTable->addRow();

  casacore::ScalarColumn<double> timeColumn(*_baselineTimeTable,
                                            ColumnNameTime);
  casacore::ScalarColumn<int> antenna1Column(*_baselineTimeTable,
                                             ColumnNameAntenna1);
  casacore::ScalarColumn<int> antenna2Column(*_baselineTimeTable,
                                             ColumnNameAntenna2);
  casacore::ScalarColumn<double> frequencyColumn(*_baselineTimeTable,
                                                 ColumnNameFrequency);
  casacore::ScalarColumn<int> kindColumn(*_baselineTimeTable, ColumnNameKind);
  casacore::ArrayColumn<casacore::Complex> valueColumn(*_baselineTimeTable,
                                                       ColumnNameValue);

  timeColumn.put(newRow, time);
  antenna1Column.put(newRow, antenna1);
  antenna2Column.put(newRow, antenna2);
  frequencyColumn.put(newRow, frequency);
  kindColumn.put(newRow, value.KindIndex());
  casacore::Vector<casacore::Complex> data(value.PolarizationCount());
  for (unsigned i = 0; i < value.PolarizationCount(); ++i)
    data[i] = value.Value(i);
  valueColumn.put(newRow, data);
}

void QualityTablesFormatter::removeStatisticFromStatTable(
    enum QualityTable qualityTable, enum StatisticKind kind) {
  unsigned kindIndex;
  if (QueryKindIndex(kind, kindIndex)) {
    casacore::Table& table = getTable(qualityTable, true);
    const casacore::ScalarColumn<int> kindColumn(table, ColumnNameKind);

    unsigned nrRow = table.nrow();

    for (unsigned i = 0; i < nrRow; ++i) {
      while (i < nrRow && kindColumn(i) == (int)kindIndex) {
        table.removeRow(i);
        --nrRow;
      }
    }
  }
}

void QualityTablesFormatter::removeKindNameEntry(enum StatisticKind kind) {
  openKindNameTable(true);
  const casacore::ScalarColumn<casacore::String> nameColumn(*_kindNameTable,
                                                            ColumnNameName);

  const unsigned nrRow = _kindNameTable->nrow();
  const casacore::String kindName(KindToName(kind));

  for (unsigned i = 0; i < nrRow; ++i) {
    if (nameColumn(i) == kindName) {
      _kindNameTable->removeRow(i);
      break;
    }
  }
}

void QualityTablesFormatter::removeEntries(enum QualityTable table) {
  casacore::Table& casaTable = getTable(table, true);
  const unsigned nrRow = casaTable.nrow();
  for (int i = nrRow - 1; i >= 0; --i) {
    casaTable.removeRow(i);
  }
}

unsigned QualityTablesFormatter::QueryStatisticEntryCount(
    enum StatisticDimension dimension, unsigned kindIndex) {
  const casacore::Table& casaTable(
      getTable(DimensionToTable(dimension), false));
  const casacore::ROScalarColumn<int> kindColumn(casaTable, ColumnNameKind);

  const unsigned nrRow = casaTable.nrow();
  size_t count = 0;

  for (unsigned i = 0; i < nrRow; ++i) {
    if (kindColumn(i) == (int)kindIndex) ++count;
  }
  return count;
}

unsigned QualityTablesFormatter::GetPolarizationCount() {
  const casacore::Table& table(getTable(TimeStatisticTable, false));
  const casacore::ROArrayColumn<casacore::Complex> valueColumn(table,
                                                               ColumnNameValue);
  return valueColumn.columnDesc().shape()[0];
}

void QualityTablesFormatter::QueryTimeStatistic(
    unsigned kindIndex,
    std::vector<std::pair<TimePosition, StatisticalValue>>& entries) {
  const casacore::Table& table(getTable(TimeStatisticTable, false));
  const unsigned nrRow = table.nrow();

  const casacore::ROScalarColumn<double> timeColumn(table, ColumnNameTime);
  const casacore::ROScalarColumn<double> frequencyColumn(table,
                                                         ColumnNameFrequency);
  const casacore::ROScalarColumn<int> kindColumn(table, ColumnNameKind);
  const casacore::ROArrayColumn<casacore::Complex> valueColumn(table,
                                                               ColumnNameValue);

  const int polarizationCount = valueColumn.columnDesc().shape()[0];

  for (unsigned i = 0; i < nrRow; ++i) {
    if (kindColumn(i) == (int)kindIndex) {
      StatisticalValue value(polarizationCount);
      value.SetKindIndex(kindIndex);
      casacore::Array<casacore::Complex> valueArray = valueColumn(i);
      casacore::Array<casacore::Complex>::iterator iter = valueArray.begin();
      for (int p = 0; p < polarizationCount; ++p) {
        value.SetValue(p, *iter);
        ++iter;
      }
      TimePosition position;
      position.time = timeColumn(i);
      position.frequency = frequencyColumn(i);
      entries.push_back(
          std::pair<TimePosition, StatisticalValue>(position, value));
    }
  }
}

void QualityTablesFormatter::QueryFrequencyStatistic(
    unsigned kindIndex,
    std::vector<std::pair<FrequencyPosition, StatisticalValue>>& entries) {
  const casacore::Table& table(getTable(FrequencyStatisticTable, false));
  const unsigned nrRow = table.nrow();

  const casacore::ROScalarColumn<double> frequencyColumn(table,
                                                         ColumnNameFrequency);
  const casacore::ROScalarColumn<int> kindColumn(table, ColumnNameKind);
  const casacore::ROArrayColumn<casacore::Complex> valueColumn(table,
                                                               ColumnNameValue);

  const int polarizationCount = valueColumn.columnDesc().shape()[0];

  for (unsigned i = 0; i < nrRow; ++i) {
    if (kindColumn(i) == (int)kindIndex) {
      StatisticalValue value(polarizationCount);
      value.SetKindIndex(kindIndex);
      casacore::Array<casacore::Complex> valueArray = valueColumn(i);
      casacore::Array<casacore::Complex>::iterator iter = valueArray.begin();
      for (int p = 0; p < polarizationCount; ++p) {
        value.SetValue(p, *iter);
        ++iter;
      }
      FrequencyPosition position;
      position.frequency = frequencyColumn(i);
      entries.push_back(
          std::pair<FrequencyPosition, StatisticalValue>(position, value));
    }
  }
}

void QualityTablesFormatter::QueryBaselineStatistic(
    unsigned kindIndex,
    std::vector<std::pair<BaselinePosition, StatisticalValue>>& entries) {
  const casacore::Table& table(getTable(BaselineStatisticTable, false));
  const unsigned nrRow = table.nrow();

  const casacore::ROScalarColumn<int> antenna1Column(table, ColumnNameAntenna1);
  const casacore::ROScalarColumn<int> antenna2Column(table, ColumnNameAntenna2);
  const casacore::ROScalarColumn<double> frequencyColumn(table,
                                                         ColumnNameFrequency);
  const casacore::ROScalarColumn<int> kindColumn(table, ColumnNameKind);
  const casacore::ROArrayColumn<casacore::Complex> valueColumn(table,
                                                               ColumnNameValue);

  const int polarizationCount = valueColumn.columnDesc().shape()[0];

  for (unsigned i = 0; i < nrRow; ++i) {
    if (kindColumn(i) == (int)kindIndex) {
      StatisticalValue value(polarizationCount);
      value.SetKindIndex(kindIndex);
      casacore::Array<casacore::Complex> valueArray = valueColumn(i);
      casacore::Array<casacore::Complex>::iterator iter = valueArray.begin();
      for (int p = 0; p < polarizationCount; ++p) {
        value.SetValue(p, *iter);
        ++iter;
      }
      BaselinePosition position;
      position.antenna1 = antenna1Column(i);
      position.antenna2 = antenna2Column(i);
      position.frequency = frequencyColumn(i);
      entries.push_back(
          std::pair<BaselinePosition, StatisticalValue>(position, value));
    }
  }
}

void QualityTablesFormatter::openMainTable(bool needWrite) {
  if (_measurementSet == nullptr) {
    if (needWrite)
      _measurementSet.reset(
          new casacore::Table(_measurementSetName, casacore::Table::Update));
    else
      _measurementSet.reset(new casacore::Table(_measurementSetName));
  } else if (needWrite) {
    if (!_measurementSet->isWritable()) _measurementSet->reopenRW();
  }
}
