/* $OpenBSD$ */

/*
* Copyright (c) 2007 Nicholas Marriott <[email protected]>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include <sys/types.h>

#include <stdlib.h>
#include <string.h>

#include "tmux.h"

static struct images    all_images = TAILQ_HEAD_INITIALIZER(all_images);
static u_int            all_images_count;

static void
image_free(struct image *im)
{
       struct screen   *s = im->s;

       TAILQ_REMOVE(&all_images, im, all_entry);
       all_images_count--;

       TAILQ_REMOVE(&s->images, im, entry);
       sixel_free(im->data);
       free(im->fallback);
       free(im);
}

int
image_free_all(struct screen *s)
{
       struct image    *im, *im1;
       int              redraw = !TAILQ_EMPTY(&s->images);

       TAILQ_FOREACH_SAFE(im, &s->images, entry, im1)
               image_free(im);
       return (redraw);
}

/* Create text placeholder for an image. */
static void
image_fallback(char **ret, u_int sx, u_int sy)
{
       char    *buf, *label;
       u_int    py, size, lsize;

       /* Allocate first line. */
       lsize = xasprintf(&label, "SIXEL IMAGE (%ux%u)\r\n", sx, sy) + 1;
       if (sx < lsize - 3)
               size = lsize - 1;
       else
               size = sx + 2;

       /* Remaining lines. Every placeholder line has \r\n at the end. */
       size += (sx + 2) * (sy - 1) + 1;
       *ret = buf = xmalloc(size);

       /* Render first line. */
       if (sx < lsize - 3) {
               memcpy(buf, label, lsize);
               buf += lsize - 1;
       } else {
               memcpy(buf, label, lsize - 3);
               buf += lsize - 3;
               memset(buf, '+', sx - lsize + 3);
               buf += sx - lsize + 3;
               snprintf(buf, 3, "\r\n");
               buf += 2;
       }

       /* Remaining lines. */
       for (py = 1; py < sy; py++) {
               memset(buf, '+', sx);
               buf += sx;
               snprintf(buf, 3, "\r\n");
               buf += 2;
       }

       free(label);
}

struct image*
image_store(struct screen *s, struct sixel_image *si)
{
       struct image    *im;

       im = xcalloc(1, sizeof *im);
       im->s = s;
       im->data = si;

       im->px = s->cx;
       im->py = s->cy;
       sixel_size_in_cells(si, &im->sx, &im->sy);

       image_fallback(&im->fallback, im->sx, im->sy);

       TAILQ_INSERT_TAIL(&s->images, im, entry);

       TAILQ_INSERT_TAIL(&all_images, im, all_entry);
       if (++all_images_count == 10/*XXX*/)
               image_free(TAILQ_FIRST(&all_images));

       return (im);
}

int
image_check_line(struct screen *s, u_int py, u_int ny)
{
       struct image    *im, *im1;
       int              redraw = 0;

       TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) {
               if (py + ny > im->py && py < im->py + im->sy) {
                       image_free(im);
                       redraw = 1;
               }
       }
       return (redraw);
}

int
image_check_area(struct screen *s, u_int px, u_int py, u_int nx, u_int ny)
{
       struct image    *im, *im1;
       int              redraw = 0;

       TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) {
               if (py + ny <= im->py || py >= im->py + im->sy)
                       continue;
               if (px + nx <= im->px || px >= im->px + im->sx)
                       continue;
               image_free(im);
               redraw = 1;
       }
       return (redraw);
}

int
image_scroll_up(struct screen *s, u_int lines)
{
       struct image            *im, *im1;
       int                      redraw = 0;
       u_int                    sx, sy;
       struct sixel_image      *new;

       TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) {
               if (im->py >= lines) {
                       im->py -= lines;
                       redraw = 1;
                       continue;
               }
               if (im->py + im->sy <= lines) {
                       image_free(im);
                       redraw = 1;
                       continue;
               }
               sx = im->sx;
               sy = (im->py + im->sy) - lines;

               new = sixel_scale(im->data, 0, 0, 0, im->sy - sy, sx, sy, 1);
               sixel_free(im->data);
               im->data = new;

               im->py = 0;
               sixel_size_in_cells(im->data, &im->sx, &im->sy);

               free(im->fallback);
               image_fallback(&im->fallback, im->sx, im->sy);
               redraw = 1;
       }
       return (redraw);
}