#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iconv.h>
#include "datachunk.h"

DataChunk *DataChunk_new(QRencodeMode mode) {
    DataChunk *chunk;

    chunk = (DataChunk *) calloc(1, sizeof(DataChunk));
    if (chunk == NULL) return NULL;

    chunk->mode = mode;

    return chunk;
}

void DataChunk_free(DataChunk *chunk) {
    if (chunk) {
        if (chunk->data) free(chunk->data);
        free(chunk);
    }
}

void DataChunk_freeList(DataChunk *list) {
    DataChunk *next;

    while (list != NULL) {
        next = list->next;
        DataChunk_free(list);
        list = next;
    }
}

static void dumpNum(DataChunk *chunk) {
    printf("%s\n", chunk->data);
}

static void dumpAn(DataChunk *chunk) {
    printf("%s\n", chunk->data);
}

static void dump8(DataChunk *chunk) {
    int i, j;
    unsigned char c;
    int count = 0;
    unsigned char buf[16];

    for (i = 0; i < chunk->size; i++) {
        buf[count] = chunk->data[i];
        c = chunk->data[i];
        if (c >= ' ' && c <= '~') {
            putchar(c);
        } else {
            putchar('.');
        }
        count++;

        if (count >= 16) {
            putchar(' ');
            for (j = 0; j < 16; j++) {
                printf(" %02x", buf[j]);
            }
            count = 0;
            putchar('\n');
        }
    }
    if (count > 0) {
        for (i = 0; i < 16 - count; i++) {
            putchar(' ');
        }
        putchar(' ');
        for (j = 0; j < count; j++) {
            printf(" %02x", buf[j]);
        }
        count = 0;
        putchar('\n');
    }
}

static void dumpKanji(DataChunk *chunk) {
    iconv_t conv;
    char *inbuf, *outbuf, *outp;
    size_t inbytes, outbytes, ret;

    conv = iconv_open("UTF-8", "SHIFT_JIS");
    inbytes = chunk->size;
    inbuf = (char *) chunk->data;
    outbytes = inbytes * 4 + 1;
    outbuf = (char *) malloc(inbytes * 4 + 1);
    outp = outbuf;
    ret = iconv(conv, &inbuf, &inbytes, &outp, &outbytes);
    if (ret == (size_t) -1) { perror(NULL); }
    *outp = '\0';

    printf("%s\n", outbuf);

    iconv_close(conv);
    free(outbuf);
}

static void dumpChunk(DataChunk *chunk) {
    switch (chunk->mode) {
        case QR_MODE_NUM:
            printf("Numeric: %d bytes\n", chunk->size);
            dumpNum(chunk);
            break;
        case QR_MODE_AN:
            printf("AlphaNumeric: %d bytes\n", chunk->size);
            dumpAn(chunk);
            break;
        case QR_MODE_8:
            printf("8-bit data: %d bytes\n", chunk->size);
            dump8(chunk);
            break;
        case QR_MODE_KANJI:
            printf("Kanji: %d bytes\n", chunk->size);
            dumpKanji(chunk);
            break;
        default:
            printf("Invalid or reserved: %d bytes\n", chunk->size);
            dump8(chunk);
            break;
    }
}

void DataChunk_dumpChunkList(DataChunk *list) {
    while (list != NULL) {
        dumpChunk(list);
        list = list->next;
    }
}

int DataChunk_totalSize(DataChunk *list) {
    int size = 0;

    while (list != NULL) {
        size += list->size;
        list = list->next;
    }

    return size;
}

unsigned char *DataChunk_concatChunkList(DataChunk *list, int *retsize) {
    int size, idx;
    unsigned char *data;

    size = DataChunk_totalSize(list);
    if (size <= 0) return NULL;

    data = (unsigned char *) malloc(size + 1);
    idx = 0;
    while (list != NULL) {
        memcpy(&data[idx], list->data, list->size);
        idx += list->size;
        list = list->next;
    }
    data[size] = '\0';

    *retsize = size;
    return data;
}
