/*
 * miniJSS - Module structure and handling routines
 * Programmed and designed by Matti 'ccr' Hamalainen
 * (C) Copyright 2006-2007 Tecnic Software productions (TNSP)
 */
#include "jssmod.h"
#include <string.h>


#ifndef JSS_LIGHT
/* Take given data until maxlen reached, make a string
 */
char *jssASCIItoStr(char * sdata, const char endByte, const size_t maxLen)
{
    size_t i, k;
    char *res;

    for (i = 0; sdata[i] && i < maxLen; i++);

    res = (char *) dmMalloc(i + 1);
    if (res == NULL)
        return NULL;

    for (k = 0; sdata[k] != endByte && k < i; k++)
        res[k] = sdata[k];

    res[k] = 0;

    return res;
}


/* Encodes a given 8-bit sample
 */
BOOL jssEncodeSample8(Uint8 * data, const size_t len, const int ops)
{
    size_t count = len;
    Sint8 t, value = 0;
    
    while (count--)
    {
        t = *data;
        
        if (ops & jsampFlipSign)
            t ^= 0x80;

        if (ops & jsampDelta)
        {
            int n = t - value;
            value = t;
            t = n;
        }
        
        *(data++) = t;
    }

    return TRUE;
}


/* Decodes a given 16-bit sample
 */
BOOL jssEncodeSample16(Uint16 * data, const size_t len, const int ops)
{
    // "Split" the 16-bit samples into 8-bit halves
    if (ops & jsampSplit)
    {
        // Allocate temporary processing buffer
        size_t count, bufSize = len * sizeof(Sint16);
        Uint8 *bp1, *bp2;
        Sint16 *sdata, *tmpBuf = dmMalloc(bufSize);
        if (tmpBuf == NULL) return FALSE;

        sdata = tmpBuf;
        bp1 = (Uint8 *) data;
        bp2 = bp1 + len;
        count = len;

        while (count--)
        {
            Sint16 t = (*sdata++);
            *bp1++ = t >> 8;
            *bp2++ = t & 0xff;
        }
        
        memcpy(data, tmpBuf, bufSize);
        dmFree(tmpBuf);
        
        return jssEncodeSample8((Uint8 *) data, bufSize, ops);
    }
    else
    {
        Sint16 t, p, value = 0, *sdata = (Sint16 *) data;
        size_t count = len;
        
        while (count--)
        {
            if (ops & jsampSwapEndianess)
            {
                p = *sdata;
                t = ((p >> 8) & 0xff) | ((p & 0xff) << 8);
            }
            else
                t = *sdata;
            
            if (ops & jsampDelta)
            {
                int n = t - value;
                value = t;
                t = n;
            }
            
            if (ops & jsampFlipSign)
                t ^= 0x8000;
            
            *(sdata++) = t;
        }
    }
    return TRUE;
}

#endif


/* Decodes a given 8-bit sample
 */
BOOL jssDecodeSample8(Uint8 * data, const size_t len, const int ops)
{
    size_t count = len;
    Sint8 t, value = 0;
    
    while (count--)
    {
        t = *data;
        
        if (ops & jsampDelta)
            t = value = t + value;
        
        if (ops & jsampFlipSign)
            t ^= 0x80;
        
        *(data++) = t;
    }
    return TRUE;
}


/* Decodes a given 16-bit sample
 */
BOOL jssDecodeSample16(Uint16 * data, const size_t len, const int ops)
{
    if (ops & jsampSplit)
    {
        size_t count, bufSize = len * sizeof(Uint16);
        Uint8 *bp1, *bp2;
        Sint16 *tmpBuf, *sdata;
        
        if (!jssDecodeSample8((Uint8 *) data, bufSize, ops))
            return FALSE;
        
        tmpBuf = dmMalloc(bufSize);
        if (tmpBuf == NULL) return FALSE;
        memcpy(tmpBuf, data, bufSize);
        
        sdata = (Sint16 *) data;
        bp1 = (Uint8 *) tmpBuf;
        bp2 = bp1 + len;
        count = len;
        while (count--)
        {
            *sdata++ = (*bp1++ << 8) | (*bp2++ & 0xff);
        }
        
        dmFree(tmpBuf);
    }
    else
    {
        Sint16 t, p, value = 0, *sdata = (Sint16 *) data;
        size_t count = len;
        while (count--)
        {
            if (ops & jsampSwapEndianess)
            {
                p = *sdata;
                t = ((p >> 8) & 0xff) | ((p & 0xff) << 8);
            }
            else
                t = *sdata;
            
            if (ops & jsampDelta)
                t = value = t + value;
            
            if (ops & jsampFlipSign)
                t ^= 0x8000;
            
            *(sdata++) = t;
        }
    }
    return TRUE;
}


/* Convert sample data from U8 to S16
 */
int jssConvertSampleTo16(void **dst, void * src, const size_t len)
{
    size_t count = len;
    Uint8 *in = (Uint8 *) src;
    Sint16 *out;

    *dst = out = dmMalloc(sizeof(Sint16) * len);
    if (out == NULL)
        return DMERR_MALLOC;
    
    while (count--)
    {
        *(out++) = (*(in++) * 256) - 32768;
    }
    
    return DMERR_OK;
}


/* Converts the given module in preparation for playing it.
 * This involves sample format conversion (8 to 16 bit, etc.)
 *
 * NOTICE! The converted module can only be saved in JSSMOD
 * format, but this is not recommended.
 */
