/*
 *  caca2tlf      Create a TOIlet font from a libcaca font
 *  Copyright (c) 2006 Sam Hocevar <sam@zoy.org>
 *                All Rights Reserved
 *
 *  $Id$
 *
 *  This program 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.
 */

/*
 * This is the main program entry point.
 */

#include "config.h"

#if defined(HAVE_INTTYPES_H)
#   include <inttypes.h>
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <cucul.h>

enum mode { GRAY, HALFBLOCKS, QUARTERBLOCKS } mode;

static void list_fonts(void);
static void add_char(unsigned long int);

cucul_font_t *f;
cucul_canvas_t *out, *onechar;
unsigned long int const *blocks;
uint8_t * image;
unsigned int w, h, gw, fgw, gh, iw, ih;

int main(int argc, char *argv[])
{
    char *fontname, *extraflag;
    unsigned int b, i;

    if(argc < 2)
    {
        fprintf(stderr, "Usage: %s [--half|--quarter] <font>\n", argv[0]);
        list_fonts();
        return -1;
    }

    if(!strcmp(argv[1], "--half") && argc >= 3)
    {
        extraflag = "--half ";
        mode = HALFBLOCKS;
        fontname = argv[2];
    }
    else if(!strcmp(argv[1], "--quarter") && argc >= 3)
    {
        extraflag = "--quarter ";
        mode = QUARTERBLOCKS;
        fontname = argv[2];
    }
    else
    {
        extraflag = "";
        mode = GRAY;
        fontname = argv[1];
    }

    f = cucul_load_font(fontname, 0);

    if(!f)
    {
        fprintf(stderr, "Font \"%s\" not found.\n", argv[1]);
        list_fonts();
        return -2;
    }

    w = cucul_get_font_width(f);
    h = cucul_get_font_height(f);
    iw = w * 2 + 1;
    ih = h + 1;
    switch(mode)
    {
    case GRAY:
        gw = w;
        fgw = w * 2;
        gh = h;
        break;
    case HALFBLOCKS:
        gw = w;
        fgw = w * 2;
        gh = (h + 1) / 2;
        break;
    case QUARTERBLOCKS:
        gw = (w + 1) / 2;
        fgw = (w * 2 + 1) / 2;
        gh = (h + 1) / 2;
        break;
    }

    blocks = cucul_get_font_blocks(f);
    onechar = cucul_create_canvas(0, 0);
    cucul_set_color_ansi(onechar, CUCUL_WHITE, CUCUL_BLACK);
    image = malloc(4 * iw * ih);

    out = cucul_create_canvas(0, 0);
    printf("tlf2a$ %u %u %u -1 4 0 0 0\n", gh, gh - 1, fgw + 2);

    printf("=============================================="
                                       "==================================\n");
    printf("  This font was automatically generated using:\n");
    printf("   %% caca2tlf %s\"%s\"\n", extraflag, fontname);
    printf("=============================================="
                                       "==================================\n");

    for(i = 32; i < 127; i++)
        add_char(i);

    add_char(196);
    add_char(214);
    add_char(220);
    add_char(228);
    add_char(246);
    add_char(252);
    add_char(223);

    for(b = 0, i = 0; blocks[i + 1]; i += 2)
    {
        int j, n = (int)(blocks[i + 1] - blocks[i]);

        for(j = 0; j < n; j++)
        {
            char buf[7];
            unsigned int len;
            unsigned long int ch = blocks[i] + j;

            if(ch <= 127 || ch == 196 || ch == 214 || ch == 220
               || ch == 228 || ch == 246 || ch == 252 || ch == 223)
                continue;

            len = cucul_utf32_to_utf8(buf, ch);
            buf[len] = '\0';
            printf("0x%.04lX %s\n", ch, buf);
            add_char(ch);
        }
    }

    cucul_free_canvas(out);
    cucul_free_canvas(onechar);
    free(image);
    cucul_free_font(f);

    return 0;
}

static void list_fonts(void)
{
    char const * const * fonts;
    unsigned int i;

    fprintf(stderr, "Available fonts:\n");

    fonts = cucul_get_font_list();
    for(i = 0; fonts[i]; i++)
        fprintf(stderr, "  \"%s\"\n", fonts[i]);
}

static void add_char(unsigned long int ch)
{
    void *buf;
    unsigned long int len;
    unsigned int x, y, myw, mygw;
    int full = cucul_utf32_is_fullwidth(ch);

    cucul_set_canvas_size(onechar, full ? 2 : 1, 1);
    cucul_put_char(onechar, 0, 0, ch);
    cucul_render_canvas(onechar, f, image, iw, ih, 4 * iw);

    myw = full ? 2 * w : w;
    mygw = full ? fgw : gw;

    cucul_set_canvas_size(out, (full ? fgw : gw) + 2, gh);

    switch(mode)
    {
    case GRAY:
        for(y = 0; y < h; y++)
            for(x = 0; x < myw; x++)
        {
            uint8_t c = image[4 * (x + y * iw) + 2];

            if(c >= 0xa0)
                cucul_put_str(out, x, y, "█");
            else if(c >= 0x80)
                cucul_put_str(out, x, y, "▓");
            else if(c >= 0x40)
                cucul_put_str(out, x, y, "▒");
            else if(c >= 0x20)
                cucul_put_str(out, x, y, "░");
            else
                cucul_put_char(out, x, y, ' ');
        }
        break;
    case HALFBLOCKS:
        for(y = 0; y < gh; y++)
            for(x = 0; x < mygw; x++)
        {
            static char const *str[] = { " ", "▀", "▄", "█" };

            uint8_t p1 = image[4 * (x + y * 2 * iw) + 2];
            uint8_t p2 = image[4 * (x + (y * 2 + 1) * iw) + 2];

            cucul_put_str(out, x, y, str[(p1 > 0x80) + 2 * (p2 > 0x80)]);
        }
        break;
    case QUARTERBLOCKS:
        for(y = 0; y < gh; y++)
            for(x = 0; x < mygw; x++)
        {
            static char const *str[] =
            {
                " ", "▘", "▝", "▀", "▖", "▌", "▞", "▛",
                "▗", "▚", "▐", "▜", "▄", "▙", "▟", "█"
            };

            uint8_t p1 = image[4 * (x * 2 + y * 2 * iw) + 2];
            uint8_t p2 = image[4 * (x * 2 + 1 + y * 2 * iw) + 2];
            uint8_t p3 = image[4 * (x * 2 + (y * 2 + 1) * iw) + 2];
            uint8_t p4 = image[4 * (x * 2 + 1 + (y * 2 + 1) * iw) + 2];

            cucul_put_str(out, x, y, str[(p1 > 0x80) + 2 * (p2 > 0x80) +
                                         4 * (p3 > 0x80) + 8 * (p4 > 0x80)]);
        }
        break;
    }

    if(ch == ' ' || ch == 0xa0)
    {
        cucul_draw_line(out, mygw - 1, 0, mygw - 1, gh - 1, '$');
        cucul_draw_line(out, mygw / 2, 0, mygw / 2, gh - 1, '$');
    }

    cucul_draw_line(out, mygw, 0, mygw, gh - 1, '@');
    cucul_put_char(out, mygw + 1, gh - 1, '@');

    buf = cucul_export_memory(out, "utf8", &len);
    fwrite(buf, len, 1, stdout);
    free(buf);
}