#include <vsos.h>
#include <sysmemory.h>
#include <ringbuf.h>
#include <vstypes.h>
#include <aucommon.h>
#include <string.h>
#include <exec.h>
#include "audiofile.h"
#define MIN(a,b) (((a)<(b))?(a):(b))
#define ETHIN_BUF_SIZE 8188
__mem_y __align s_int16 inBuf[ETHIN_BUF_SIZE];

struct AudioIn audioIn = {
	NULL
};

auto u_int16 AudioInFill(void) {
	s_int16 fi = audioIn.wr - audioIn.rd;
	if (fi < 0)
		fi += audioIn.bufSize;
	return fi & ~3;
}

ioresult AllocateAudioBuffer(register struct AudioIn *a) {
	ioresult res = S_OK;
	a->bufSize = ETHIN_BUF_SIZE;

	a->buf = (__mem_y u_int16 *)inBuf;
	memsetY(a->buf, 0, ETHIN_BUF_SIZE);
	a->modifier = MAKEMODF(ETHIN_BUF_SIZE);
	Disable();
	a->wr = a->rd = (__mem_y s_int16 *)(a->buf);
	Enable();
	if (!(a->buf)) {
		res = S_ERROR;
	}
	return res;
}
IOCTL_RESULT AudioIoctl(register __i0 VO_FILE *self, s_int16 request,
                        IOCTL_ARGUMENT argp) {
	ioresult res = S_OK;

	switch (request) {

	case IOCTL_RESTART:
		AllocateAudioBuffer(&audioIn);
		printf("IB: %p\n", audioIn.buf);
		break;

	case IOCTL_AUDIO_GET_IRATE:
		*((u_int32 *)argp) = audioIn.sampleRate;
		break;

	case IOCTL_AUDIO_GET_BITS:
		return (audioIn.flags & (AIN_32BIT)) ? 32 : 16;
		break;

	case IOCTL_AUDIO_GET_INPUT_BUFFER_SIZE:
		return audioIn.bufSize;
		break;

	case IOCTL_AUDIO_GET_INPUT_BUFFER_FILL:
		return AudioInFill();
		break;

	case IOCTL_AUDIO_GET_SAMPLE_COUNTER:
		Disable();
		*((u_int32 *)argp) = audioIn.sampleCounter;
		Enable();
		break;

	case IOCTL_AUDIO_GET_OVERFLOWS:
		Disable();
		*((u_int32 *)argp) = audioIn.overflow;
		Enable();
		break;
	default:
		res = S_ERROR;
		break;
	}

	return res;
}
u_int16 AudioInsert(register __mem_y u_int16 *src, register u_int16 steps, register u_int16 words) {
	u_int16 space = audioIn.bufSize - AudioInFill();
	u_int16 samples;
	/* Don't overwrite */
	if (audioIn.flags & AIN_32BIT) {
		samples = (words >> 2);
		space -= 4;
	} else {
		samples = (words >> 1);
		space -= 2;
	}
	if (space < words) {
		audioIn.overflow++;
		return;
	}
	audioIn.sampleCounter += samples;
	/* Samples used as garbage variable */
	while(steps--) {
		samples = *src++;
	}
	Forbid();
	audioIn.wr =  (__mem_y s_int16 *)
		RING_YY_D(ringcpyYY((__mem_y u_int16 *)audioIn.wr, audioIn.modifier, src, 1, words));
	Permit();
	return samples;
}
u_int16 AudioRead(register __i0 VO_FILE *self, u_int16 *buf,
                  u_int16 destinationIndex, u_int16 bytes) {
	u_int16 left = bytes>>1;
	/* Only whole samples are allowed. This is not a character device driver. */
	if (destinationIndex || (bytes&((audioIn.flags & AIN_32BIT) ? 7 : 3))) {
		return 0;
	}
	while (left) {
		u_int16 toRead = MIN(left, 16);
		while (AudioInFill() < toRead) {
			Delay(1);
		}
		audioIn.rd = (__mem_y s_int16 *)
			RING_YX_S(ringcpyYX((u_int16*)buf, 1, (__mem_y u_int16 *)audioIn.rd, audioIn.modifier, toRead));
		buf += toRead;
		left -= toRead;
	}
	return bytes;
}

char *Identify(register __i0 void *self, char *buf, u_int16 bufSize) {
	strncpy(buf, "AUIETH", bufSize);
	buf[bufSize-1] = '\0';
	return buf;
}
#pragma msg 30 off
FILEOPS audioFileOps = {
	NULL, /* Open() */
	NULL, /* Close() */
	AudioIoctl, /* Ioctl() */
	AudioRead, /* Read() */
	CommonOkResultFunction /* Write() (does not exist, always returns 0) */
};
#pragma msg 30 on

SIMPLE_FILE audioFile = {
	__MASK_PRESENT | __MASK_OPEN | __MASK_READABLE |
	__MASK_FILE | __MASK_CHARACTER_DEVICE, /* Flags */
	Identify, /* Identify() */
	&audioFileOps,
	0, /* pos */
	0, /* ungetc_buffer */
	NULL, /* dev */
};
