/*
 *  libpipi       Pathetic image processing interface library
 *  Copyright (c) 2004-2008 Sam Hocevar <sam@zoy.org>
 *                All Rights Reserved
 *
 *  $Id$
 *
 *  This library is free software. It comes without any warranty, to
 *  the extent permitted by applicable law. You can redistribute it
 *  and/or modify it under the terms of the Do What The Fuck You Want
 *  To Public License, Version 2, as published by Sam Hocevar. See
 *  http://sam.zoy.org/wtfpl/COPYING for more details.
 */

/*
 * context.c: processing stack handling routines
 */

#include "config.h"

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

#include "pipi.h"
#include "pipi_internals.h"

pipi_context_t *pipi_create_context()
{
    pipi_context_t *ret;

    ret = malloc(sizeof(pipi_context_t));
    memset(ret, 0, sizeof(pipi_context_t));

    return ret;
}

void pipi_destroy_context(pipi_context_t *ctx)
{
    free(ctx);
}

pipi_command_t const *pipi_get_command_list(void)
{
    static pipi_command_t const list[] =
    {
        { "load", 1 },
        { "save", 1 },

        { "dup", 0 },
        { "swap", 0 },
        { "roll", 1 },

        { "gamma", 1 },
        { "scale", 1 },
        { "geometry", 1 },
        { "tile", 1 },
        { "dither", 1 },
        { "blur", 1 },
        { "boxblur", 1 },
        { "median", 1 },
        { "gray", 0 },
        { "brightness", 1 },
        { "contrast", 1 },
        { "autocontrast", 0 },
        { "order", 0 },
        { "hflip", 0 },
        { "vflip", 0 },
        { "rotate90", 0 },
        { "rotate180", 0 },
        { "rotate270", 0 },
        { "invert", 0 },
        { "threshold", 1 },
        { "dilate", 0 },
        { "erode", 0 },
        { "wrap", 0 },
        { "combine", 0 },
        { "split", 0 },
        { "mean", 0 },
        { "min", 0 },
        { "max", 0 },
        { "add", 0 },
        { "sub", 0 },
        { "difference", 0 },
        { "multiply", 0 },
        { "divide", 0 },
        { "screen", 0 },
        { "overlay", 0 },
        { "line", 1 },

        /* End marker */
        { NULL, 0 }
    };

    return list;
}

