/* ====================================================================
   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements.  See the NOTICE file distributed with
   this work for additional information regarding copyright ownership.
   The ASF licenses this file to You under the Apache License, Version 2.0
   (the "License"); you may not use this file except in compliance with
   the License.  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
==================================================================== */

package org.apache.poi.ss.util;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;

import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.util.NumberComparisonExamples.ComparisonExample;
import org.apache.poi.util.HexDump;

/**
 * Creates a spreadsheet that checks Excel's comparison of various IEEE double values.
 * The class {@link NumberComparisonExamples} contains specific comparison examples
 * (along with their expected results) that get encoded into rows of the spreadsheet.
 * Each example is checked with a formula (in column I) that displays either "OK" or
 * "ERROR" depending on whether actual results match those expected.
 */
public class NumberComparingSpreadsheetGenerator {

    private static final class SheetWriter {

        private final HSSFSheet _sheet;
        private int _rowIndex;

        public SheetWriter(HSSFWorkbook wb) {
            HSSFSheet sheet = wb.createSheet("Sheet1");

            writeHeaderRow(wb, sheet);
            _sheet = sheet;
            _rowIndex = 1;
        }

        void addTestRow(double a, double b, int expResult) {
            writeDataRow(_sheet, _rowIndex++, a, b, expResult);
        }
    }

    private static void writeHeaderCell(HSSFRow row, int i, String text, HSSFCellStyle style) {
        HSSFCell cell = row.createCell(i);
        cell.setCellValue(new HSSFRichTextString(text));
        cell.setCellStyle(style);
    }
    static void writeHeaderRow(HSSFWorkbook wb, HSSFSheet sheet) {
        sheet.setColumnWidth(0, 6000);
        sheet.setColumnWidth(1, 6000);
        sheet.setColumnWidth(2, 3600);
        sheet.setColumnWidth(3, 3600);
        sheet.setColumnWidth(4, 2400);
        sheet.setColumnWidth(5, 2400);
        sheet.setColumnWidth(6, 2400);
        sheet.setColumnWidth(7, 2400);
        sheet.setColumnWidth(8, 2400);
        HSSFRow row = sheet.createRow(0);
        HSSFCellStyle style = wb.createCellStyle();
        HSSFFont font = wb.createFont();
        font.setBold(true);
        style.setFont(font);
        writeHeaderCell(row, 0, "Raw Long Bits A", style);
        writeHeaderCell(row, 1, "Raw Long Bits B", style);
        writeHeaderCell(row, 2, "Value A", style);
        writeHeaderCell(row, 3, "Value B", style);
        writeHeaderCell(row, 4, "Exp Cmp", style);
        writeHeaderCell(row, 5, "LT", style);
        writeHeaderCell(row, 6, "EQ", style);
        writeHeaderCell(row, 7, "GT", style);
        writeHeaderCell(row, 8, "Check", style);
    }
    /**
     * Fills a spreadsheet row with one comparison example. The two numeric values are written to
     * columns C and D. Columns (F, G and H) respectively get formulas ("v0<v1", "v0=v1", "v0>v1"),
     * which will be evaluated by Excel. Column D gets the expected comparison result. Column I
     * gets a formula to check that Excel's comparison results match that predicted in column D.
     *
     * @param v0 the first value to be compared
     * @param v1 the second value to be compared
     * @param expRes expected comparison result (-1, 0, or +1)
     */
    static void writeDataRow(HSSFSheet sheet, int rowIx, double v0, double v1, int expRes) {
        HSSFRow row = sheet.createRow(rowIx);

        int rowNum = rowIx + 1;


        row.createCell(0).setCellValue(formatDoubleAsHex(v0));
        row.createCell(1).setCellValue(formatDoubleAsHex(v1));
        row.createCell(2).setCellValue(v0);
        row.createCell(3).setCellValue(v1);
        row.createCell(4).setCellValue(expRes < 0 ? "LT" : expRes > 0 ? "GT" : "EQ");
        row.createCell(5).setCellFormula("C" + rowNum + "<" + "D" + rowNum);
        row.createCell(6).setCellFormula("C" + rowNum + "=" + "D" + rowNum);
        row.createCell(7).setCellFormula("C" + rowNum + ">" + "D" + rowNum);
        // TODO - bug elsewhere in POI - something wrong with encoding of NOT() function
        String frm = "if(or(" +
            "and(E#='LT', F#      , G#=FALSE, H#=FALSE)," +
            "and(E#='EQ', F#=FALSE, G#      , H#=FALSE)," +
            "and(E#='GT', F#=FALSE, G#=FALSE, H#      )" +
            "), 'OK', 'error')"    ;
        row.createCell(8).setCellFormula(frm.replaceAll("#", String.valueOf(rowNum)).replace('\'', '"'));
    }

    private static String formatDoubleAsHex(double d) {
        long l = Double.doubleToLongBits(d);
        return HexDump.longToHex(l)+'L';
    }

    public static void main(String[] args) throws IOException {
        try (HSSFWorkbook wb = new HSSFWorkbook()) {
            SheetWriter sw = new SheetWriter(wb);
            ComparisonExample[] ces = NumberComparisonExamples.getComparisonExamples();
            for (ComparisonExample ce : ces) {
                sw.addTestRow(ce.getA(), ce.getB(), ce.getExpectedResult());
            }

            File outputFile = new File("ExcelNumberCompare.xls");
            try (OutputStream os = Files.newOutputStream(outputFile.toPath())) {
                wb.write(os);
            }
            System.out.println("Finished writing '" + outputFile.getAbsolutePath() + "'");
        }
    }
}
