starting_data = [3, 225, 1, 225, 6, 6, 1100, 1, 238, 225, 104, 0, 1101, 32, 43, 225, 101, 68, 192, 224, 1001, 224, -160,
                 224, 4, 224, 102, 8, 223, 223, 1001, 224, 2, 224, 1, 223, 224, 223, 1001, 118, 77, 224, 1001, 224, -87,
                 224, 4, 224, 102, 8, 223, 223, 1001, 224, 6, 224, 1, 223, 224, 223, 1102, 5, 19, 225, 1102, 74, 50,
                 224, 101, -3700, 224, 224, 4, 224, 1002, 223, 8, 223, 1001, 224, 1, 224, 1, 223, 224, 223, 1102, 89,
                 18, 225, 1002, 14, 72, 224, 1001, 224, -3096, 224, 4, 224, 102, 8, 223, 223, 101, 5, 224, 224, 1, 223,
                 224, 223, 1101, 34, 53, 225, 1102, 54, 10, 225, 1, 113, 61, 224, 101, -39, 224, 224, 4, 224, 102, 8,
                 223, 223, 101, 2, 224, 224, 1, 223, 224, 223, 1101, 31, 61, 224, 101, -92, 224, 224, 4, 224, 102, 8,
                 223, 223, 1001, 224, 4, 224, 1, 223, 224, 223, 1102, 75, 18, 225, 102, 48, 87, 224, 101, -4272, 224,
                 224, 4, 224, 102, 8, 223, 223, 1001, 224, 7, 224, 1, 224, 223, 223, 1101, 23, 92, 225, 2, 165, 218,
                 224, 101, -3675, 224, 224, 4, 224, 1002, 223, 8, 223, 101, 1, 224, 224, 1, 223, 224, 223, 1102, 8, 49,
                 225, 4, 223, 99, 0, 0, 0, 677, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1105, 0, 99999, 1105, 227, 247, 1105,
                 1, 99999, 1005, 227, 99999, 1005, 0, 256, 1105, 1, 99999, 1106, 227, 99999, 1106, 0, 265, 1105, 1,
                 99999, 1006, 0, 99999, 1006, 227, 274, 1105, 1, 99999, 1105, 1, 280, 1105, 1, 99999, 1, 225, 225, 225,
                 1101, 294, 0, 0, 105, 1, 0, 1105, 1, 99999, 1106, 0, 300, 1105, 1, 99999, 1, 225, 225, 225, 1101, 314,
                 0, 0, 106, 0, 0, 1105, 1, 99999, 1107, 226, 226, 224, 1002, 223, 2, 223, 1005, 224, 329, 1001, 223, 1,
                 223, 1007, 677, 226, 224, 1002, 223, 2, 223, 1006, 224, 344, 1001, 223, 1, 223, 108, 677, 226, 224,
                 102, 2, 223, 223, 1006, 224, 359, 1001, 223, 1, 223, 7, 226, 226, 224, 1002, 223, 2, 223, 1005, 224,
                 374, 101, 1, 223, 223, 107, 677, 677, 224, 1002, 223, 2, 223, 1006, 224, 389, 1001, 223, 1, 223, 1007,
                 677, 677, 224, 1002, 223, 2, 223, 1006, 224, 404, 1001, 223, 1, 223, 1107, 677, 226, 224, 1002, 223, 2,
                 223, 1005, 224, 419, 1001, 223, 1, 223, 108, 226, 226, 224, 102, 2, 223, 223, 1006, 224, 434, 1001,
                 223, 1, 223, 1108, 226, 677, 224, 1002, 223, 2, 223, 1006, 224, 449, 1001, 223, 1, 223, 1108, 677, 226,
                 224, 102, 2, 223, 223, 1005, 224, 464, 1001, 223, 1, 223, 107, 226, 226, 224, 102, 2, 223, 223, 1006,
                 224, 479, 1001, 223, 1, 223, 1008, 226, 226, 224, 102, 2, 223, 223, 1005, 224, 494, 101, 1, 223, 223,
                 7, 677, 226, 224, 1002, 223, 2, 223, 1005, 224, 509, 101, 1, 223, 223, 8, 226, 677, 224, 1002, 223, 2,
                 223, 1006, 224, 524, 1001, 223, 1, 223, 1007, 226, 226, 224, 1002, 223, 2, 223, 1006, 224, 539, 101, 1,
                 223, 223, 1008, 677, 677, 224, 1002, 223, 2, 223, 1006, 224, 554, 101, 1, 223, 223, 1108, 677, 677,
                 224, 102, 2, 223, 223, 1006, 224, 569, 101, 1, 223, 223, 1107, 226, 677, 224, 102, 2, 223, 223, 1005,
                 224, 584, 1001, 223, 1, 223, 8, 677, 226, 224, 1002, 223, 2, 223, 1006, 224, 599, 101, 1, 223, 223,
                 1008, 677, 226, 224, 102, 2, 223, 223, 1006, 224, 614, 1001, 223, 1, 223, 7, 226, 677, 224, 1002, 223,
                 2, 223, 1005, 224, 629, 101, 1, 223, 223, 107, 226, 677, 224, 102, 2, 223, 223, 1005, 224, 644, 101, 1,
                 223, 223, 8, 677, 677, 224, 102, 2, 223, 223, 1005, 224, 659, 1001, 223, 1, 223, 108, 677, 677, 224,
                 1002, 223, 2, 223, 1005, 224, 674, 101, 1, 223, 223, 4, 223, 99, 226]

