/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 *
 * Copyright (C) 2014 Franklin Wei, Benjamin Brown
 * Copyright (C) 2004 Gregory Montoir
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ***************************************************************************/

#include "serializer.h"
#include "file.h"


void ser_create(struct Serializer* c, File *stream, enum Mode mode, uint8_t *ptrBlock, uint16_t saveVer)
{
    c->_stream = stream;
    c->_mode = mode;
    c->_ptrBlock = ptrBlock;
    c->_saveVer = saveVer;
}

void ser_saveOrLoadEntries(struct Serializer* c, struct Entry *entry) {
    debug(DBG_SER, "ser_saveOrLoadEntries() _mode=%d", c->_mode);
    c->_bytesCount = 0;
    switch (c->_mode) {
    case SM_SAVE:
        ser_saveEntries(c, entry);
        break;
    case SM_LOAD:
        ser_loadEntries(c, entry);
        break;
    }
    debug(DBG_SER, "ser_saveOrLoadEntries() _bytesCount=%d", c->_bytesCount);
}

void ser_saveEntries(struct Serializer* c, struct Entry *entry) {
    debug(DBG_SER, "ser_saveEntries()");
    for (; entry->type != SET_END; ++entry) {
        if (entry->maxVer == CUR_VER) {
            switch (entry->type) {
            case SET_INT:
                ser_saveInt(c, entry->size, entry->data);
                c->_bytesCount += entry->size;
                break;
            case SET_ARRAY:
                if (entry->size == SES_INT8) {
                    file_write(c->_stream, entry->data, entry->n);
                    c->_bytesCount += entry->n;
                } else {
                    uint8_t *p = (uint8_t *)entry->data;
                    for (int i = 0; i < entry->n; ++i) {
                        ser_saveInt(c, entry->size, p);
                        p += entry->size;
                        c->_bytesCount += entry->size;
                    }
                }
                break;
            case SET_PTR:
                file_writeUint32BE(c->_stream, *(uint8_t **)(entry->data) - c->_ptrBlock);
                c->_bytesCount += 4;
                break;
            case SET_END:
                break;
            }
        }
    }
}

void ser_loadEntries(struct Serializer* c, struct Entry *entry) {
    debug(DBG_SER, "ser_loadEntries()");
    for (; entry->type != SET_END; ++entry) {
        if (c->_saveVer >= entry->minVer && c->_saveVer <= entry->maxVer) {
            switch (entry->type) {
            case SET_INT:
                ser_loadInt(c, entry->size, entry->data);
                c->_bytesCount += entry->size;
                break;
            case SET_ARRAY:
                if (entry->size == SES_INT8) {
                    file_read(c->_stream, entry->data, entry->n);
                    c->_bytesCount += entry->n;
                } else {
                    uint8_t *p = (uint8_t *)entry->data;
                    for (int i = 0; i < entry->n; ++i) {
                        ser_loadInt(c, entry->size, p);
                        p += entry->size;
                        c->_bytesCount += entry->size;
                    }
                }
                break;
            case SET_PTR:
                *(uint8_t **)(entry->data) = c->_ptrBlock + file_readUint32BE(c->_stream);
                c->_bytesCount += 4;
                break;
            case SET_END:
                break;
            }
        }
    }
}

void ser_saveInt(struct Serializer* c, uint8_t es, void *p) {
    switch (es) {
    case 1:
        file_writeByte(c->_stream, *(uint8_t *)p);
        break;
    case 2:
        file_writeUint16BE(c->_stream, *(uint16_t *)p);
        break;
    case 4:
        file_writeUint32BE(c->_stream, *(uint32_t *)p);
        break;
    }
}

void ser_loadInt(struct Serializer* c, uint8_t es, void *p) {
    switch (es) {
    case 1:
        *(uint8_t *)p = file_readByte(c->_stream);
        break;
    case 2:
        *(uint16_t *)p = file_readUint16BE(c->_stream);
        break;
    case 4:
        *(uint32_t *)p = file_readUint32BE(c->_stream);
        break;
    }
}
