# # # # # # # # # # # # # # # # # # # # # # # # # # # #
#  Framework provided by Daan de Graaf(UvA)           #
#  For Automaten en Formele Talen                     #
#  Under Guidance of Inge Bethke(UvA)                 #
#  Last edited 2018-05-05 by Bas van den Heuvel (UvA) #
# # # # # # # # # # # # # # # # # # # # # # # # # # # #

R = 1
L = -1
N = 0
movecounter = 0


class StartSymbol(object):
    def __init__(self, symbol):
        self.symbol = symbol

    def __int__(self):
        # Use 2, since $ represents index 2
        return 2

    def __repr__(self):
        return self.symbol

_DOLLAR_ = StartSymbol('$')


def decimal_to_unary(dec):
    dec = int(dec)
    if dec == 0:
        return [0]
    return [1 for i in range(dec)]


class Tape(object):
    ''' Tape object with different methods '''
    def __init__(self, init):
        self.default = 0
        self.items = [init]
        print('type ', type(self.items[0]))
        print('items: ', self.items)

    def get(self, index):
        ''' Get symbol from index'''
        if index < len(self.items):
            return self.items[index]
        else:
            return self.default

    def set(self, index, value):
        ''' Set an index to given value '''
        if index >= len(self.items):
            # provides an 'infinent' tape
            self.items.extend([self.default]*(index + 1 - len(self)))
        self.items[index] = value

    def __len__(self):
        return len(self.items)

    def __repr__(self):
        return ' '.join(map(str, self.items))


def executables(name):
    ''' Holding all the executable operations '''
    if name == '[ADD]':
        return [
            [(0, L, 3), (0, L, 1)],
            [(0, L, 2), (1, L, 1)],
            [(1, R, 4), (1, R, 7)],
            [(0, L, 9), (None)],
            [(1, R, 5), (None)],
            [(0, L, 6), (1, R, 5)],
            [(None), (0, L, 9)],
            [(1, R, 7), (1, R, 8)],
            [(0, L, 9), (1, R, 8)]
        ]

    # #### 20 points #####
    elif name == '[SUB]':
        # Your implementation here #
        ...

    # #### 30 points #####
    elif name == '[MUL]':
        # Your implementation here #
        ...

    # #### 30 points #####
    elif name == '[DIV]':
        # Your implementation here #
        ...


class TM(object):
    def __init__(self):
        # Set start of tape
        self.tape = Tape(_DOLLAR_)
        self.register = None
        self.ram = {}
        self.pending = None
        self.movecounter = 0

        self.head = 0

    # #### 20 points #####
    def run(self):
        # Your implementation here #
        ...

        # Keep track of tapehead movement
        print('\nmoves: ', self.movecounter)

    def tapeToMemory(self):
        '''Writes digits on the tape to memory'''
        print('_____to_memory_____')
        symbol = self.read()

        result = []
        while symbol != 0:
            result.append(symbol)
            self.write(0)
            self.move(-1)
            symbol = self.read()
        # Use the assignable variable
        self.ram[self.pending] = result

    def printTape(self, space=1):
        ''' Print tape in unary representation '''
        print(self.tape)
        ident = ' '*((1+space)*self.head)
        print(ident + '^')

    def writeDecimal(self, dec):
        ''' Write decimal to tap in unary representation'''
        print('Writedecimal ', dec)
        val = decimal_to_unary(dec)
        print('val: ', val)
        self.move(1)
        self.write(0)

        for i in val:
            self.move(1)
            self.write(i)

    def writeUnaryList(self, unary):
        ''' Write an unary digit to the tape'''
        self.move(1)
        self.write(0)
        for i in unary:
            print(i)
            self.move(1)
            self.write(i)

    def write(self, digit):
        self.tape.set(self.head, digit)

    def read(self):
        return self.tape.get(self.head)

    def execute(self, operation):
        ''' Executes given operation'''
        state = 0
        rule = executables(operation)

        done = False
        while not done:
            i = self.read()
            try:
                print('\n')
                print('state: ', state)
                write, move, state = rule[state][int(i)]
                print(str(write) + ', ' + str(move) + ', ' + str(state))
                self.printTape()
                self.write(write)
                self.move(move)
            except:
                print('HALT')
                done = True

    def move(self, direction):
        self.movecounter += 1
        self.head += direction
