class ViewportResultsJsonCsvWriter {

  public String getJson(ViewportResults viewportResults) {
    List<ResultsCell> viewportCells = viewportResults.getResults();
    List<Object> results = Lists.newArrayListWithCapacity(viewportCells.size());
    for (ResultsCell cell : viewportCells) {
      Object cellValue = cell.getValue();
      ValueSpecification cellValueSpec = cell.getValueSpecification();
      Object formattedValue = _formatter.format(cellValue, cellValueSpec, cell.getFormat(), cell.getInlineKey());
      Collection<Object> history = cell.getHistory();
      Class<?> columnType = cell.getType();
      DataType columnFormat = _formatter.getDataType(columnType);
      Map<String, Object> valueMap = Maps.newHashMap();
      AggregatedExecutionLog executionLog = cell.getExecutionLog();
      LogLevel logLevel = maxLogLevel(executionLog);

      valueMap.put(VALUE, formattedValue);
      if (columnFormat == UNKNOWN) {
        // if the the column type isn't known then send the type with the value
        valueMap.put(TYPE, _formatter.getDataTypeForValue(cellValue, cellValueSpec).name());
      }
      if (history != null) {
        List<Object> formattedHistoy = formatHistory(cellValueSpec, cell.getInlineKey(), history);
        if (formattedHistoy != null) {
          valueMap.put(HISTORY, formattedHistoy);
        }
      }
      if (cell.isError() || isError(formattedValue)) {
        valueMap.put(ERROR, true);
      }
      if (logLevel != null) {
        valueMap.put(LOG_LEVEL, logLevel);
      }
      if (hasLogOutput(executionLog)) {
        valueMap.put(LOG_OUTPUT, formatLogOutput(executionLog));
      }
      results.add(valueMap);
    }
    String duration = _durationFormatter.format(new BigDecimal(viewportResults.getCalculationDuration().toMillis()));
    ImmutableMap<String, Object> resultsMap = ImmutableMap.of(VERSION, viewportResults.getVersion(),
                                                              CALCULATION_DURATION, duration,
                                                              DATA, results);
    return new JSONObject(resultsMap).toString();
  }

  public  ViewportResultsJsonCsvWriter(ResultsFormatter formatter);

}