/* Extended Module Player
 * Copyright (C) 1996-2022 Claudio Matsuoka and Hipolito Carraro Jr
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "common.h"
#include "period.h"
#include "player.h"
#include "hio.h"
#include "loader.h"


struct xmp_instrument *libxmp_get_instrument(struct context_data *ctx, int ins)
{
	struct smix_data *smix = &ctx->smix;
	struct module_data *m = &ctx->m;
	struct xmp_module *mod = &m->mod;
	struct xmp_instrument *xxi;

	if (ins < 0) {
		xxi = NULL;
	} else if (ins < mod->ins) {
		xxi = &mod->xxi[ins];
	} else if (ins < mod->ins + smix->ins) {
		xxi = &smix->xxi[ins - mod->ins];
	} else {
		xxi = NULL;
	}

	return xxi;
}

struct xmp_sample *libxmp_get_sample(struct context_data *ctx, int smp)
{
	struct smix_data *smix = &ctx->smix;
	struct module_data *m = &ctx->m;
	struct xmp_module *mod = &m->mod;
	struct xmp_sample *xxs;

	if (smp < 0) {
		xxs = NULL;
	} else if (smp < mod->smp) {
		xxs = &mod->xxs[smp];
	} else if (smp < mod->smp + smix->smp) {
		xxs = &smix->xxs[smp - mod->smp];
	} else {
		xxs = NULL;
	}

	return xxs;
}

int xmp_start_smix(xmp_context opaque, int chn, int smp)
{
	struct context_data *ctx = (struct context_data *)opaque;
	struct smix_data *smix = &ctx->smix;

	if (ctx->state > XMP_STATE_LOADED) {
		return -XMP_ERROR_STATE;
	}

	smix->xxi = (struct xmp_instrument *) calloc(smp, sizeof(struct xmp_instrument));
	if (smix->xxi == NULL) {
		goto err;
	}
	smix->xxs = (struct xmp_sample *) calloc(smp, sizeof(struct xmp_sample));
	if (smix->xxs == NULL) {
		goto err1;
	}

	smix->chn = chn;
	smix->ins = smix->smp = smp;

	return 0;

    err1:
	free(smix->xxi);
	smix->xxi = NULL;
    err:
	return -XMP_ERROR_INTERNAL;
}

int xmp_smix_play_instrument(xmp_context opaque, int ins, int note, int vol, int chn)
{
	struct context_data *ctx = (struct context_data *)opaque;
	struct player_data *p = &ctx->p;
	struct smix_data *smix = &ctx->smix;
	struct module_data *m = &ctx->m;
	struct xmp_module *mod = &m->mod;
	struct xmp_event *event;

	if (ctx->state < XMP_STATE_PLAYING) {
		return -XMP_ERROR_STATE;
	}

	if (chn >= smix->chn || chn < 0 || ins >= mod->ins || ins < 0) {
		return -XMP_ERROR_INVALID;
	}

	if (note == 0) {
		note = 60;		/* middle C note number */
	}

	event = &p->inject_event[mod->chn + chn];
	memset(event, 0, sizeof (struct xmp_event));
	event->note = (note < XMP_MAX_KEYS) ? note + 1 : note;
	event->ins = ins + 1;
	event->vol = vol + 1;
	event->_flag = 1;

	return 0;
}

int xmp_smix_play_sample(xmp_context opaque, int ins, int note, int vol, int chn)
{
	struct context_data *ctx = (struct context_data *)opaque;
	struct player_data *p = &ctx->p;
	struct smix_data *smix = &ctx->smix;
	struct module_data *m = &ctx->m;
	struct xmp_module *mod = &m->mod;
	struct xmp_event *event;

	if (ctx->state < XMP_STATE_PLAYING) {
		return -XMP_ERROR_STATE;
	}

	if (chn >= smix->chn || chn < 0 || ins >= smix->ins || ins < 0) {
		return -XMP_ERROR_INVALID;
	}

	if (note == 0) {
		note = 60;		/* middle C note number */
	}

	event = &p->inject_event[mod->chn + chn];
	memset(event, 0, sizeof (struct xmp_event));
	event->note = (note < XMP_MAX_KEYS) ? note + 1 : note;
	event->ins = mod->ins + ins + 1;
	event->vol = vol + 1;
	event->_flag = 1;

	return 0;
}

int xmp_smix_channel_pan(xmp_context opaque, int chn, int pan)
{
	struct context_data *ctx = (struct context_data *)opaque;
	struct player_data *p = &ctx->p;
	struct smix_data *smix = &ctx->smix;
	struct module_data *m = &ctx->m;
	struct channel_data *xc;

	if (chn >= smix->chn || pan < 0 || pan > 255) {
		return -XMP_ERROR_INVALID;
	}

	xc = &p->xc_data[m->mod.chn + chn];
	xc->pan.val = pan;

	return 0;
}

