/* utils.vala
 *
 * Copyright 2020 James Westman <james@flyingpimonster.net>
 *
 * 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 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 */

namespace Camera {
    /*
     * Scales the rectangle (w, h) up or down to fit perfectly in (tw, th).
     */
    private static void scale_to_fit(ref double w, ref double h,
                                     double tw, double th) {
        double ratio = w / h;
        double t_ratio = tw / th;
        if (t_ratio < ratio) {
            w = tw;
            h = tw * (1 / ratio);
        } else {
            w = th * ratio;
            h = th;
        }
    }

    /*
     * Scales the rectangle (w, h) to completely fill (tw, th).
     */
    private static void scale_to_fill(ref double w, ref double h,
                                      double tw, double th) {
        double ratio = w / h;
        double t_ratio = tw / th;
        if (t_ratio > ratio) {
            w = tw;
            h = tw * (1 / ratio);
        } else {
            w = th * ratio;
            h = th;
        }
    }

    /*
     * Finds the amount to translate the first rectangle so that it is centered
     * on the second.
     */
    private static void center(double w, double h,
                               double tw, double th,
                               out double out_x, out double out_y) {
        out_x = tw / 2 - w / 2;
        out_y = th / 2 - h / 2;
    }

    /*
     * Draws a rounded square on a Cairo context.
     *
     * It is up to the caller to call fill() or paint(); this just sets up the
     * path.
     *
     * x and y are the coordinates of the center of the square.
     */
    private void rounded_square(Cairo.Context ctx, double size, double radius, double x, double y) {
        x -= size;
        y -= size;
        size *= 2.0;

        // top right
        ctx.arc(x + size - radius, y + radius, radius, -0.5 * Math.PI, 0);
        // bottom right
        ctx.arc(x + size - radius, y + size - radius, radius, 0, 0.5 * Math.PI);
        // bottom left
        ctx.arc(x + radius, y + size - radius, radius, 0.5 * Math.PI, Math.PI);
        // top left
        ctx.arc(x + radius, y + radius, radius, Math.PI, -0.5 * Math.PI);
        // back to top right
        ctx.line_to(x + size - radius, y);
    }

    /**
     * Provides a way to scale and draw a GdkPixbuf in Cairo, while storing a
     * cache of the scaled image.
     */
    private class CachedSurface {
        /**
         * The original, unscaled image
         */
        private Gdk.Pixbuf _pixbuf;
        public Gdk.Pixbuf pixbuf {
            get {
                return _pixbuf;
            }
            set {
                if (value != _pixbuf) {
                    _pixbuf = value;
                    cached = null;
                }
            }
        }

        /**
         * The last scaled image
         */
        private Cairo.ImageSurface cached;


        public CachedSurface.with_pixbuf(Gdk.Pixbuf pixbuf) {
            this.pixbuf = pixbuf;
        }


        public void draw(Cairo.Context cr, int scale_factor, double x, double y, int width, int height)
                requires (this.pixbuf != null) {

            width *= scale_factor;
            height *= scale_factor;

            if (cached == null || width != cached.get_width() || height != cached.get_height()) {
                // create a new, scaled image
                cached = new Cairo.ImageSurface(ARGB32, width, height);

                var cr_scaled = new Cairo.Context(cached);
                cr_scaled.scale(width / (double) pixbuf.width, height / (double) pixbuf.height);
                Gdk.cairo_set_source_pixbuf(cr_scaled, pixbuf, 0, 0);
                cr_scaled.paint();
            }

            cr.save();

            cr.translate(x, y);
            if (scale_factor != 1) {
                cr.scale(1 / (double) scale_factor, 1 / (double) scale_factor);
            }

            cr.set_source_surface(cached, 0, 0);

            cr.paint();
            cr.restore();
        }
    }
}
