#include "mk.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

static const MK_CHAR className[] = CLASS_INTERNAL_CONSOLE;
static const MK_CHAR classSuper[] = CLASS_INTERNAL_IO;

int mk_console_print( MK_VM_STRUCT *vm, MK_VM_FRAME_ITEM *target, MK_VM_FRAME_ITEM *display, MK_VM_FRAME_ITEM **result )
{
	if( (INT_PTR)display & MK_VM_FRAME_ITEM_TYPE_DIRECT_VALUE ||
		MK_TYPE_ATTRIBUTE( display->flags ) != MK_VM_FRAME_ITEM_TYPE_STRING_VALUE )
	{
		MK_VARIABLE *pMethod = NULL;

		// call display.to_s()
		pMethod = 
			mk_vm_find_method( vm, 
				display, 
				mk_get_symbol_name_ptr( vm, "to_s" ), 
				0 );
		if( pMethod == NULL )
			return MK_VM_EXECUTE_EXPR_THROW;
		mk_vm_push_stack( vm->localStack, (INT_PTR)display );
		if( mk_vm_call_method( vm, pMethod ) == MK_VM_EXECUTE_EXPR_RETURN_NEXTSTEP )
		{
			MK_VM_FRAME_ITEM *converted = 
				(MK_VM_FRAME_ITEM*)mk_vm_pop_stack( vm->localStack );
			if( converted != NULL && 
				MK_TYPE_ATTRIBUTE( converted->flags ) == MK_VM_FRAME_ITEM_TYPE_STRING_VALUE )
				mk_console_print( vm, target, converted, result );
			else
				fputs( "Unknown object", stdout );
		}
	}
	else
	{
		fputs( display->stringTypeValue, stdout );
	}
	*result = NULL;
	return MK_VM_EXECUTE_EXPR_RETURN_RETURN;
}

int mk_console_print_ln( MK_VM_STRUCT *vm, MK_VM_FRAME_ITEM *target, MK_VM_FRAME_ITEM *display, MK_VM_FRAME_ITEM **result )
{
	int retCode = 
		mk_console_print( vm, target, display, result );
	if( retCode == MK_VM_EXECUTE_EXPR_RETURN_RETURN )
		fputs( "\n", stdout );
	return retCode;
}

int mk_console_lshift( MK_VM_STRUCT *vm, MK_VM_FRAME_ITEM *target, MK_VM_FRAME_ITEM *right, MK_VM_FRAME_ITEM **result )
{
	int retCode = 
		mk_console_print( vm, target, right, result );
	if( retCode == MK_VM_EXECUTE_EXPR_RETURN_RETURN )
		*result = target;
	return retCode;
}

int mk_console_getc( MK_VM_STRUCT *vm, MK_VM_FRAME_ITEM *target, MK_VM_FRAME_ITEM **result )
{
	*result = 
		mk_create_vm_frame_item_object( &vm->pFrameItemTable );
	(*result)->flags = MK_TYPE_SET_ATTRIBUTE( (*result)->flags, MK_VM_FRAME_ITEM_TYPE_CHARACTER_VALUE );
	(*result)->int32TypeValue = fgetc( stdin );
	return MK_VM_EXECUTE_EXPR_RETURN_RETURN;
}

MK_CLASS *mk_create_console_class( MK_VM_STRUCT *vm )
{
	MK_CLASS *result =
		mk_create_object( MK_TYPE_CLASS );
	
	result->nameThis = mk_get_symbol_name_ptr( vm, className );
	result->nameSuper = mk_get_symbol_name_ptr( vm, classSuper );

	mk_register_variable(
		vm,
		mk_create_method(
			vm, 
			"print",
			MK_TYPE_VARIABLE | 
				MK_TYPE_ATTRIBUTE_VARIABLE_METHOD | 
				MK_TYPE_ATTRIBUTE_VARIABLE_FINAL | 
				MK_TYPE_ATTRIBUTE_VARIABLE_STATIC |
				MK_TYPE_ATTRIBUTE_VARIABLE_METHOD_NATIVE,
			1,
			(INT_PTR)mk_console_print ),
		result );

	mk_register_variable(
		vm,
		mk_create_method(
			vm, 
			"println",
			MK_TYPE_VARIABLE | 
				MK_TYPE_ATTRIBUTE_VARIABLE_METHOD | 
				MK_TYPE_ATTRIBUTE_VARIABLE_FINAL | 
				MK_TYPE_ATTRIBUTE_VARIABLE_STATIC |
				MK_TYPE_ATTRIBUTE_VARIABLE_METHOD_NATIVE,
			1,
			(INT_PTR)mk_console_print_ln ),
		result );

	mk_register_variable( 
		vm,
		mk_create_method( 
			vm, 
			"<<", 
			MK_TYPE_VARIABLE | 
				MK_TYPE_ATTRIBUTE_VARIABLE_METHOD | 
				MK_TYPE_ATTRIBUTE_VARIABLE_FINAL | 
				MK_TYPE_ATTRIBUTE_VARIABLE_STATIC |
				MK_TYPE_ATTRIBUTE_VARIABLE_METHOD_NATIVE |
				MK_TYPE_ATTRIBUTE_VARIABLE_METHOD_OPERATOR |
				MK_LEX_TYPE_RESERVED_MARK_LSHIFT, 
			1, 
			(INT_PTR)mk_console_lshift ),
		result);

	mk_register_variable(
		vm,
		mk_create_method(
			vm, 
			"getc",
			MK_TYPE_VARIABLE | 
				MK_TYPE_ATTRIBUTE_VARIABLE_METHOD | 
				MK_TYPE_ATTRIBUTE_VARIABLE_FINAL | 
				MK_TYPE_ATTRIBUTE_VARIABLE_STATIC |
				MK_TYPE_ATTRIBUTE_VARIABLE_METHOD_NATIVE,
			0,
			(INT_PTR)mk_console_getc ),
		result );
	return result;
}
