package ru.dbotthepony.kstarbound.lua

import org.apache.logging.log4j.LogManager
import org.classdump.luna.ByteString
import org.classdump.luna.Conversions
import org.classdump.luna.LuaRuntimeException
import org.classdump.luna.runtime.AbstractFunctionAnyArg
import org.classdump.luna.runtime.Dispatch
import org.classdump.luna.runtime.ExecutionContext

class PrintFunction(val env: Any) : AbstractFunctionAnyArg() {
	private inner class State(arguments: Array<out Any>) {
		private val resolved = ArrayList<ByteString>(arguments.size)
		private var tostring: Any? = null

		private val machine = StateMachine()

		init {
			machine.add { Dispatch.index(it, env, "tostring") }
			machine.add {
				tostring = it.returnBuffer.get0()

				if (tostring == null) {
					throw LuaRuntimeException("global 'tostring' is nil (required by 'print')")
				}
			}

			for (argument in arguments) {
				machine.add { Dispatch.call(it, tostring, argument) }
				machine.add {
					val canonical = Conversions.canonicalRepresentationOf(it.returnBuffer.get0())

					if (canonical is ByteString) {
						resolved.add(canonical)
					} else {
						throw LuaRuntimeException("Illegal value returned by 'tostring' (required by 'print')")
					}
				}
			}

			machine.add {
				LOGGER.info(resolved.joinToString("\t") { it.decode() })
				it.returnBuffer.setTo()
			}
		}

		fun run(context: ExecutionContext) {
			machine.run(context, this@PrintFunction, this)
		}
	}

	override fun resume(context: ExecutionContext, suspendedState: Any) {
		(suspendedState as State).run(context)
	}

	override fun invoke(context: ExecutionContext, args: Array<out Any>) {
		State(args).run(context)
	}

	companion object {
		private val LOGGER = LogManager.getLogger()
	}
}