int jssConvertModuleForPlaying(JSSModule *module)
{
    int i;
    if (module == NULL)
        return DMERR_NULLPTR;

    // Convert instruments
    for (i = 0; i < module->ninstruments; i++)
    {
        JSSInstrument *inst = module->instruments[i];
        if (inst != NULL && inst->data != NULL)
        {
            int res;
            void *data = NULL;

            if (inst->flags & jsf16bit)
                continue;

            if ((res = jssConvertSampleTo16(&data, inst->data, inst->size)) != DMERR_OK)
                return res;

            inst->flags |= jsf16bit;
            dmFree(inst->data);
            inst->data = data;
        }
    }
    
    return DMERR_OK;
}


/* Allocates a new module structure or returns errorvalue if failed.
 * Memory is allocated only for the basic structure. Sample- and pattern
 * areas must be allocated separately with appropriate routines.
 */
JSSModule *jssAllocateModule(void)
{
    int i;
    JSSModule *module;

    // Allocate module structure
    module = dmMalloc0(sizeof(JSSModule));
    if (module == NULL)
        return NULL;

    // Initialize structure
    for (i = 0; i < jsetNChannels; i++)
        module->defPanning[i] = jchPanMiddle;

    for (i = 0; i < jsetMaxOrders; i++)
        module->orderList[i] = jsetOrderEnd;

    // Allocate mutex
#ifdef JSS_SUP_THREADS
    module->mutex = dmCreateMutex();
#endif

    return module;
}


/* Frees a given module structure, freeing all memory areas
 * that were allocated for it (including patterns, samples, etc.)
 */
int jssFreeModule(JSSModule * module)
{
    int i;

    if (module == NULL)
        return DMERR_NULLPTR;
    
    // Free strings
#ifndef JSS_LIGHT
    dmFree(module->moduleName);
    dmFree(module->trackerName);
#endif

    // Free patterns
    for (i = 0; i < module->npatterns; i++)
    {
        if (module->patterns[i] != NULL)
        {
            JSSPattern *pat = module->patterns[i];
            dmFree(pat->data);
            dmFree(pat);
            module->patterns[i] = NULL;
        }
    }

    // Free the "empty" pattern
    JSSPattern *pat = module->patterns[jsetMaxPatterns];
    if (pat != NULL)
    {
        dmFree(pat->data);
        dmFree(pat);
        module->patterns[i] = NULL;
    }

    // Free instruments
    for (i = 0; i < module->ninstruments; i++)
    {
        if (module->instruments[i] != NULL)
        {
            JSSInstrument *inst = module->instruments[i];
#ifndef JSS_LIGHT
            dmFree(inst->desc);
#endif
            dmFree(inst->data);
            dmFree(inst);
            module->instruments[i] = NULL;
        }
    }

    // Free extended instruments
    for (i = 0; i < module->nextInstruments; i++)
    {
        if (module->extInstruments[i] != NULL)
        {
            JSSExtInstrument *ext = module->extInstruments[i];
#ifndef JSS_LIGHT
            dmFree(ext->desc);
#endif
            dmFree(ext);
            module->extInstruments[i] = NULL;
        }
    }

    // Free mutex
#ifdef JSS_SUP_THREADS
    dmDestroyMutex(module->mutex);
#endif
    
    // Free the module structure
    memset(module, 0, sizeof(JSSModule));
    dmFree(module);

    return DMERR_OK;
}


/* Allocates and initializes a internal pattern structure.
 */
JSSPattern *jssAllocatePattern(const int nrows, const int nchannels)
{
    int row, chn;
    JSSPattern *res;
    JSSNote *pnote;

    // Check arguments
    if (nrows <= 0 || nchannels <= 0)
        JSSERROR(DMERR_INVALID_ARGS, NULL, "Invalid nrows=%i or nchannels=%i.\n", nrows, nchannels);

    // Allocate a pattern structure
    res = dmMalloc0(sizeof(JSSPattern));
    if (res == NULL)
        JSSERROR(DMERR_MALLOC, NULL, "Could not allocate pattern structure.\n");

    // Allocate notedata
    res->data = dmCalloc(nrows * nchannels, sizeof(JSSNote));
    if (res->data == NULL)
    {
        dmFree(res);
        JSSERROR(DMERR_MALLOC, NULL, "Could not allocate pattern data (nrows=%i, nchannels=%i).\n", nrows,
             nchannels);
    }

    // Initialize
    res->nrows = nrows;
    res->nchannels = nchannels;

    pnote = res->data;
    for (row = 0; row < nrows; row++)
    {
        for (chn = 0; chn < nchannels; chn++)
        {
            pnote->note = pnote->instrument = pnote->volume =
                pnote->effect = pnote->param = jsetNotSet;

            pnote++;
        }
    }

    // OK, return pointer to struct
    return res;
}


/* Allocates and initializes internal "normal" instrument structure.
 */
JSSInstrument *jssAllocateInstrument(void)
{
    JSSInstrument *res;

    // Allocate a instrument structure
    res = dmMalloc0(sizeof(JSSInstrument));
    if (res == NULL)
        return NULL;

    return res;
}


/* Allocates and initializes "extended" instrument structure.
 */
JSSExtInstrument *jssAllocateExtInstrument(void)
{
    int i;
    JSSExtInstrument *res;

    // Allocate a instrument structure
    res = dmMalloc0(sizeof(JSSExtInstrument));
    if (res == NULL)
        return NULL;

    for (i = 0; i < jsetNNotes; i++)
    {
        res->sNumForNotes[i] = jsetNotSet;
    }
    
    return res;
}
