/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include "orcus/orcus_ods.hpp"
#include "orcus/pstring.hpp"
#include "orcus/global.hpp"
#include "orcus/stream.hpp"
#include "orcus/spreadsheet/factory.hpp"
#include "orcus/spreadsheet/document.hpp"
#include "orcus/spreadsheet/sheet.hpp"
#include "orcus/spreadsheet/shared_strings.hpp"
#include "orcus/spreadsheet/styles.hpp"

#include <cstdlib>
#include <cassert>
#include <string>
#include <iostream>
#include <sstream>
#include <vector>

#include <mdds/flat_segment_tree.hpp>

using namespace orcus;
using namespace orcus::spreadsheet;
using namespace std;

typedef mdds::flat_segment_tree<size_t,bool> bool_segment_type;

namespace {

std::vector<const char*> dirs = {
    SRCDIR"/test/ods/raw-values-1/",
    SRCDIR"/test/ods/formula-1/",
    SRCDIR"/test/ods/formula-2/",
};

void test_ods_import_cell_values()
{
    for (const char* dir : dirs)
    {
        string path(dir);
        cout << path << endl;

        // Read the input.ods document.
        path.append("input.ods");
        spreadsheet::document doc;
        spreadsheet::import_factory factory(doc);
        orcus_ods app(&factory);
        app.read_file(path.c_str());

        // Dump the content of the model.
        ostringstream os;
        doc.dump_check(os);
        string check = os.str();

        // Check that against known control.
        path = dir;
        path.append("check.txt");
        file_content control(path.data());

        assert(!check.empty());
        assert(!control.empty());

        pstring s1(&check[0], check.size());
        pstring s2 = control.str();
        assert(s1.trim() == s2.trim());
    }
}

void test_ods_import_column_widths_row_heights()
{
    const char* filepath = SRCDIR"/test/ods/column-width-row-height/input.ods";
    document doc;
    import_factory factory(doc);
    orcus_ods app(&factory);
    app.read_file(filepath);

    assert(doc.sheet_size() > 0);
    spreadsheet::sheet* sh = doc.get_sheet(0);
    assert(sh);

    // Column widths are in twips.
    col_width_t cw = sh->get_col_width(1, nullptr, nullptr);
    assert(cw == 1440); // 1 in
    cw = sh->get_col_width(2, nullptr, nullptr);
    assert(cw == 2160); // 1.5 in
    cw = sh->get_col_width(3, nullptr, nullptr);
    assert(cw == 2592); // 1.8 in

    // Row heights are in twips too.
    row_height_t rh = sh->get_row_height(3, nullptr, nullptr);
    assert(rh == 720); // 0.5 in
    rh = sh->get_row_height(4, nullptr, nullptr);
    assert(rh == 1440); // 1 in
    rh = sh->get_row_height(5, nullptr, nullptr);
    assert(rh == 2160); // 1.5 in
    rh = sh->get_row_height(6, nullptr, nullptr);
    assert(rh == 2592); // 1.8 in
}

void test_ods_import_formatted_text()
{
    const char* filepath = SRCDIR"/test/ods/formatted-text/bold-and-italic.ods";
    document doc;
    import_factory factory(doc);
    orcus_ods app(&factory);
    app.read_file(filepath);

    assert(doc.sheet_size() > 0);
    spreadsheet::sheet* sh = doc.get_sheet(0);
    assert(sh);

    const import_shared_strings* ss = doc.get_shared_strings();
    assert(ss);

    const styles& styles = doc.get_styles();

    // A1 is unformatted
    size_t str_id = sh->get_string_identifier(0,0);
    const string* str = ss->get_string(str_id);
    assert(str && *str == "Normal Text");
    size_t xfid = sh->get_cell_format(0,0);
    assert(xfid == 0); // ID of 0 represents default format.
    const format_runs_t* fmt = ss->get_format_runs(str_id);
    assert(!fmt); // The string should be unformatted.

    // A2 is all bold via cell format.
    str_id = sh->get_string_identifier(1,0);
    str = ss->get_string(str_id);
    assert(str && *str == "Bold Text");
    xfid = sh->get_cell_format(1,0);
    const cell_format_t* xf = styles.get_cell_format(xfid);
    assert(xf);
    const font_t* font_data = styles.get_font(xf->font);
    assert(font_data && font_data->bold && !font_data->italic);
    fmt = ss->get_format_runs(str_id);
    assert(!fmt); // This string should be unformatted.

    // A3 is all italic.
    str_id = sh->get_string_identifier(2,0);
    str = ss->get_string(str_id);
    assert(str && *str == "Italic Text");
    xfid = sh->get_cell_format(2,0);
    xf = styles.get_cell_format(xfid);
    assert(xf);
    font_data = styles.get_font(xf->font);
    assert(font_data && !font_data->bold && font_data->italic);
    fmt = ss->get_format_runs(str_id);
    assert(!fmt); // This string should be unformatted.

    // A4 is all bolid and italic.
    str_id = sh->get_string_identifier(3,0);
    str = ss->get_string(str_id);
    assert(str && *str == "Bold and Italic Text");
    xfid = sh->get_cell_format(3,0);
    xf = styles.get_cell_format(xfid);
    assert(xf);
    font_data = styles.get_font(xf->font);
    assert(font_data && font_data->bold && font_data->italic);
    fmt = ss->get_format_runs(str_id);
    assert(!fmt); // This string should be unformatted.

    // A5 has mixed format runs.
    str_id = sh->get_string_identifier(4,0);
    str = ss->get_string(str_id);
    assert(str && *str == "Bold and Italic mixed");
    xfid = sh->get_cell_format(4,0);
    xf = styles.get_cell_format(xfid);
    assert(xf);
    font_data = styles.get_font(xf->font);
    fmt = ss->get_format_runs(str_id);
    assert(fmt); // This string should be formatted.

    {
        // Check the bold format segment.
        bool_segment_type bold_runs(0, str->size(), font_data->bold);
        for (size_t i = 0, n = fmt->size(); i < n; ++i)
        {
            format_run run = fmt->at(i);
            bold_runs.insert_back(run.pos, run.pos+run.size, run.bold);
        }

        bold_runs.build_tree();
        bool is_bold = false;
        size_t start_pos, end_pos;

        // The first four letters 'Bold' should be bold.
        bool good = bold_runs.search_tree(0, is_bold, &start_pos, &end_pos).second;
        assert(good);
        assert(is_bold);
        assert(start_pos == 0);
        assert(end_pos == 4);

        // The rest should be non-bold.
        good = bold_runs.search_tree(4, is_bold, &start_pos, &end_pos).second;
        assert(good);
        assert(!is_bold);
        assert(start_pos == 4);
        assert(end_pos == str->size());
    }

    {
        // Check the italic format segment.
        bool_segment_type italic_runs(0, str->size(), font_data->italic);
        for (size_t i = 0, n = fmt->size(); i < n; ++i)
        {
            format_run run = fmt->at(i);
            italic_runs.insert_back(run.pos, run.pos+run.size, run.italic);
        }

        italic_runs.build_tree();
        bool it_italic = false;
        size_t start_pos, end_pos;

        // The first 9 letters 'Bold and ' should not be italic.
        bool good = italic_runs.search_tree(0, it_italic, &start_pos, &end_pos).second;
        assert(good);
        assert(!it_italic);
        assert(start_pos == 0);
        assert(end_pos == 9);

        // The next 6 letters 'Italic' should be italic.
        good = italic_runs.search_tree(9, it_italic, &start_pos, &end_pos).second;
        assert(good);
        assert(it_italic);
        assert(start_pos == 9);
        assert(end_pos == 15);

        // The rest should be non-italic.
        good = italic_runs.search_tree(15, it_italic, &start_pos, &end_pos).second;
        assert(good);
        assert(!it_italic);
        assert(start_pos == 15);
        assert(end_pos == str->size());
    }
}

}

int main()
{
    test_ods_import_cell_values();
    test_ods_import_column_widths_row_heights();
    test_ods_import_formatted_text();
    return EXIT_SUCCESS;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
