#include "../test.h"

#define TEST(name) TEST_CASE("parse-mixed-mode: " name, "[parse-mixed-mode]")

TEST("frame + message") {
    EstablishedServerParser p;
    auto bin = gen_message().mask().payload("iframe").str() +
               gen_message().mask().payload("imessage").str();
    auto frange = p.get_frames(bin);
    REQUIRE(frange.begin() != frange.end());
    CHECK_FRAME(*frange.first).payload("iframe");
    auto mrange = p.get_messages();
    CHECK(++frange.first == frange.end()); // previous iterator invalidated
    CHECK_MESSAGE(*mrange.first).nframes(1).payload("imessage");
    CHECK(++mrange.first == mrange.end()); // nothing more
}

TEST("message + frame") {
    EstablishedServerParser p;
    auto bin = gen_message().mask().payload("imessage2").str() +
               gen_message().mask().payload("iframe2").str();
    auto mrange = p.get_messages(bin);
    CHECK_MESSAGE(*mrange.begin()).nframes(1).payload("imessage2");
    auto frange = p.get_frames();
    CHECK(++mrange.first == mrange.end()); // previous iterator invalidated
    CHECK_FRAME(*frange.begin()).payload("iframe2");
    CHECK(++frange.first == frange.end()); // nothing more
}

TEST("1x2 frame + message") {
    EstablishedServerParser p;
    auto bin = gen_message().mask().payload("part1part2").nframes(2).str() +
               gen_message().mask().payload("msg").str();

    SECTION("2 frames first then message") {
        auto frange = p.get_frames(bin);
        auto f1 = *frange.first++;
        auto f2 = *frange.first;
        CHECK_FRAME(f1).payload("part1");
        CHECK_FRAME(f2).opcode(Opcode::CONTINUE).payload("part2");
        auto mrange = p.get_messages();
        auto m = *mrange.first++;
        CHECK_MESSAGE(m).payload("msg");
        CHECK(mrange.first == mrange.second);
    }

    SECTION("1 frame then message") {
        auto frange = p.get_frames(bin);
        CHECK_FRAME(*frange.first).opcode(Opcode::TEXT).payload("part1");
        CHECK_THROWS_AS(p.get_messages(), Error);
    }
}

TEST("1x2 frame + control in the middle + message") {
    EstablishedServerParser p;
    auto bin = gen_frame().opcode(Opcode::TEXT).mask().payload("fpart1").str() +
               gen_frame().opcode(Opcode::PING).mask().final().str() +
               gen_frame().opcode(Opcode::CONTINUE).mask().final().payload("fpart2").str() +
               gen_message().mask().payload("msg").str();

    auto frange = p.get_frames(bin);

    SECTION("3 frames first then message") {
        auto f1 = *frange.first++;
        auto cl = *frange.first++;
        auto f2 = *frange.first;
        CHECK_FRAME(f1).opcode(Opcode::TEXT).payload("fpart1");
        CHECK_FRAME(cl).opcode(Opcode::PING);
        CHECK_FRAME(f2).opcode(Opcode::CONTINUE).payload("fpart2");
        auto mrange = p.get_messages();
        CHECK_MESSAGE(*mrange.first).payload("msg");
    }

    SECTION("1 frame then control then message") {
        auto f1 = *frange.first++;
        auto cl = *frange.first;
        CHECK_FRAME(f1).opcode(Opcode::TEXT).payload("fpart1");
        CHECK_FRAME(cl).opcode(Opcode::PING);
        CHECK_THROWS_AS(p.get_messages(), Error);
    }
}

TEST("1x2 message + frame") {
    EstablishedServerParser p;
    auto bin1 = gen_message().mask().payload("pingpong").nframes(2).vec();
    auto bin2 = gen_message().mask().payload("trololo").str();

    SECTION("message first then frame") {
        auto mrange = p.get_messages(bin1[0]);
        CHECK(mrange.first == mrange.second); // not yet
        mrange = p.get_messages(bin1[1]);
        CHECK_MESSAGE(*mrange.first).payload("pingpong");
        auto frange = p.get_frames();
        CHECK(frange.first == frange.second); // not yet
        frange = p.get_frames(bin2);
        CHECK_FRAME(*frange.first).payload("trololo");
    }

    SECTION("partial message first then frame") {
        auto mrange = p.get_messages(bin1[0]);
        CHECK(mrange.first == mrange.second); // not yet
        CHECK_THROWS_AS(p.get_frames(bin2), Error); // exception when trying to get frames
    }
}

TEST("1x2 message + control in the middle + frame") {
    EstablishedServerParser p;
    auto bin1 = gen_message().mask().payload("pingpong").nframes(2).vec();
    auto cbin = gen_frame().opcode(Opcode::PONG).mask().final().str();
    auto bin2 = gen_message().mask().payload("trololo").str();

    SECTION("message first then frame") {
        auto mrange = p.get_messages(bin1[0]);
        CHECK(mrange.first == mrange.second); // not yet
        mrange = p.get_messages(cbin);
        CHECK_MESSAGE(*mrange.first).opcode(Opcode::PONG);
        mrange = p.get_messages(bin1[1]);
        CHECK_MESSAGE(*mrange.first).payload("pingpong");
        auto frange = p.get_frames();
        CHECK(frange.first == frange.second); // not yet
        frange = p.get_frames(bin2);
        CHECK_FRAME(*frange.first).payload("trololo");
    }

    SECTION("partial message then control then frame") {
        auto mrange = p.get_messages(bin1[0]);
        CHECK(mrange.first == mrange.second); // not yet
        mrange = p.get_messages(cbin);
        CHECK_MESSAGE(*mrange.first).opcode(Opcode::PONG);
        CHECK_THROWS_AS(p.get_frames(bin1[1]), Error);
    }
}