#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include "buf.h"
uint8_t *slurp_file(const char *path, uint64_t *filesize);

/* This program takes a ttc file and turns it into its component ttf files */
/* The makes two changes to the data:                                      */
/*	* The tables are placed a different offsets                        */
/*	  (so the offset fields in the table header are different)         */
/*      * the checksumAdjustment field of the 'head' table is set correctly*/

#define CHR(ch1,ch2,ch3,ch4) (((ch1)<<24)|((ch2)<<16)|((ch3)<<8)|(ch4))

static __attribute__((always_inline)) inline uint32_t av_bswap32(uint32_t x)
{
    __asm__("bswap   %0" : "+r" (x));
    return x;
}

static unsigned int filecheck(buf_t *ttf) {
    unsigned int *p = (unsigned int*)ttf->w_buffer;
    unsigned int i = 0, len = ttf->used>>2, sum = 0, chunk;

    while (i < len) {
	    chunk = p[i++];
	    sum += chunk;
    }
    return(sum);
}

static void copytable(buf_t *ttf, buf_t *ttc, unsigned int offset, unsigned int length) {
    unsigned int i, ch;

    buffer_setpos(ttc, offset);
    for (i = 0; i < length; i++) {
        if (buffer_iseof(ttc)) break;
        ch = buffer_read_c(ttc);
        buffer_append_c(ttf, ch);
    }
    if (i < length)
	    fprintf(stderr, "File ended before table (i: %u, length: %u)\n", i, length);
    if (length&1) {
        buffer_append_c(ttf, 0);
	    ++length;
    }
    if (length & 2) {
        buffer_append_c(ttf, 0);
        buffer_append_c(ttf, 0);
    }
}

typedef struct _ttfhead_t {
    uint32_t sfntver;
    uint16_t tablecnt;
    uint16_t pad0[3];
} __attribute__((packed)) ttfhead_t;

static void handlefont(const char *filename, unsigned int which, buf_t *ttc, unsigned int offset) {
    char outfile[2000], *p, *pt, *endpt;
    buf_t ttf;
    int i, cnt, *offsets, *lengths, headidx, tag, pos, headpos, savepos, outfd = -1;
    ttfhead_t head;

    //strcpy(outfile,filename);
    pt = strrchr(filename, '/');
    if (!pt) pt = filename;
    endpt = strrchr(pt,'.');
    if (endpt==NULL)
	    endpt = outfile + strlen(outfile);
    getcwd(outfile, 1900);
    p = outfile + strlen(outfile);
    *p++ = '/';
    memcpy(p, pt, endpt - pt); p += (size_t)(endpt - pt);
    *p++ = '_';
    *p++ = (which / 10)+0x30;
    *p++ = (which % 10)+0x30;
    *p++ = '.'; *p++ = 't'; *p++ = 't'; *p++ = 'f'; *p++ = '\0';

    buffer_create(&ttf, 65536);
    buffer_setpos(ttc, offset);
    buffer_read(ttc, &head, sizeof(ttfhead_t));
    buffer_append_data(&ttf, &head, sizeof(ttfhead_t));

    cnt = (head.tablecnt >> 8);
    fprintf(stderr, "count: %u/%u\n", cnt, head.tablecnt);
    offsets = malloc(cnt*sizeof(int));
    lengths = malloc(cnt*sizeof(int));
    headidx = -1;
    for ( i=0; i<cnt; ++i ) {
	    tag = av_bswap32(buffer_read_le32(ttc));
	    if (tag==CHR('h','e','a','d'))
	        headidx = i;
	    if (tag==CHR('E','B','D','T') || tag==CHR('E','B','L','C')) {
            buffer_incrpos(ttc, 12);
            continue;
        }
        fprintf(stderr, "tag: 0x%08X (%c%c%c%c)\n", tag, ((tag >> 24) + 0), (((tag >> 16) & 0xFF) + 0), (((tag >> 8) & 0xFF) + 0), ((tag & 0xFF) + 0));
        buffer_append_le32(&ttf, av_bswap32(tag));
	    buffer_append_le32(&ttf, buffer_read_le32(ttc));	/* checksum */
	    offsets[i] = av_bswap32(buffer_read_le32(ttc));		/* Reserve space for offset, will fix later */
	    lengths[i] = av_bswap32(buffer_read_le32(ttc));
        buffer_append_le32(&ttf, av_bswap32(offsets[i]));
        buffer_append_le32(&ttf, av_bswap32(lengths[i]));
    }
    headpos = -1;
    for (i=0; i<cnt; ++i) {
	    pos = buffer_getpos(&ttf);
	    copytable(&ttf,ttc,offsets[i],lengths[i]);
	    if (i==headidx) {
            ttf.w_buffer[pos+ 8] = 0;
            ttf.w_buffer[pos+ 9] = 0;
            ttf.w_buffer[pos+10] = 0;
            ttf.w_buffer[pos+11] = 0;
	        headpos = pos;
	    }
        savepos = buffer_getpos(&ttf);
	    buffer_setpos(&ttf, 12+i*16+8);
	    buffer_append_le32(&ttf, av_bswap32(pos));				/* Fix offset here */
        buffer_setpos(&ttf, savepos);
    }
    if (headpos!=-1) {
	    unsigned int checksum;
	    checksum = filecheck(&ttf);
	    checksum = 0xb1b0afba-checksum;
	    buffer_setpos(&ttf,headpos+8);
        buffer_append_le32(&ttf, av_bswap32(checksum));
    }
    fprintf (stderr, "%s ", outfile);
    if ((outfd = open(outfile, O_WRONLY|O_CREAT|O_APPEND, 0644)) < 0) {
	    fprintf(stderr, "Failed to open %s for output.\n", outfile);
        return;
    }
    write(outfd, ttf.w_buffer, ttf.used);
    close(outfd);
    free(offsets); free(lengths);
    buffer_free(&ttf);
}

