/* Copyright (c) 2013 Fredrik Wikstrom. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
**    notice, this list of conditions and the following disclaimer.
**
** 2. Redistributions in binary form must reproduce the above copyright
**    notice, this list of conditions and the following disclaimer in the
**    documentation and/or other materials provided with the distribution.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
** POSSIBILITY OF SUCH DAMAGE.
*/

#include <exec/exec.h>
#include <dos/dos.h>
#include <devices/flite.h>
#include <proto/exec.h>
#include <proto/dos.h>

#define PROGNAME "Say"
#define TEMPLATE "TEXT,PHONES/S,S=SPEED/K/N,P=PITCH/K/N,O=OUT/K"
#define clamp(v,min,max) (((((v)>(max))?(max):(v))<(min))?(min):(v))

struct UserArgs {
	char *text;
	uint32 phones;
	int32 *duration;
	int32 *pitch;
	char *filename;
};

int main () {
	struct RDArgs *rdargs;
	struct UserArgs args = {0};
	struct MsgPort *mp;
	struct FliteRequest *fr;
	int ret = RETURN_FAIL;

	rdargs = IDOS->ReadArgs(TEMPLATE, (LONG *)&args, NULL);
	if (!rdargs) {
		IDOS->PrintFault(IDOS->IoErr(), PROGNAME);
		return ret;
	}

	if (!args.text) {
		args.text = "Enter some text to speak!";
	}

	/* Note: we could use a struct IOStdReq here if we didn't need any of
       the more "advanced" features of the device. */
	/* Any additional fields will be ignored by flite.device 52.1 */
	mp = IExec->AllocSysObject(ASOT_PORT, NULL);
	fr = IExec->AllocSysObjectTags(ASOT_IOREQUEST,
		ASOIOR_Size,		sizeof(struct FliteRequest),
		ASOIOR_ReplyPort,	mp,
		TAG_END);

	if (fr) {
		/* minimum version/revision */
		/* This information will unfortunately be ignored by 52.1, */
		/* so an additional check after OpenDevice() may be in order. */
		fr->fr_Version = 52;
		fr->fr_Revision = 2;

		if (!IExec->OpenDevice("flite.device", 0, (struct IORequest *)fr, 0)) {
			
			fr->fr_Std.io_Command = CMD_WRITE;
			fr->fr_Std.io_Data = args.text;
			fr->fr_Std.io_Length = ~0;

			if (args.phones) {
				fr->fr_Input = FLITE_INPUT_PHONES;
			}
			if (args.duration) {
				fr->fr_Duration = clamp(*args.duration, 1, 100) / 10.0;
			}
			if (args.pitch) {
				fr->fr_Pitch = clamp(*args.pitch, 50, 500);
			}
			if (args.filename) {
				fr->fr_Output = FLITE_OUTPUT_FILE;
				fr->fr_Lock = IDOS->GetCurrentDir();
				fr->fr_Filename = args.filename;
			}

			IExec->SendIO((struct IORequest *)fr);

			IExec->Wait((1 << mp->mp_SigBit)|SIGBREAKF_CTRL_C);

			/* Note: Never use CheckIO() or WaitIO() on an unused IORequest! */
			if (!IExec->CheckIO((struct IORequest *)fr)) {
				/* Aborting only works if the IORequest has not yet been
				   removed from the MsgPort by the flite.device process. */
				IExec->AbortIO((struct IORequest *)fr);
			}
			/* Wait for request to finish, and perform cleanup afterwards */
			IExec->WaitIO((struct IORequest *)fr);

			switch (fr->fr_Std.io_Error) {
				case IOERR_ABORTED:
					IDOS->PutErrStr(PROGNAME": aborted\n");
					break;
				case FLERR_UNKNOWN:
					IDOS->PutErrStr(PROGNAME": libflite error\n");
					break;
			}

			ret = RETURN_OK;

			IExec->CloseDevice((struct IORequest *)fr);
		} else {
			IDOS->PrintFault(IDOS->IoErr(), PROGNAME);
		}
	} else {
		IDOS->PrintFault(ERROR_NO_FREE_STORE, PROGNAME);
	}

	IExec->FreeSysObject(ASOT_IOREQUEST, fr);
	IExec->FreeSysObject(ASOT_PORT, mp);

	IDOS->FreeArgs(rdargs);

	return ret;
}
