###
# Modern Albufeira Prolog Interpreter
#
# Warranty & Liability
# To the extent permitted by applicable law and unless explicitly
# otherwise agreed upon, XLOG Technologies AG makes no warranties
# regarding the provided information. XLOG Technologies AG assumes
# no liability that any problems might be solved with the information
# provided by XLOG Technologies AG.
#
# Rights & License
# All industrial property rights regarding the information - copyright
# and patent rights in particular - are the sole property of XLOG
# Technologies AG. If the company was not the originator of some
# excerpts, XLOG Technologies AG has at least obtained the right to
# reproduce, change and translate the information.
#
# Reproduction is restricted to the whole unaltered document. Reproduction
# of the information is only allowed for non-commercial uses. Selling,
# giving away or letting of the execution of the library is prohibited.
# The library can be distributed as part of your applications and libraries
# for execution provided this comment remains unchanged.
#
# Restrictions
# Only to be distributed with programs that add significant and primary
# functionality to the library. Not to be distributed with additional
# software intended to replace any components of the library.
#
# Trademarks
# Jekejeke is a registered trademark of XLOG Technologies AG.
##

from nova.core import (
    set, deref, is_structure, make_check,
    check_integer, MAX_ARITY, check_nil,
    exec_build, exec_unify, int32)


class PiWam:
    def __init__(self):
        self.code = NotImplemented
        self.state = NotImplemented


def run(pi):
    pc = 0
    accu = 0
    while pc < len(pi.code):
        instr = pi.code[pc]
        pc += 1
        value = run_get(pi, instr)
        accu = run_fun(instr, accu, value)
        run_set(pi, instr, accu)
        pc += run_jump(instr, accu)


def run_get(pi, instr):
    act = instr >> 20
    obj = ((instr >> 10) & 0x03FF) - 512
    mode = (act >> 4) & 0x0F
    if mode == 0:  # get
        return pi.state[obj]
    elif mode == 1:  # cst
        return obj
    elif mode == 2:  # set
        return 0
    elif mode == 4:  # out
        return 0
    elif mode == 5:  # in
        return int32(int(input(": ")))
    else:
        raise ValueError("illegal mode")


def run_fun(instr, accu, value):
    act = instr >> 20
    fun = act >> 8
    if fun == 0:  # val
        return value
    elif fun == 1:  # acc
        return accu
    elif fun == 2:  # add
        return int32(value + accu)
    elif fun == 3:  # sub
        return int32(value - accu)
    elif fun == 4:  # mul
        return int32(value * accu)
    elif fun == 8:  # nil
        # t.b.d.
        return 0
    elif fun == 9:  # cons
        # t.b.d.
        return 0
    elif fun == 10:  # car
        # t.b.d.
        return 0
    elif fun == 11:  # cdr
        # t.b.d.
        return 0
    else:
        raise ValueError("illegal fun")


def run_set(pi, instr, accu):
    act = instr >> 20
    obj = ((instr >> 10) & 0x03FF) - 512
    mode = (act >> 4) & 0x0F
    if mode == 0:  # get
        return
    elif mode == 1:  # cst
        return
    elif mode == 2:  # set
        pi.state[obj] = accu
        return
    elif mode == 4:  # out
        print(accu)
        return
    elif mode == 5:  # in
        return
    else:
        raise ValueError("illegal mode")


def run_jump(instr, accu):
    act = instr >> 20
    rel = (instr & 0x03FF) - 512
    cond = act & 0x0F
    if cond == 0:  # eq
        return rel if accu == 0 else 0
    elif cond == 1:  # nq
        return rel if accu != 0 else 0
    elif cond == 2:  # ls
        return rel if accu < 0 else 0
    elif cond == 3:  # gr
        return rel if accu > 0 else 0
    elif cond == 4:  # lq
        return rel if accu <= 0 else 0
    elif cond == 5:  # gq
        return rel if accu >= 0 else 0
    elif cond == 8:  # unit
        # t.b.d.
        return 0
    elif cond == 9:  # pair
        # t.b.d.
        return 0
    elif cond == 15:  # true
        return rel
    else:
        raise ValueError("illegal cond")


#################################################################
# π-WAM Interface                                               #
#################################################################

###
# test_ir_piwam_new(C, S, P): internal only
# The predicate succeeds in P with a CPU backed π-WAM for
# the code C and the state S.
##
def test_ir_piwam_new(args):
    pi = PiWam()
    alpha = exec_build(args[0])
    pi.code = list_ints(alpha)
    beta = exec_build(args[1])
    pi.state = list_ints(beta)
    return exec_unify(args[2], pi)


def list_ints(obj):
    peek = obj
    i = 0
    while (is_structure(peek) and
           peek.functor == "." and
           len(peek.args) == 2 and
           i < MAX_ARITY):
        i += 1
        peek = deref(peek.args[1])
    check_nil(peek)
    args = [NotImplemented] * i
    peek = obj
    i = 0
    while (is_structure(peek) and
           peek.functor == "." and
           len(peek.args) == 2):
        val = deref(peek.args[0])
        check_integer(val)
        val &= 0xFFFFFFFF
        if val >= 0x80000000:
            val -= 0x100000000
        args[i] = val
        i += 1
        peek = deref(peek.args[1])
    return args


###
# test_ir_piwam_run(P): internal only
# The predicate fails. As a side effect it will run the
# π-WAM P in the current thread. The predicate terminates
# when the π-WAM P terminates.
##
def test_ir_piwam_run(args):
    alpha = exec_build(args[0])
    run(alpha)
    return False


#################################################################
# π-WAM Lib Init                                                #
#################################################################

def main():
    set("ir_piwam_new", 3, make_check(test_ir_piwam_new))
    set("ir_piwam_run", 1, make_check(test_ir_piwam_run))