int xmp_smix_load_sample(xmp_context opaque, int num, const char *path)
{
	struct context_data *ctx = (struct context_data *)opaque;
	struct smix_data *smix = &ctx->smix;
	struct module_data *m = &ctx->m;
	struct xmp_instrument *xxi;
	struct xmp_sample *xxs;
	HIO_HANDLE *h;
	uint32 magic;
	int chn, rate, bits, size;
	int retval = -XMP_ERROR_INTERNAL;

	if (num >= smix->ins) {
		retval = -XMP_ERROR_INVALID;
		goto err;
	}

	xxi = &smix->xxi[num];
	xxs = &smix->xxs[num];

	h = hio_open(path, "rb");
	if (h == NULL) {
		retval = -XMP_ERROR_SYSTEM;
		goto err;
	}

	/* Init instrument */

	xxi->sub = (struct xmp_subinstrument *) calloc(1, sizeof(struct xmp_subinstrument));
	if (xxi->sub == NULL) {
		retval = -XMP_ERROR_SYSTEM;
		goto err1;
	}

	xxi->vol = m->volbase;
	xxi->nsm = 1;
	xxi->sub[0].sid = num;
	xxi->sub[0].vol = xxi->vol;
	xxi->sub[0].pan = 0x80;

	/* Load sample */

	magic = hio_read32b(h);
	if (magic != 0x52494646) {	/* RIFF */
		retval = -XMP_ERROR_FORMAT;
		goto err2;
	}

	if (hio_seek(h, 22, SEEK_SET) < 0) {
		retval = -XMP_ERROR_SYSTEM;
		goto err2;
	}
	chn = hio_read16l(h);
	if (chn != 1) {
		retval = -XMP_ERROR_FORMAT;
		goto err2;
	}

	rate = hio_read32l(h);
	if (rate == 0) {
		retval = -XMP_ERROR_FORMAT;
		goto err2;
	}

	if (hio_seek(h, 34, SEEK_SET) < 0) {
		retval = -XMP_ERROR_SYSTEM;
		goto err2;
	}
	bits = hio_read16l(h);
	if (bits == 0) {
		retval = -XMP_ERROR_FORMAT;
		goto err2;
	}

	if (hio_seek(h, 40, SEEK_SET) < 0) {
		retval = -XMP_ERROR_SYSTEM;
		goto err2;
	}
	size = hio_read32l(h);
	if (size == 0) {
		retval = -XMP_ERROR_FORMAT;
		goto err2;
	}

	libxmp_c2spd_to_note(rate, &xxi->sub[0].xpo, &xxi->sub[0].fin);

	xxs->len = 8 * size / bits;
	xxs->lps = 0;
	xxs->lpe = 0;
	xxs->flg = bits == 16 ? XMP_SAMPLE_16BIT : 0;

	xxs->data = (unsigned char *) malloc(size + 8);
	if (xxs->data == NULL) {
		retval = -XMP_ERROR_SYSTEM;
		goto err2;
	}

	/* ugly hack to make the interpolator happy */
	memset(xxs->data, 0, 4);
	memset(xxs->data + 4 + size, 0, 4);
	xxs->data += 4;

	if (hio_seek(h, 44, SEEK_SET) < 0) {
		retval = -XMP_ERROR_SYSTEM;
		goto err2;
	}
	if (hio_read(xxs->data, 1, size, h) != size) {
		retval = -XMP_ERROR_SYSTEM;
		goto err2;
	}
	hio_close(h);

	return 0;

    err2:
	free(xxi->sub);
	xxi->sub = NULL;
    err1:
	hio_close(h);
    err:
	return retval;
}

int xmp_smix_release_sample(xmp_context opaque, int num)
{
	struct context_data *ctx = (struct context_data *)opaque;
	struct smix_data *smix = &ctx->smix;

	if (num >= smix->ins) {
		return -XMP_ERROR_INVALID;
	}

	libxmp_free_sample(&smix->xxs[num]);
	free(smix->xxi[num].sub);

	smix->xxs[num].data = NULL;
	smix->xxi[num].sub = NULL;

	return 0;
}

void xmp_end_smix(xmp_context opaque)
{
	struct context_data *ctx = (struct context_data *)opaque;
	struct smix_data *smix = &ctx->smix;
	int i;

	for (i = 0; i < smix->smp; i++) {
		xmp_smix_release_sample(opaque, i);
	}

	free(smix->xxs);
	free(smix->xxi);
	smix->xxs = NULL;
	smix->xxi = NULL;
}
