from abc import ABC, abstractmethod
import operator

class Intcode:
	def __init__(self, intcode, mode: Mode, alarm=False, from_result=None):
		self.intcode = intcode
		self.original_intcode = intcode.copy()
		self.mode = mode
		self.from_result = from_result

		if (alarm):
			self.alarm(12, 2)

		self.current = 0;

	def run(self):
		self.mode.run()

	def next(self):
		next_op_code = self.intcode[self.current]
		return next_op_code

	def result(self):
		return print(f"result: {self.intcode[0]}")

	@mode.setter
	def mode(self, mode: Mode) -> None:
		self.mode = mode

	def alarm(self, pos1, pos2):
		self.intcode[1] = pos1
		self.intcode[2] = pos2

	def calc(self, pos1, pos2, pos_result, op):
		
		pos1 = self.intcode[pos1]
		pos2 = self.intcode[pos2]
		pos_result = self.intcode[pos_result]
		result = op(self.intcode[pos1], self.intcode[pos2])
		self.intcode[pos_result] = result

class Mode(ABC):

	@abstractmethod
	def run(self):
		pass


class normalMode(Mode):
	def run(self):
		self.normal()

	def normal(self):
		op_code = self.next()
		while op_code:

			if (op_code == 1):
				self.calc(self.current + 1, self.current + 2, self.current + 3, operator.add)
			if (op_code == 2):
				self.calc(self.current + 1, self.current + 2, self.current + 3, operator.mul)
			if (op_code == 99):
				self.result()
				break
			
			self.current += 4
			op_code = self.next()


class findMode(Mode):
	def run(self):
		for x in range(100):
				for y in range(100):
					self.find(x,y)
					self.current = 0
					self.reset()
	
	def find(self, pos1, pos2):
		self.alarm(pos1, pos2)

		op_code = self.next()
		while op_code:
			
			if (op_code == 1):
				try:
					self.calc(self.current + 1, self.current + 2, self.current + 3, operator.add)
				except IndexError:
					break
				
			if (op_code == 2):
				try:
					self.calc(self.current + 1, self.current + 2, self.current + 3, operator.mul)
				except IndexError:
					break

			if (op_code == 99):
				if (self.intcode[0] == self.from_result):
					self.find_result()
				break

			
			self.current += 4
			op_code = self.next()

	def reset(self):
		self.intcode = self.original_intcode.copy()

	def find_result(self):
		return print(f"noun: {self.intcode[1]}, verb: {self.intcode[2]}, result: {100 * self.intcode[1] + self.intcode[2]}")