#! /usr/bin/env python
"""Generate opcode.h from opcode.list"""

import sys
import csv


class OPCODE(object):
    def __init__(self, name, code, has_arg1, has_arg2, has_result):
        self.name = name.upper()
        self.code = int(code)
        self.has_arg1 = int(has_arg1)
        self.has_arg2 = int(has_arg2)
        self.has_result = int(has_result)

    def __gt__(self, others):
        return self.code > others.code

    def __str__(self):
        return '{{"{}", OP_{}, {}, {}, {}}}'.format(
            self.name.lower(), self.name, self.has_arg1, self.has_arg2, self.has_result)

    def __repr__(self):
        return '{} - {}'.format(self.name, self.code)


def write_opcode(to, opcodes):
    # Sort opcodes by code
    opcodes.sort()

    with open(to, 'w') as f:
        f.write('''/* File opcode.h was generated by scripts/gen_opcode.py. */
#ifndef OPCODE_H
#define OPCODE_H
#include "vm.h"
/* opcode listing */
enum {\n''')

        # Write opcode list
        f.write(',\n'.join(['    OP_{} = {}'.format(op.name, op.code) for op in opcodes]))
        f.write('\n};\n\n')

        # Write opcode labels
        f.write('#define OP_LABELS ')
        for index, op in enumerate(opcodes):
            f.write('&&OP_{}, '.format(op.name))
            if index and index % 4 == 0:
                f.write('\\\n')
                if op != opcodes[-1]:
                    f.write('    ')
        f.write('\n\n')

        # Write instructions
        f.write('''struct instruction {
    const char *name;
    int opcode;
    int has_op1;
    int has_op2;
    int has_result;
};\n\n''')
        f.write('static const struct instruction instrs[] = {\n')
        f.write(',\n'.join(['    {}'.format(op) for op in opcodes]))
        f.write(',\n    {NULL, 0}\n};')

        # Write helper function(s)
        f.write('\n\nvoid hook_opcodes(vm_env *);')
        f.write('\n\n#endif /* OPCODE_H */\n')


def main():
    if len(sys.argv) == 2:
        to_file = sys.argv[1]
    else:
        to_file = 'opcode.h'

    with open('opcode.def') as csvfile:
        reader = csv.reader(csvfile, delimiter=',', quotechar='"')
        next(reader)  # Skip header line

        write_opcode(to_file, [OPCODE(*op) for op in reader])


if __name__ == '__main__':
    main()
