package org.cafebabepy.runtime.module.builtins;

import org.cafebabepy.runtime.PyObject;
import org.cafebabepy.runtime.Python;
import org.cafebabepy.runtime.module.AbstractCafeBabePyModule;
import org.cafebabepy.runtime.module.DefinePyFunction;
import org.cafebabepy.runtime.module.DefinePyModule;

import java.util.ArrayList;
import java.util.List;

import static org.cafebabepy.util.ProtocolNames.__len__;

/**
 * Created by yotchang4s on 2017/05/12.
 */
@DefinePyModule(name = "builtins")
public class PyBuiltinsModule extends AbstractCafeBabePyModule {
    public PyBuiltinsModule(Python runtime) {
        super(runtime);
    }

    @DefinePyFunction(name = "isinstance")
    public PyObject isinstance(PyObject object, PyObject classInfo) {
        if (!classInfo.isType() && !(classInfo instanceof PyTupleType)) {
            throw this.runtime.newRaiseTypeError(
                    "isinstance() arg 2 must be a type or tuple of types");
        }

        return issubclass(object.getType(), classInfo);
    }

    @DefinePyFunction(name = "issubclass")
    public PyObject issubclass(PyObject clazz, PyObject classInfo) {
        if (!clazz.isType()
                || (!classInfo.isType() && !(classInfo instanceof PyTupleType))) {
            throw this.runtime.newRaiseTypeError(
                    "issubclass() arg 2 must be a type or tuple of types");
        }

        /*
        // FIXME 再帰的にtupleを見る
        if (classInfo instanceof PyTupleType) {
            this.runtime.iter(classInfo, types::add);
        }
        */

        for (PyObject type : clazz.getTypes()) {
            if (type == classInfo) {
                return this.runtime.True();
            }
        }

        return this.runtime.False();
    }

    // FIXME Same CPython
    @DefinePyFunction(name = "print")
    public PyObject print(PyObject objects) {
        System.out.println(objects.toJava(String.class));

        return this.runtime.None();
    }

    @DefinePyFunction(name = "chr")
    public PyObject chr(PyObject i) {
        int value = i.toJava(int.class);
        if (value < 0 || 0x10FFFF < value) {
            throw this.runtime.newRaiseException("builtins.ValueError", "chr() arg not in range(0x110000)");
        }

        return this.runtime.str(new String(new int[]{value}, 0, 1));
    }

    @DefinePyFunction(name = "len")
    public PyObject len(PyObject s) {
        PyObject len = this.runtime.getattrOptional(s, __len__).orElseThrow(() ->
                this.runtime.newRaiseTypeError("object of type '" + s.getFullName() + "' has no len()")
        );

        return len.call();
    }
}

@@ -42,16 +42,67 @@
 ltins;%0A%0A
+import org.cafebabepy.runtime.CafeBabePyException;%0A
 import o
@@ -346,57 +346,58 @@
 le;%0A
-%0A
 import 
-java.util.ArrayList;%0Aimport java.util.Lis
+org.cafebabepy.runtime.object.PyObjectObjec
 t;%0A%0A
@@ -2662,11 +2662,1378 @@
 );%0A    %7D
+%0A%0A    @DefinePyFunction(name = %22iter%22)%0A    public PyObject iter(PyObject object) %7B%0A        // FIXME sentinel%0A        return this.runtime.iter(object);%0A    %7D%0A%0A    @DefinePyFunction(name = %22reversed%22)%0A    public PyObject reversed(PyObject seq) %7B%0A        return this.runtime.reversed(seq);%0A    %7D%0A%0A    @DefinePyFunction(name = %22zip%22)%0A    public PyObject zip(PyObject%5B%5D iterables) %7B%0A        PyObject object = new PyObjectObject(this.runtime);%0A        object.initialize();%0A%0A        this.runtime.eval(object, %22%3CFIXME%3E%22,%0A                %22def zip(*iterables):%5Cn%22 +%0A                        %22  # zip('ABCD', 'xy') --%3E Ax By%5Cn%22 +%0A                        %22  sentinel = object()%5Cn%22 +%0A                        %22  iterators = %5Biter(it) for it in iterables%5D%5Cn%22 +%0A                        %22  while iterators:%5Cn%22 +%0A                        %22    result = %5B%5D%5Cn%22 +%0A                        %22    for it in iterators:%5Cn%22 +%0A                        %22      elem = next(it, sentinel)%5Cn%22 +%0A                        %22      if elem is sentinel:%5Cn%22 +%0A                        %22        return%5Cn%22 +%0A                        %22      result.append(elem)%5Cn%22 +%0A                        %22      yield tuple(result)%22%0A        );%0A%0A        return object.getScope().get(this.runtime.str(%22zip%22)).map(zip -%3E zip.call(iterables)).orElseThrow(() -%3E%0A                new CafeBabePyException(%22zip is not found%22)%0A        );%0A    %7D
 %0A%7D%0A

Add iter, reversed, zip function