/*
 *	Copyright (c) 2024 Scott Duensing, scott@kangaroopunch.com
 *
 *	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 "text.h"
#include "portme.h"
#include "memory.h"
#include "story.h"
#include "state.h"
#include "object.h"
#include "messages.h"
#include "zscii.h"
#include "lib.h"


static void textMoreCheck(void);
static void textNumPrint_r(int32_t val);


void textCharPrint(char c) {
	static char     lineBuffer[256];
	static uint16_t lineWidth = 0;
	byte            width     = storyScreenWidthCharacters();
	byte            space;
	byte            i;
	byte            x;
	byte            y;

	if (__state.redirecting) {
		if (storyVersion() <= 3) {
			if (__state.statusBarLen < width) {
				__state.statusBar[__state.statusBarLen++] = c;
			}
		}
		return;
	}

	if (!__state.outputting) return;

	// Do we need to word wrap?
	if (lineWidth == width - 1) {
		// Are we printing a space or CR?  If so, just go to next line.
		if ((c == ' ') || (c == '\n')) {
			c = '\n';
		} else {
			// Scan the line backwards for the first space.
			for (space=lineWidth-1; space>0; space--) {
				if (lineBuffer[space] == ' ') break;
			}
			// Did we find a space?
			if (space == 0) {
				// No.  Just wrap.
				c = '\n';
			} else {
				// Move to after space.
				space++;
				// Erase text to move to next line from this line.
				portCharGetPos(&x, &y);
				portCharSetPos(space, y);
				for (i=space; i<=lineWidth; i++) {
					portCharPrint(' ');
				}
				// Wrap display.
				portCharPrint('\n');
				__state.linesOut++;
				// Move erased characters to front of buffer & redisplay.
				portCharGetPos(&x, &y);
				portCharSetPos(1, y);
				textMoreCheck();
				x = 0;
				for (i=space; i<=lineWidth; i++) {
					lineBuffer[x++] = lineBuffer[i];
					portCharPrint(lineBuffer[i]);
				}
				lineWidth = x;
			}
		}
	}

	// Display it.
	portCharPrint(c);

	// Update buffer.
	if (c == '\n') {
		// New line.
		lineWidth = 0;
		__state.linesOut++;
		textMoreCheck();
	} else {
		lineBuffer[lineWidth++] = c;
	}
}


static void textMoreCheck(void) {
	byte x;
	byte y;

	if (__state.linesOut >= storyScreenHeightLines() - __state.statusWindowLineCount - 1) {
		// Display [MORE] prompt.
		portCharGetPos(&x, &y);
		textStringPrint("[MORE]");
		textStringPrint("      ");
		portCharGet();
		portCharSetPos(x, y);
		textStringPrint("      ");
		portCharSetPos(x, y);
		__state.linesOut = 0;
	}
}


static void textNumPrint_r(int32_t val) {

	if (val > 9) {
		textNumPrint_r(val / 10);
	}

	textCharPrint('0' + (val % 10));
}


void textNumPrint(int16_t num) {
	int32_t n = num;

	if (n < 0) {
		textCharPrint('-');
		n = -n;
	}
	textNumPrint_r(n);
}


void textInput(uint32_t ramAddr, uint8_t length) {
	uint8_t i;
	char    c;
	byte    x;
	byte    y;

	for (i=0; i<length; i++) {
		c = portCharGet();
		// Delete/Backspace.
		if ((c == 8) || (c == 127)) {
			if (i > 0) {
				portCharGetPos(&x, &y);
				if (x > 1) {
					x--;
				} else {
					x = 1;
					y--;
				}
				portCharSetPos(x, y);
				portCharPrint(' ');
				portCharSetPos(x, y);
				i -= 2;
			}
		} else {
			ZPOKE(ramAddr + i, c);
			// Enter/Return.
			if ((c == 13) || (c == 10)) {
				portCharPrint('\n');
				break;
			}
			portCharPrint(c);
		}
	}
}


void textObjectPrint(uint16_t object) {
	uint32_t ptr = objectPointerGet(object);
	uint32_t addr;

	if (storyVersion() <= 3) {
		// Skip to properties field.
		ptr += 7;
		// Dereference to get to property table.
		addr = ZPEEKW(ptr);
		zsciiPrint(addr + 1, 0);
	} else {
		portDie(MSG_UNIMPLEMENTED);
	}
}


void textOutputStream(int16_t stream, int16_t options) {
	switch (stream) {
		case STREAM_OUTPUT_SCREEN:
			__state.outputting = true;
			break;

		case -STREAM_OUTPUT_SCREEN:
			__state.outputting = false;
			break;

		case STREAM_OUTPUT_MEMORY:
			__state.redirecting = true;
			break;

		case -STREAM_OUTPUT_MEMORY:
			__state.redirecting = false;
			break;
	}

	// Set up redirection pointers.
	if (storyVersion() <= 3) {
		__state.statusBarLen = 0;
	}
}


void textStringPrint(char *s) {
	uint16_t i;

	for (i=0; i<libStrLen(s); i++) {
		textCharPrint(s[i]);
	}
}


void textTimePrint(uint16_t hours, uint16_t minutes) {
	bool PM = (hours < 12) ? false : true;

	hours %= 12;
	if (hours == 0) hours = 12;

	if (hours < 10) textCharPrint(' ');
	textNumPrint(hours);

	textCharPrint(':');

	if (minutes < 10) textCharPrint('0');
	textNumPrint(minutes);

	textStringPrint((PM ? " pm" : " am"));
}