int pipi_command(pipi_context_t *ctx, char const *cmd, ...)
{
    if(!strcmp(cmd, "load"))
    {
        char const *file;
        va_list ap;

        va_start(ap, cmd);
        file = va_arg(ap, char const *);
        va_end(ap);
        ctx->images[ctx->nimages] = pipi_load(file);
        if(ctx->images[ctx->nimages] == NULL)
            return -1;
        ctx->nimages++;
    }
    else if(!strcmp(cmd, "save"))
    {
        char const *file;
        va_list ap;

        if(ctx->nimages < 1)
            return -1;
        ctx->nimages--;
        va_start(ap, cmd);
        file = va_arg(ap, char const *);
        va_end(ap);
        pipi_save(ctx->images[ctx->nimages], file);
        pipi_free(ctx->images[ctx->nimages]);
    }
    else if(!strcmp(cmd, "gamma"))
    {
        char const *val;
        va_list ap;

        va_start(ap, cmd);
        val = va_arg(ap, char const *);
        va_end(ap);

        pipi_set_gamma(atof(val));
    }
    else if(!strcmp(cmd, "dither"))
    {
        pipi_image_t *src, *dst;
        char const *method;
        va_list ap;

        if(ctx->nimages < 1)
            return -1;
        va_start(ap, cmd);
        method = va_arg(ap, char const *);
        va_end(ap);
        src = ctx->images[ctx->nimages - 1];
        dst = NULL;
        if(!strcmp(method, "ost"))
            dst = pipi_dither_ostromoukhov(src, 0);
        else if(!strcmp(method, "sost"))
            dst = pipi_dither_ostromoukhov(src, 1);
        else if(!strcmp(method, "ediff"))
        {
            if(ctx->nimages < 2)
                return -1;
            dst = pipi_dither_ediff(ctx->images[ctx->nimages - 2], src, 0);
            pipi_free(ctx->images[ctx->nimages - 2]);
            ctx->nimages--;
        }
        else if(!strcmp(method, "sediff"))
        {
            if(ctx->nimages < 2)
                return -1;
            dst = pipi_dither_ediff(ctx->images[ctx->nimages - 2], src, 1);
            pipi_free(ctx->images[ctx->nimages - 2]);
            ctx->nimages--;
        }
        else if(!strncmp(method, "ordered", 7))
        {
            double scale = 1., angle = .0;
            if(ctx->nimages < 2)
                return -1;
            method = strchr(method, ':');
            if(method)
            {
                scale = atof(method + 1);
                method = strchr(method + 1, ':');
                if(method)
                    angle = atof(method + 1);
            }
            if(scale <= 0.)
                scale = 1.;
            dst = pipi_dither_ordered_ext(ctx->images[ctx->nimages - 2], src,
                                          scale, angle);
            pipi_free(ctx->images[ctx->nimages - 2]);
            ctx->nimages--;
        }
        else if(!strncmp(method, "halftone", 8))
        {
            double r, angle = .0;
            method = strchr(method, ':');
            if(!method)
                return -1;
            r = atof(method + 1);
            method = strchr(method + 1, ':');
            if(method)
                angle = atof(method + 1);
            if(r < 1.)
                r = 1.;
            dst = pipi_dither_halftone(src, r, angle);
        }
        else if(!strcmp(method, "random"))
            dst = pipi_dither_random(src);
        else if(!strcmp(method, "dbs"))
            dst = pipi_dither_dbs(src);
        if(dst == NULL)
            return -1;
        pipi_free(src);
        ctx->images[ctx->nimages - 1] = dst;
    }
    else if(!strcmp(cmd, "blur"))
    {
        pipi_image_t *src, *dst;
        char const *arg;
        va_list ap;
        double w, h, a = 0.0;

        if(ctx->nimages < 1)
            return -1;
        va_start(ap, cmd);
        arg = va_arg(ap, char const *);
        va_end(ap);
        w = h = atof(arg);
        arg = strchr(arg, 'x');
        if(arg)
        {
            h = atof(arg + 1);
            arg = strchr(arg, 'r');
            if(arg)
                a = atof(arg + 1);
        }
        src = ctx->images[ctx->nimages - 1];
        dst = pipi_gaussian_blur_ext(src, w, h, a, 0.0, 0.0);
        if(dst == NULL)
            return -1;
        pipi_free(src);
        ctx->images[ctx->nimages - 1] = dst;
    }
    else if(!strcmp(cmd, "boxblur"))
    {
        pipi_image_t *src, *dst;
        char const *arg;
        va_list ap;
        double w, h;

        if(ctx->nimages < 1)
            return -1;
        va_start(ap, cmd);
        arg = va_arg(ap, char const *);
        va_end(ap);
        w = h = atof(arg);
        arg = strchr(arg, 'x');
        if(arg)
            h = atof(arg + 1);
        src = ctx->images[ctx->nimages - 1];
        dst = pipi_box_blur_ext(src, w, h);
        if(dst == NULL)
            return -1;
        pipi_free(src);
        ctx->images[ctx->nimages - 1] = dst;
    }
    else if(!strcmp(cmd, "median"))
    {
        pipi_image_t *src, *dst;
        char const *arg;
        va_list ap;
        double w, h;

        if(ctx->nimages < 1)
            return -1;
        va_start(ap, cmd);
        arg = va_arg(ap, char const *);
        va_end(ap);
        w = h = atof(arg);
        arg = strchr(arg, 'x');
        if(arg)
            h = atof(arg + 1);
        src = ctx->images[ctx->nimages - 1];
        dst = pipi_median_ext(src, w, h);
        if(dst == NULL)
            return -1;
        pipi_free(src);
        ctx->images[ctx->nimages - 1] = dst;
    }
    else if(!strcmp(cmd, "geometry"))
    {
        pipi_image_t *src, *dst;
        char const *arg;
        va_list ap;
        int w, h;

        if(ctx->nimages < 1)
            return -1;
        va_start(ap, cmd);
        arg = va_arg(ap, char const *);
        va_end(ap);
        w = atoi(arg);
        arg = strchr(arg, 'x');
        if(!arg)
            return -1;
        h = atoi(arg + 1);
        if(w <= 0 || h <= 0)
            return -1;
        src = ctx->images[ctx->nimages - 1];
        dst = pipi_resize(src, w, h);
        if(dst == NULL)
            return -1;
        pipi_free(src);
        ctx->images[ctx->nimages - 1] = dst;
    }
    else if(!strcmp(cmd, "tile"))
    {
        pipi_image_t *src, *dst;
        char const *arg;
        va_list ap;
        int w, h;

        if(ctx->nimages < 1)
            return -1;
        va_start(ap, cmd);
        arg = va_arg(ap, char const *);
        va_end(ap);
        w = atoi(arg);
        arg = strchr(arg, 'x');
        if(!arg)
            return -1;
        h = atoi(arg + 1);
        if(w <= 0 || h <= 0)
            return -1;
        src = ctx->images[ctx->nimages - 1];
        dst = pipi_tile(src, w, h);
        if(dst == NULL)
            return -1;
        pipi_free(src);
        ctx->images[ctx->nimages - 1] = dst;
    }
    else if(!strcmp(cmd, "scale"))
    {
        pipi_image_t *src, *dst;
        char const *arg;
        va_list ap;
        double scale;
        int w, h;

        if(ctx->nimages < 1)
            return -1;
        src = ctx->images[ctx->nimages - 1];
        va_start(ap, cmd);
        arg = va_arg(ap, char const *);
        va_end(ap);
        scale = atof(arg);
        w = (int)(scale * src->w + 0.5);
        h = (int)(scale * src->h + 0.5);
        if(w <= 0 || h <= 0)
            return -1;
        dst = pipi_resize(src, w, h);
        if(dst == NULL)
            return -1;
        pipi_free(src);
        ctx->images[ctx->nimages - 1] = dst;
    }
    else if(!strcmp(cmd, "brightness"))
    {
        pipi_image_t *src, *dst;
        char const *arg;
        va_list ap;
        double val;

        if(ctx->nimages < 1)
            return -1;
        va_start(ap, cmd);
        arg = va_arg(ap, char const *);
        va_end(ap);
        val = atof(arg);
        src = ctx->images[ctx->nimages - 1];
        dst = pipi_brightness(src, val);
        if(dst == NULL)
            return -1;
        pipi_free(src);
        ctx->images[ctx->nimages - 1] = dst;
    }
    else if(!strcmp(cmd, "contrast"))
    {
        pipi_image_t *src, *dst;
        char const *arg;
        va_list ap;
        double val;

        if(ctx->nimages < 1)
            return -1;
        va_start(ap, cmd);
        arg = va_arg(ap, char const *);
        va_end(ap);
        val = atof(arg);
        src = ctx->images[ctx->nimages - 1];
        dst = pipi_contrast(src, val);
        if(dst == NULL)
            return -1;
        pipi_free(src);
        ctx->images[ctx->nimages - 1] = dst;
    }
    else if(!strcmp(cmd, "threshold"))
    {
        pipi_image_t *src, *dst;
        char const *arg;
        va_list ap;
        double val;

        if(ctx->nimages < 1)
            return -1;
        va_start(ap, cmd);
        arg = va_arg(ap, char const *);
        va_end(ap);
        val = atof(arg);
        src = ctx->images[ctx->nimages - 1];
        dst = pipi_threshold(src, val);
        if(dst == NULL)
            return -1;
        pipi_free(src);
        ctx->images[ctx->nimages - 1] = dst;
    }
    else if(!strcmp(cmd, "hflip"))
    {
        pipi_image_t *tmp;
        if(ctx->nimages < 1)
            return -1;
        tmp = ctx->images[ctx->nimages - 1];
        ctx->images[ctx->nimages - 1] = pipi_hflip(tmp);
        pipi_free(tmp);
    }
    else if(!strcmp(cmd, "vflip"))
    {
        pipi_image_t *tmp;
        if(ctx->nimages < 1)
            return -1;
        tmp = ctx->images[ctx->nimages - 1];
        ctx->images[ctx->nimages - 1] = pipi_vflip(tmp);
        pipi_free(tmp);
    }
    else if(!strcmp(cmd, "rotate90"))
    {
        pipi_image_t *tmp;
        if(ctx->nimages < 1)
            return -1;
        tmp = ctx->images[ctx->nimages - 1];
        ctx->images[ctx->nimages - 1] = pipi_rotate90(tmp);
        pipi_free(tmp);
    }
    else if(!strcmp(cmd, "rotate180"))
    {
        pipi_image_t *tmp;
        if(ctx->nimages < 1)
            return -1;
        tmp = ctx->images[ctx->nimages - 1];
        ctx->images[ctx->nimages - 1] = pipi_rotate180(tmp);
        pipi_free(tmp);
    }
    else if(!strcmp(cmd, "rotate270"))
    {
        pipi_image_t *tmp;
        if(ctx->nimages < 1)
            return -1;
        tmp = ctx->images[ctx->nimages - 1];
        ctx->images[ctx->nimages - 1] = pipi_rotate270(tmp);
        pipi_free(tmp);
    }
    else if(!strcmp(cmd, "order"))
    {
        pipi_image_t *tmp;
        if(ctx->nimages < 1)
            return -1;
        tmp = ctx->images[ctx->nimages - 1];
        ctx->images[ctx->nimages - 1] = pipi_order(tmp);
        pipi_free(tmp);
    }
    else if(!strcmp(cmd, "split"))
    {
        pipi_image_t *src;

        if(ctx->nimages < 1)
            return -1;
        src = ctx->images[ctx->nimages - 1];
        ctx->nimages += 2;
        ctx->images[ctx->nimages - 3] = pipi_red(src);
        ctx->images[ctx->nimages - 2] = pipi_green(src);
        ctx->images[ctx->nimages - 1] = pipi_blue(src);
        pipi_free(src);
    }
    else if(!strcmp(cmd, "combine"))
    {
        pipi_image_t *dst;

        if(ctx->nimages < 3)
            return -1;
        dst = pipi_rgb(ctx->images[ctx->nimages - 3],
                       ctx->images[ctx->nimages - 2],
                       ctx->images[ctx->nimages - 1]);
        if(dst == NULL)
            return -1;
        pipi_free(ctx->images[ctx->nimages - 3]);
        pipi_free(ctx->images[ctx->nimages - 2]);
        pipi_free(ctx->images[ctx->nimages - 1]);
        ctx->images[ctx->nimages - 3] = dst;
        ctx->nimages -= 2;
    }
    else if(!strcmp(cmd, "mean"))
    {
        pipi_image_t *dst;

        if(ctx->nimages < 2)
            return -1;
        dst = pipi_mean(ctx->images[ctx->nimages - 2],
                        ctx->images[ctx->nimages - 1]);
        if(dst == NULL)
            return -1;
        pipi_free(ctx->images[ctx->nimages - 2]);
        pipi_free(ctx->images[ctx->nimages - 1]);
        ctx->images[ctx->nimages - 2] = dst;
        ctx->nimages--;
    }
    else if(!strcmp(cmd, "min"))
    {
        pipi_image_t *dst;

        if(ctx->nimages < 2)
            return -1;
        dst = pipi_min(ctx->images[ctx->nimages - 2],
                       ctx->images[ctx->nimages - 1]);
        if(dst == NULL)
            return -1;
        pipi_free(ctx->images[ctx->nimages - 2]);
        pipi_free(ctx->images[ctx->nimages - 1]);
        ctx->images[ctx->nimages - 2] = dst;
        ctx->nimages--;
    }
    else if(!strcmp(cmd, "max"))
    {
        pipi_image_t *dst;

        if(ctx->nimages < 2)
            return -1;
        dst = pipi_max(ctx->images[ctx->nimages - 2],
                       ctx->images[ctx->nimages - 1]);
        if(dst == NULL)
            return -1;
        pipi_free(ctx->images[ctx->nimages - 2]);
        pipi_free(ctx->images[ctx->nimages - 1]);
        ctx->images[ctx->nimages - 2] = dst;
        ctx->nimages--;
    }
    else if(!strcmp(cmd, "add"))
    {
        pipi_image_t *dst;

        if(ctx->nimages < 2)
            return -1;
        dst = pipi_add(ctx->images[ctx->nimages - 2],
                       ctx->images[ctx->nimages - 1]);
        if(dst == NULL)
            return -1;
        pipi_free(ctx->images[ctx->nimages - 2]);
        pipi_free(ctx->images[ctx->nimages - 1]);
        ctx->images[ctx->nimages - 2] = dst;
        ctx->nimages--;
    }
    else if(!strcmp(cmd, "sub"))
    {
        pipi_image_t *dst;

        if(ctx->nimages < 2)
            return -1;
        dst = pipi_sub(ctx->images[ctx->nimages - 2],
                       ctx->images[ctx->nimages - 1]);
        if(dst == NULL)
            return -1;
        pipi_free(ctx->images[ctx->nimages - 2]);
        pipi_free(ctx->images[ctx->nimages - 1]);
        ctx->images[ctx->nimages - 2] = dst;
        ctx->nimages--;
    }
    else if(!strcmp(cmd, "difference"))
    {
        pipi_image_t *dst;

        if(ctx->nimages < 2)
            return -1;
        dst = pipi_difference(ctx->images[ctx->nimages - 2],
                              ctx->images[ctx->nimages - 1]);
        if(dst == NULL)
            return -1;
        pipi_free(ctx->images[ctx->nimages - 2]);
        pipi_free(ctx->images[ctx->nimages - 1]);
        ctx->images[ctx->nimages - 2] = dst;
        ctx->nimages--;
    }
    else if(!strcmp(cmd, "multiply"))
    {
        pipi_image_t *dst;

        if(ctx->nimages < 2)
            return -1;
        dst = pipi_multiply(ctx->images[ctx->nimages - 2],
                            ctx->images[ctx->nimages - 1]);
        if(dst == NULL)
            return -1;
        pipi_free(ctx->images[ctx->nimages - 2]);
        pipi_free(ctx->images[ctx->nimages - 1]);
        ctx->images[ctx->nimages - 2] = dst;
        ctx->nimages--;
    }
    else if(!strcmp(cmd, "divide"))
    {
        pipi_image_t *dst;

        if(ctx->nimages < 2)
            return -1;
        dst = pipi_divide(ctx->images[ctx->nimages - 2],
                          ctx->images[ctx->nimages - 1]);
        if(dst == NULL)
            return -1;
        pipi_free(ctx->images[ctx->nimages - 2]);
        pipi_free(ctx->images[ctx->nimages - 1]);
        ctx->images[ctx->nimages - 2] = dst;
        ctx->nimages--;
    }
    else if(!strcmp(cmd, "screen"))
    {
        pipi_image_t *dst;

        if(ctx->nimages < 2)
            return -1;
        dst = pipi_screen(ctx->images[ctx->nimages - 2],
                          ctx->images[ctx->nimages - 1]);
        if(dst == NULL)
            return -1;
        pipi_free(ctx->images[ctx->nimages - 2]);
        pipi_free(ctx->images[ctx->nimages - 1]);
        ctx->images[ctx->nimages - 2] = dst;
        ctx->nimages--;
    }
    else if(!strcmp(cmd, "overlay"))
    {
        pipi_image_t *dst;

        if(ctx->nimages < 2)
            return -1;
        dst = pipi_overlay(ctx->images[ctx->nimages - 2],
                           ctx->images[ctx->nimages - 1]);
        if(dst == NULL)
            return -1;
        pipi_free(ctx->images[ctx->nimages - 2]);
        pipi_free(ctx->images[ctx->nimages - 1]);
        ctx->images[ctx->nimages - 2] = dst;
        ctx->nimages--;
    }
    else if(!strcmp(cmd, "wrap"))
    {
        if(ctx->nimages < 1)
            return -1;
        ctx->images[ctx->nimages - 1]->wrap = 1;
    }
    else if(!strcmp(cmd, "autocontrast"))
    {
        pipi_image_t *tmp;
        if(ctx->nimages < 1)
            return -1;
        tmp = ctx->images[ctx->nimages - 1];
        ctx->images[ctx->nimages - 1] = pipi_autocontrast(tmp);
        pipi_free(tmp);
    }
    else if(!strcmp(cmd, "invert"))
    {
        pipi_image_t *tmp;
        if(ctx->nimages < 1)
            return -1;
        tmp = ctx->images[ctx->nimages - 1];
        ctx->images[ctx->nimages - 1] = pipi_invert(tmp);
        pipi_free(tmp);
    }
    else if(!strcmp(cmd, "dilate"))
    {
        pipi_image_t *tmp;
        if(ctx->nimages < 1)
            return -1;
        tmp = ctx->images[ctx->nimages - 1];
        ctx->images[ctx->nimages - 1] = pipi_dilate(tmp);
        pipi_free(tmp);
    }
    else if(!strcmp(cmd, "erode"))
    {
        pipi_image_t *tmp;
        if(ctx->nimages < 1)
            return -1;
        tmp = ctx->images[ctx->nimages - 1];
        ctx->images[ctx->nimages - 1] = pipi_erode(tmp);
        pipi_free(tmp);
    }
    else if(!strcmp(cmd, "gray"))
    {
        if(ctx->nimages < 1)
            return -1;
        pipi_getpixels(ctx->images[ctx->nimages - 1], PIPI_PIXELS_Y_F);
    }
    else if(!strcmp(cmd, "free"))
    {
        if(ctx->nimages < 1)
            return -1;
        ctx->nimages--;
        pipi_free(ctx->images[ctx->nimages]);
    }
    else if(!strcmp(cmd, "dup"))
    {
        if(ctx->nimages < 1)
            return -1;
        ctx->images[ctx->nimages] = pipi_copy(ctx->images[ctx->nimages - 1]);
        ctx->nimages++;
    }
    else if(!strcmp(cmd, "swap"))
    {
        pipi_image_t *tmp;
        if(ctx->nimages < 2)
            return -1;
        tmp = ctx->images[ctx->nimages - 1];
        ctx->images[ctx->nimages - 1] = ctx->images[ctx->nimages - 2];
        ctx->images[ctx->nimages - 2] = tmp;
    }
    else if(!strcmp(cmd, "roll"))
    {
        pipi_image_t *tmp;
        char const *arg;
        va_list ap;
        int val;

        va_start(ap, cmd);
        arg = va_arg(ap, char const *);
        va_end(ap);
        val = atoi(arg);
        if(val <= 0 || ctx->nimages < val)
            return -1;
        if(val == 1)
            return 0;
        tmp = ctx->images[ctx->nimages - val];
        memmove(ctx->images + ctx->nimages - val,
                ctx->images + ctx->nimages - val + 1,
                (val - 1) * sizeof(*ctx->images));
        ctx->images[ctx->nimages - 1] = tmp;
    }
    else if(!strcmp(cmd, "line"))
    {
        char const *arg;
        va_list ap;
        int x1, y1, x2, y2, aa = 0, ret;
        uint32_t color = 0;

        if(ctx->nimages < 1)
            return -1;

        va_start(ap, cmd);
        arg = va_arg(ap, char const *);
        va_end(ap);

        ret = sscanf(arg, "%d,%d,%d,%d,%08x,%d",
               &x1, &y1, &x2, &y2, &color, &aa);
        if(ret < 5) return -1;

        ctx->images[ctx->nimages] = pipi_copy(ctx->images[ctx->nimages - 1]);
        pipi_draw_line(ctx->images[ctx->nimages],
                       x1,  y1,  x2,  y2, color, aa);
        ctx->nimages++;
    }
    else
    {
        return -1;
    }

    return 0;
}

