/*
 * Copyright 2022 Rive
 */

#include "rive/math/math_types.hpp"
#include "rive/math/raw_path_utils.hpp"
#include <cmath>

// Extract subsets

void rive::quad_subdivide(const rive::Vec2D src[3], float t, rive::Vec2D dst[5])
{
    assert(t >= 0 && t <= 1);
    auto ab = lerp(src[0], src[1], t);
    auto bc = lerp(src[1], src[2], t);
    dst[0] = src[0];
    dst[1] = ab;
    dst[2] = lerp(ab, bc, t);
    dst[3] = bc;
    dst[4] = src[2];
}

void rive::cubic_subdivide(const rive::Vec2D src[4],
                           float t,
                           rive::Vec2D dst[7])
{
    assert(t >= 0 && t <= 1);
    auto ab = lerp(src[0], src[1], t);
    auto bc = lerp(src[1], src[2], t);
    auto cd = lerp(src[2], src[3], t);
    auto abc = lerp(ab, bc, t);
    auto bcd = lerp(bc, cd, t);
    dst[0] = src[0];
    dst[1] = ab;
    dst[2] = abc;
    dst[3] = lerp(abc, bcd, t);
    dst[4] = bcd;
    dst[5] = cd;
    dst[6] = src[3];
}

void rive::line_extract(const rive::Vec2D src[2],
                        float startT,
                        float endT,
                        rive::Vec2D dst[2])
{
    assert(startT <= endT);
    assert(startT >= 0 && endT <= 1);

    dst[0] = lerp(src[0], src[1], startT);
    dst[1] = lerp(src[0], src[1], endT);
}

void rive::quad_extract(const rive::Vec2D src[3],
                        float startT,
                        float endT,
                        rive::Vec2D dst[3])
{
    assert(startT <= endT);
    assert(startT >= 0 && endT <= 1);

    rive::Vec2D tmp[5];
    if (startT == 0 && endT == 1)
    {
        std::copy(src, src + 3, dst);
    }
    else if (startT == 0)
    {
        rive::quad_subdivide(src, endT, tmp);
        std::copy(tmp, tmp + 3, dst);
    }
    else if (endT == 1)
    {
        rive::quad_subdivide(src, startT, tmp);
        std::copy(tmp + 2, tmp + 5, dst);
    }
    else
    {
        assert(endT > 0);
        rive::quad_subdivide(src, endT, tmp);
        rive::Vec2D tmp2[5];
        rive::quad_subdivide(tmp, startT / endT, tmp2);
        std::copy(tmp2 + 2, tmp2 + 5, dst);
    }
}

void rive::cubic_extract(const rive::Vec2D src[4],
                         float startT,
                         float endT,
                         rive::Vec2D dst[4])
{
    assert(startT <= endT);
    assert(startT >= 0 && endT <= 1);

    rive::Vec2D tmp[7];
    if (startT == 0 && endT == 1)
    {
        std::copy(src, src + 4, dst);
    }
    else if (startT == 0)
    {
        rive::cubic_subdivide(src, endT, tmp);
        std::copy(tmp, tmp + 4, dst);
    }
    else if (endT == 1)
    {
        rive::cubic_subdivide(src, startT, tmp);
        std::copy(tmp + 3, tmp + 7, dst);
    }
    else
    {
        assert(endT > 0);
        rive::cubic_subdivide(src, endT, tmp);
        rive::Vec2D tmp2[7];
        rive::cubic_subdivide(tmp, startT / endT, tmp2);
        std::copy(tmp2 + 3, tmp2 + 7, dst);
    }
}
