/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 *
 * Copyright (C) 2022 Aidan MacDonald
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/

#include "rectangle.h"
#include "system.h"

bool rect_contains(const struct rectangle *ra, const struct rectangle *rb)
{
    return ra->x <= rb->x && rb->x + rb->w < ra->x + ra->w &&
           ra->y <= rb->y && rb->y + rb->h < ra->y + ra->h;
}

bool rect_overlap(const struct rectangle *ra, const struct rectangle *rb)
{
    return ra->x + ra->w > rb->x && rb->x + rb->w > ra->x &&
           ra->y + ra->h > rb->y && rb->y + rb->h > ra->y;
}

bool rect_intersect(const struct rectangle *ra, const struct rectangle *rb,
                    struct rectangle *r_out)
{
    if (!rect_valid(ra) || !rect_valid(rb))
        return false;

    int x = MAX(ra->x, rb->x);
    int y = MAX(ra->y, rb->y);
    int w = MIN(ra->x + ra->w, rb->x + rb->w) - x;
    int h = MIN(ra->y + ra->h, rb->y + rb->h) - y;

    r_out->x = x;
    r_out->y = y;
    r_out->w = w;
    r_out->h = h;

    return w > 0 && h > 0;
}

void rect_union(const struct rectangle *ra, const struct rectangle *rb,
                struct rectangle *r_out)
{
    if (!rect_valid(ra)) {
        *r_out = *rb;
    } else if (!rect_valid(rb)) {
        *r_out = *ra;
    } else {
        int x = MIN(ra->x, rb->x);
        int y = MIN(ra->y, rb->y);
        int w = MAX(ra->x + ra->w, rb->x + rb->w) - x;
        int h = MAX(ra->y + ra->h, rb->y + rb->h) - y;

        r_out->x = x;
        r_out->y = y;
        r_out->w = w;
        r_out->h = h;
    }
}

int rect_difference(const struct rectangle *rect,
                    const struct rectangle *rsub,
                    struct rectangle rects_out[4])
{
    if (!rect_valid(rect) || !rect_valid(rsub)) {
        rects_out[0] = *rect;
        return 1;
    }

    int x0 = MAX(rect->x, rsub->x);
    int y0 = MAX(rect->y, rsub->y);
    int x1 = MIN(rect->x + rect->w, rsub->x + rsub->w);
    int y1 = MIN(rect->y + rect->h, rsub->y + rsub->h);

    /* no intersection */
    if (x1 - x0 <= 0 || y1 - y0 <= 0) {
        rects_out[0] = *rect;
        return 1;
    }

    /*  rect
     * +-------------+
     * |   .  2  .   |
     * |   +-----+   |
     * | 0 |rsub | 1 |
     * |   +-----+   |
     * |   .  3  .   |
     * +-------------+
     */

    int n = 0;

    if (rect->x < x0) {
        rects_out[n].x = rect->x;
        rects_out[n].y = rect->y;
        rects_out[n].w = x0 - rect->x;
        rects_out[n].h = rect->h;
        n++;
    }

    if (x1 < rect->x + rect->w) {
        rects_out[n].x = x1;
        rects_out[n].y = rect->y;
        rects_out[n].w = rect->x + rect->w - x1;
        rects_out[n].h = rect->h;
        n++;
    }

    if (rect->y < y0) {
        rects_out[n].x = x0;
        rects_out[n].y = rect->y;
        rects_out[n].w = x1 - x0;
        rects_out[n].h = y0 - rect->y;
        n++;
    }

    if (y1 < rect->y + rect->h) {
        rects_out[n].x = x0;
        rects_out[n].y = y1;
        rects_out[n].w = x1 - x0;
        rects_out[n].h = rect->y + rect->h - y1;
        n++;
    }

    return n;
}
