/*
 *	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 "story.h"
#include "state.h"
#include "opcodes.h"
#include "oc_call.h"
#include "oc_compare.h"
#include "oc_input.h"
#include "oc_math.h"
#include "oc_memory.h"
#include "oc_misc.h"
#include "oc_object.h"
#include "oc_output.h"
#include "oc_save.h"
#include "oc_window.h"


// Macros to declare implemented opcodes.
#ifdef DEBUGGING
#define OP(num, name)   __state.opcodes[num] = opcodes_##name;  __state.opcodesName[num] = #name
#define OPX(num, name)  __state.extOpcodes[num] = opcodes_ext_##name;  __state.extOpcodes[num] = #name
#else
#define OP(num, name)   __state.opcodes[num] = opcodes_##name
#define OPX(num, name)  __state.extOpcodes[num] = opcodes_ext_##name
#endif

// Macros to declare un-implemented opcodes.
#define mOP(num, name)
#define mOPX(num, name)


void opcodesBuiltInitialTable(void) {
	// 2-operand instructions...
	OP(1, je);
	OP(2, jl);
	OP(3, jg);
	OP(4, dec_chk);
	OP(5, inc_chk);
	OP(6, jin);
	OP(7, test);
	OP(8, or);
	OP(9, and);
	OP(10, test_attr);
	OP(11, set_attr);
	OP(12, clear_attr);
	OP(13, store);
	OP(14, insert_obj);
	OP(15, loadw);
	OP(16, loadb);
	OP(17, get_prop);
	OP(18, get_prop_addr);
	OP(19, get_next_prop);
	OP(20, add);
	OP(21, sub);
	OP(22, mul);
	OP(23, div);
	OP(24, mod);

	// 1-operand instructions...
	OP(128, jz);
	OP(129, get_sibling);
	OP(130, get_child);
	OP(131, get_parent);
	OP(132, get_prop_len);
	OP(133, inc);
	OP(134, dec);
	OP(135, print_addr);

	OP(137, remove_obj);
	OP(138, print_obj);
	OP(139, ret);
	OP(140, jump);
	OP(141, print_paddr);
	OP(142, load);
	OP(143, not);

	// 0-operand instructions...
	OP(176, rtrue);
	OP(177, rfalse);
	OP(178, print);
	OP(179, print_ret);
	OP(180, nop);
	OP(181, save);
	OP(182, restore);
	OP(183, restart);
	OP(184, ret_popped);
	OP(185, pop);
	OP(186, quit);
	OP(187, new_line);

	// variable operand instructions...
	OP(224, call);
	OP(225, storew);
	OP(226, storeb);
	OP(227, put_prop);
	OP(228, read);
	OP(229, print_char);
	OP(230, print_num);
	OP(231, random);
	OP(232, push);
	OP(233, pull);

	if (storyVersion() < 3) return;

	OP(188, show_status);
	OP(189, verify);

	OP(234, split_window);
	OP(235, set_window);

	OP(243, output_stream);
	mOP(244, input_stream);
	mOP(245, sound_effect);

	if (storyVersion() < 4) return;

	// show_status is illegal in ver4+, but a build of Wishbringer
	// accidentally calls it, so always treat it as NOP instead.
	mOP(188, opcode_nop);

	mOP(25, call_2s);

	mOP(180, save_ver4);

	mOP(224, call_vs);

	mOP(228, sread_ver4);

	mOP(236, call_vs2);
	mOP(237, erase_window);
	mOP(238, erase_line);
	mOP(239, set_cursor);
	mOP(240, get_cursor);
	mOP(241, set_text_style);
	mOP(242, buffer_mode);

	mOP(246, read_char);
	mOP(247, scan_table);

	if (storyVersion() < 5) return;

	mOP(26, call_2n);
	mOP(27, set_color);
	mOP(28, throw);

	mOP(136, call_1s);

	mOP(143, call_1n);

	mOP(185, catch);

	OP(191, piracy);

	mOP(228, aread);

	mOP(243, output_stream_ver5);

	mOP(245, sound_effect_ver5);

	mOP(248, not_ver5);
	mOP(249, call_vn);
	mOP(250, call_vn2);
	mOP(251, tokenise);
	mOP(252, encode_text);
	mOP(253, copy_table);
	mOP(254, print_table);
	mOP(255, check_arg_count);

	// this is the "save" and "restore" opcodes in ver1-4; illegal in ver5+.
	// in ver5+, they use extended opcodes 0 and 1 for these.
	__state.opcodes[180] = 0;
	__state.opcodes[181] = 0;
#ifdef DEBUGGING
	__state.opcodesName[180] = 0;
	__state.opcodesName[181] = 0;
#endif

	// extended opcodes in ver5+ ...
	mOPX(0, save_ext);
	mOPX(1, restore_ext);
	mOPX(2, log_shift);
	mOPX(3, art_shift);
	mOPX(4, set_font);

	mOPX(9, save_undo);
	mOPX(10, restore_undo);
	mOPX(11, print_unicode);
	mOPX(12, check_unicode);
	mOPX(13, set_true_color);

	if (storyVersion() < 6) return;

	mOP(27, set_color_ver6);
	mOP(28, throw_ver6);

	mOP(185, catch_ver6);

	mOP(233, pull_ver6);

	mOP(238, erase_line_ver6);
	mOP(239, set_cursor_ver6);

	mOP(243, output_stream_ver6);

	mOP(248, not_ver6);

	mOPX(4, set_font_ver6);
	mOPX(5, draw_picture);
	mOPX(6, picture_data);
	mOPX(7, erase_picture);
	mOPX(8, set_margins);  // xop# 8

	mOPX(13, set_true_color_ver6);

	mOPX(16, move_window);
	mOPX(17, window_size);
	mOPX(18, window_style);
	mOPX(19, get_wind_prop);
	mOPX(20, scroll_window);
	mOPX(21, pop_stack);
	mOPX(22, read_mouse);
	mOPX(23, mouse_window);
	mOPX(24, push_stack);
	mOPX(25, put_wind_prop);
	mOPX(26, print_form);
	mOPX(27, make_menu);
	mOPX(28, picture_table);
	mOPX(29, buffer_screen);
}


void opcodesSetup(void) {
	uint8_t i;

	opcodesBuiltInitialTable();

	// 2OP opcodes repeating with different operand forms.
	for (i=32; i<=127; i++)  __state.opcodes[i] = __state.opcodes[i % 32];
	// 1OP opcodes repeating with different operand forms.
	for (i=144; i<=175; i++) __state.opcodes[i] = __state.opcodes[128 + (i % 16)];
	// 2OP opcodes repeating with VAR operand forms.
	for (i=192; i<=223; i++) __state.opcodes[i] = __state.opcodes[i % 32];

#ifdef DEBUGGING
	for (i=32; i<=127; i++)  __state.opcodesName[i] = __state.opcodesName[i % 32];
	for (i=144; i<=175; i++) __state.opcodesName[i] = __state.opcodesName[128 + (i % 16)];
	for (i=192; i<=223; i++) __state.opcodesName[i] = __state.opcodesName[i % 32];
#endif
}
