#include <cstdio>
#include <cstdlib>
#include <cstring>

#define ILLEGAL_FILE_TYPE	0

int main (char** argc, int argv) {
    if (argv < 3) {
        printf("Usage: sbconvert [input path] [output path]");
        return EXIT_FAILURE;
    }
    FILE* in = fopen(argc[1], "rbx");
    if (in == NULL) {
        printf("Could not open input file (be sure it exists!)");
        return EXIT_FAILURE;
    }
    FILE* out = fopen(argc[2], "wbx");
    if (out == NULL) {
        printf("Could not open output file (be sure it exists!)");
        return EXIT_FAILURE;
    }
    try {
        convert(in, out);
        return EXIT_SUCCESS;
    } catch (int error) {
        printError(error);
        return EXIT_FAILURE;
    }
}

void convert (FILE* in, FILE* out) {
    if (strcmp(readString(in, 8), "ScratchV") == 0) {
        convertSB(in, out);
        return;
    }
    fseek(in, 0, SEEK_SET);
    if (strcmp(readString(in, 2), "PK") == 0) {
        fseek(in, 0, SEEK_SET);
        convertZIP(in, out);
        return;
    }
    throw ILLEGAL_FILE_TYPE;
}

void convertZIP (FILE* in, FILE* out) {
    ScratchProject p = readZIP(in);
    writeSB(p, out);
}

void convertSB (FILE* in, FILE* out) {
    ScratchProject p = readSB(in);
    writeZIP(p, out);
}

ScratchProject readZIP (FILE* in) {
    unsigned long inlen = 0;
    while (fgetc(in) != EOF) inlen++
    if (!feof(in)) throw IO_ERROR;
    fseek(in, 0, SEEK_SET);
    unsigned char* buf[inlen];
    for (unsigned long l = 0; l < inlen; l++)
        buf[l] = fgetc(in);
    zipios::ZipFile f = zipios::ZipFile(sstream::istringstream(buf), 0, inlen);
    std::vector<std::shared_ptr<FileEntry>> vector = f.entries();
    ByteBuffer bitmaps[vector.size()];
    int bi = 0;
    ByteBuffer vectors[vector.size()];
    int vi = 0;
    ByteBuffer sounds[vector.size()];
    int si = 0;
    ByteBuffer projectJSON = NULL;
    for (std::vector<std::shared_ptr<FileEntry>>::iterator it = entries.begin(); it != entries.end(); ++it) {
        FileEntry e = *(*it);
        if (suffixMatch(e.getName(), "project.json")) {
            if (projectJSON) throw MULTIPLE_PROJECT_JSONS;
            projectJSON = readString(e);
        } else if (suffixMatch(e.getName(), ".png")) {
            bitmaps[bi++] = readBinary(e);
        } else if (suffixMatch(e.getName(), ".svg")) {
            vectors[vi++] = readString(e);
        } else if (suffixMatch(e.getName(), ".wav")) {
            sounds[si++] = readBinary(e);
        }
    }
    if (!projectJSON) throw NO_PROJECT_JSON;
    return read(projectJSON, bitmaps, bi, vectors, vi, sounds, si);
}

ByteBuffer readBinary (FileEntry e, FileCollection zf) {
    zipios::ZipFile::stream_pointer_t is = zf.getInputStream(e.getName());
    if (is) {
    	is.seekg (0, is.end);
    	int size = is.tellg();
    	is.seekg (0, is.beg);
        unsigned char buf[size];
        is->read(buf, size);
        if (is) return ByteBuffer(buf, size);
    }
    throw ENTRY_READ_ERROR;
}

char* readString (FileEntry e, FileCollection zf) {
    zipios::ZipFile::stream_pointer_t is = zf.getInputStream(e.getName());
    if (is) {
    	is.seekg(0, is.end);
    	int size = is.tellg();
    	is.seekg(0, is.beg);
        char buf[size + 1];
        is->read(buf, size);
        buf[size] = '\0';
        if (is) return buf;
    }
    throw ENTRY_READ_ERROR;
}

ScratchProject read (char* project,
                     ByteBuffer* bitmaps, int blen,
                     char** vectors, int vlen,
                     ByteBuffer* sounds, int slen) {
    Reader::Reader reader = Reader::Reader();
    Value::Value value = Value::Value(ValueType.objectValue);
    if (!reader.parse(project, project + strlen(project), &value, false)) throw INVALID_JSON;
    if (!value["targets"].isArray()) throw MALFORMED_JSON;
    if (!value["monitors"].isArray()) throw MALFORMED_JSON;
    if (!value["extensions"].isArray()) throw MALFORMED_JSON;
    if (value["extensions"].size() != 0) throw UNSUPPORTED_FEATURE;
    ScratchProject p = ScratchProject();
    readTargets(value["targets"], bitmaps, blen, vectors, vlen, sounds, slen);
    readMonitors(value["monitors"], bitmaps, blen, vectors, vlen, sounds, slen);
    return p;
}