#include "paint/stroke_script.h"
#include "test_harness.h"

#include <string>
#include <string_view>

using pp::foundation::StatusCode;
using pp::paint::max_stroke_script_bytes;
using pp::paint::max_stroke_script_line_length;
using pp::paint::parse_stroke_script;

namespace {

void parses_comments_and_multiple_strokes(pp::tests::Harness& h)
{
    constexpr std::string_view script_text =
        "# scripted automation fixture\n"
        "stroke 0 0 0.25 10 0 0.75 2\n"
        "\n"
        "stroke 10 0 1 10 10 0.5 5 # trailing comment\n";

    const auto script = parse_stroke_script(script_text);

    PP_EXPECT(h, script.ok());
    PP_EXPECT(h, script.value().strokes.size() == 2U);
    PP_EXPECT(h, script.value().strokes[0].start.x == 0.0F);
    PP_EXPECT(h, script.value().strokes[0].start.pressure == 0.25F);
    PP_EXPECT(h, script.value().strokes[0].end.x == 10.0F);
    PP_EXPECT(h, script.value().strokes[0].spacing == 2.0F);
    PP_EXPECT(h, script.value().strokes[1].end.y == 10.0F);
    PP_EXPECT(h, script.value().strokes[1].end.pressure == 0.5F);
}

void rejects_malformed_stroke_scripts(pp::tests::Harness& h)
{
    const auto empty = parse_stroke_script("");
    const auto comments_only = parse_stroke_script("# nope\n\n");
    const auto unknown = parse_stroke_script("move 0 0 1 10 0 1 2\n");
    const auto missing_tokens = parse_stroke_script("stroke 0 0 1 10 0 1\n");
    const auto too_many_tokens = parse_stroke_script("stroke 0 0 1 10 0 1 2 extra\n");
    const auto bad_number = parse_stroke_script("stroke 0 0 1 10 nope 1 2\n");
    const auto nan_number = parse_stroke_script("stroke 0 0 1 10 nan 1 2\n");
    const auto zero_spacing = parse_stroke_script("stroke 0 0 1 10 0 1 0\n");

    PP_EXPECT(h, !empty.ok());
    PP_EXPECT(h, empty.status().code == StatusCode::invalid_argument);
    PP_EXPECT(h, !comments_only.ok());
    PP_EXPECT(h, comments_only.status().code == StatusCode::invalid_argument);
    PP_EXPECT(h, !unknown.ok());
    PP_EXPECT(h, unknown.status().code == StatusCode::invalid_argument);
    PP_EXPECT(h, !missing_tokens.ok());
    PP_EXPECT(h, missing_tokens.status().code == StatusCode::invalid_argument);
    PP_EXPECT(h, !too_many_tokens.ok());
    PP_EXPECT(h, too_many_tokens.status().code == StatusCode::invalid_argument);
    PP_EXPECT(h, !bad_number.ok());
    PP_EXPECT(h, bad_number.status().code == StatusCode::invalid_argument);
    PP_EXPECT(h, !nan_number.ok());
    PP_EXPECT(h, nan_number.status().code == StatusCode::invalid_argument);
    PP_EXPECT(h, !zero_spacing.ok());
    PP_EXPECT(h, zero_spacing.status().code == StatusCode::invalid_argument);
}

void rejects_oversized_stroke_scripts(pp::tests::Harness& h)
{
    const std::string oversized_script(max_stroke_script_bytes + 1U, 'x');
    const std::string oversized_line(max_stroke_script_line_length + 1U, 'x');

    const auto too_large_script = parse_stroke_script(oversized_script);
    const auto too_large_line = parse_stroke_script(oversized_line);

    PP_EXPECT(h, !too_large_script.ok());
    PP_EXPECT(h, too_large_script.status().code == StatusCode::out_of_range);
    PP_EXPECT(h, !too_large_line.ok());
    PP_EXPECT(h, too_large_line.status().code == StatusCode::out_of_range);
}

}

int main()
{
    pp::tests::Harness harness;
    harness.run("parses_comments_and_multiple_strokes", parses_comments_and_multiple_strokes);
    harness.run("rejects_malformed_stroke_scripts", rejects_malformed_stroke_scripts);
    harness.run("rejects_oversized_stroke_scripts", rejects_oversized_stroke_scripts);
    return harness.finish();
}