static void handlefile(const char *filename, unsigned char *ttcbuf, size_t ttcsize) {
    buf_t b;
    unsigned int tag, version, cnt, i;
    unsigned int *offsets;

    buffer_create_from_data(&b, ttcbuf, ttcsize);
    tag= av_bswap32(buffer_read_le32(&b));
    if (tag!=CHR('t','t','c','f')) {
	    fprintf( stderr, "File does not look like a ttc file, bad header magic.\n");
        return;
    }

    version = av_bswap32(buffer_read_le32(&b));
    if (version!=0x10000 && version != 0x20000)
	    fprintf(stderr, "Unexpected ttc version number: %08x\n", version);
    cnt = av_bswap32(buffer_read_le32(&b));
    offsets = malloc(cnt*sizeof(int));
    for (i=0; i<cnt; ++i) {
	    offsets[i] = av_bswap32(buffer_read_le32(&b));
    }
    for (i=0; i<cnt; ++i)
	    handlefont(filename,i,&b,offsets[i]);
    free(offsets);
}
    
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>

uint8_t *slurp_file(const char *path, uint64_t *filesize) {
    struct stat st;
    uint8_t *ret = NULL;
    int fd = -1;
    stat(path, &st);
    *filesize = st.st_size;
    ret = malloc(st.st_size+1);
    if((fd = open(path, O_RDONLY)) < 0) {
        return NULL;
    }
    read(fd, ret, st.st_size);
    close(fd);
    return ret;
}

int main(int argc, char *argv[]) {
    int i;

    for ( i=1; i<argc; ++i ) {
        uint64_t ttc_size;
        uint8_t *ttc_buf = slurp_file(argv[i], &ttc_size);
        if (!ttc_buf) {
	        fprintf(stderr, "Could not open %s\n", argv[i]);
            continue;
        }
        fprintf(stderr, "%s => ", argv[i]);
	    handlefile(argv[i], ttc_buf, ttc_size);
        fprintf(stderr, "\n");
    }
    return( 0 );
}