PARAMETER_MODE_POSITION = 0
PARAMETER_MODE_VALUE = 1

COMMAND_HALT = 99


def decode_command_params(code):
    command = code % 100
    code //= 100

    param_mode_1 = code % 10
    code //= 10

    param_mode_2 = code % 10
    code //= 10

    param_mode_3 = code % 10
    code //= 10

    return command, param_mode_1, param_mode_2, param_mode_3


def get_data(input_data, index, param_mode):
    result = 0
    if param_mode is PARAMETER_MODE_POSITION:
        result = input_data[input_data[index]]
    elif param_mode is PARAMETER_MODE_VALUE:
        result = input_data[index]

    return result


def command_add(input_data, index, com_params, adt_params):
    result = get_data(input_data, index + 1, com_params[1]) + get_data(input_data, index + 2, com_params[2])
    input_data[input_data[index + 3]] = result
    return index + 4


def command_mlt(input_data, index, com_params, adt_params):
    result = get_data(input_data, index + 1, com_params[1]) * get_data(input_data, index + 2, com_params[2])
    input_data[input_data[index + 3]] = result
    return index + 4


def command_input(input_data, index, com_params, adt_params):
    input_data[input_data[index + 1]] = adt_params[0]
    return index + 2


def command_output(input_data, index, com_params, adt_params):
    diag_code = get_data(input_data, index + 1, com_params[1])
    print("{} after {} steps".format(diag_code, adt_params[1]))
    return index + 2


def command_jump_if_true(input_data, index, com_params, adt_params):
    if get_data(input_data, index + 1, com_params[1]) > 0:
        result = get_data(input_data, index + 2, com_params[2])
    else:
        result = index + 3
    return result


def command_jump_if_false(input_data, index, com_params, adt_params):
    if get_data(input_data, index + 1, com_params[1]) == 0:
        result = get_data(input_data, index + 2, com_params[2])
    else:
        result = index + 3
    return result


def command_less_than(input_data, index, com_params, adt_params):
    if get_data(input_data, index + 1, com_params[1]) < get_data(input_data, index + 2, com_params[2]):
        input_data[input_data[index + 3]] = 1
    else:
        input_data[input_data[index + 3]] = 0
    return index + 4


def command_equals(input_data, index, com_params, adt_params):
    if get_data(input_data, index + 1, com_params[1]) == get_data(input_data, index + 2, com_params[2]):
        input_data[input_data[index + 3]] = 1
    else:
        input_data[input_data[index + 3]] = 0
    return index + 4


def command_(input_data, index, com_params):
    pass


command_map = {
    1: command_add,
    2: command_mlt,
    3: command_input,
    4: command_output,
    5: command_jump_if_true,
    6: command_jump_if_false,
    7: command_less_than,
    8: command_equals
}


# Task - https://adventofcode.com/2019/day/5
def main():
    index = 0
    steps = 0
    input_data = starting_data.copy()
    # input_data = [3, 21, 1008, 21, 8, 20, 1005, 20, 22, 107, 8, 21, 20, 1006, 20, 31,
    #               1106, 0, 36, 98, 0, 0, 1002, 21, 125, 20, 4, 20, 1105, 1, 46, 104,
    #               999, 1105, 1, 46, 1101, 1000, 1, 20, 4, 20, 1105, 1, 46, 98, 99]
    input_value = 5

    while decode_command_params(input_data[index])[0] is not COMMAND_HALT:
        com_params = decode_command_params(input_data[index])

        func = command_map.get(com_params[0])
        if func is None:
            raise ValueError("Unknown command")
        else:
            index = func(input_data, index, com_params, (input_value, steps))

        steps += 1

    print("{} total steps".format(steps))


if __name__ == '__main__':
    main()
