package org.cafebabepy.runtime.module.builtins;

import org.cafebabepy.runtime.CafeBabePyException;
import org.cafebabepy.runtime.PyObject;
import org.cafebabepy.runtime.Python;
import org.cafebabepy.runtime.module.AbstractCafeBabePyType;
import org.cafebabepy.runtime.module.DefinePyFunction;
import org.cafebabepy.runtime.module.DefinePyType;
import org.cafebabepy.runtime.object.java.PyDictObject;

import java.util.Map;
import java.util.stream.Collectors;

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

/**
 * Created by yotchang4s on 2018/03/10.
 */
@DefinePyType(name = "builtins.dict")
public class PyDictType extends AbstractCafeBabePyType {

    public PyDictType(Python runtime) {
        super(runtime);
    }

    @DefinePyFunction(name = __str__)
    @SuppressWarnings("unchecked")
    public PyObject __str__(PyObject self) {
        Map<PyObject, PyObject> map = (Map<PyObject, PyObject>) self.toJava(Map.class);

        String jstr = map.entrySet().stream().map(e -> e.getKey() + ": " + e.getValue()).collect(Collectors.joining(", ", "{", "}"));

        return this.runtime.str(jstr);
    }

    @DefinePyFunction(name = __getitem__)
    public PyObject __getitem__(PyObject self, PyObject key) {
        if (!this.runtime.isInstance(self, "builtins.dict")) {
            throw this.runtime.newRaiseTypeError("descriptor '__getitem__' requires a 'dict' object but received a '" + self.getType().getName() + "'");
        }

        if (!PyDictObject.class.isAssignableFrom(self.getClass())) {
            throw new CafeBabePyException(PyDictObject.class.getName() + " is not assignable from " + self.getClass().getName());
        }

        PyDictObject dict = (PyDictObject) self;
        PyObject value = dict.getRawMap().get(key);
        if (value == null) {
            throw this.runtime.newRaiseException("builtins.KeyError", key.toJava(String.class));
        }

        return value;
    }

    @DefinePyFunction(name = __setitem__)
    public PyObject __setitem__(PyObject self, PyObject key, PyObject value) {
        if (!this.runtime.isInstance(self, "builtins.dict")) {
            throw this.runtime.newRaiseTypeError("descriptor '__setitem__' requires a 'dict' object but received a '" + self.getType().getName() + "'");
        }

        if (!PyDictObject.class.isAssignableFrom(self.getClass())) {
            throw new CafeBabePyException(PyDictObject.class.getName() + " is not assignable from " + self.getClass().getName());
        }

        PyDictObject dict = (PyDictObject) self;
        dict.getRawMap().put(key, value);

        return this.runtime.None();
    }

    @DefinePyFunction(name = __contains__)
    public PyObject __contains__(PyObject self, PyObject key) {
        if (!this.runtime.isInstance(self, "builtins.dict")) {
            throw this.runtime.newRaiseTypeError("descriptor '__contains__' requires a 'dict' object but received a '" + self.getType().getName() + "'");
        }

        if (!PyDictObject.class.isAssignableFrom(self.getClass())) {
            throw new CafeBabePyException(PyDictObject.class.getName() + " is not assignable from " + self.getClass().getName());
        }

        PyDictObject dict = (PyDictObject) self;
        return this.runtime.bool(dict.getRawMap().containsKey(key));
    }

    @DefinePyFunction(name = __delitem__)
    public void __delitem__(PyObject self, PyObject key) {
        if (!this.runtime.isInstance(self, "builtins.dict")) {
            throw this.runtime.newRaiseTypeError("descriptor '__delitem__' requires a 'dict' object but received a '" + self.getType().getName() + "'");
        }

        if (!PyDictObject.class.isAssignableFrom(self.getClass())) {
            throw new CafeBabePyException(PyDictObject.class.getName() + " is not assignable from " + self.getClass().getName());
        }

        PyDictObject dict = (PyDictObject) self;
        PyObject remove = dict.getRawMap().remove(key);
        if (remove == null) {
            throw this.runtime.newRaiseException("builtins.KeyError", String.valueOf(key));
        }
    }
}

@@ -4061,11 +4061,1089 @@
  %7D%0A    %7D
+%0A%0A    // FIXME default value%0A    @DefinePyFunction(name = %22get%22)%0A    public PyObject get(PyObject self, PyObject key, PyObject... defaultValue) %7B%0A        if (!this.runtime.isInstance(self, %22builtins.dict%22)) %7B%0A            throw this.runtime.newRaiseTypeError(%22descriptor 'get' requires a 'dict' object but received a '%22 + self.getType().getName() + %22'%22);%0A        %7D%0A%0A        if (!PyDictObject.class.isAssignableFrom(self.getClass())) %7B%0A            throw new CafeBabePyException(PyDictObject.class.getName() + %22 is not assignable from %22 + self.getClass().getName());%0A        %7D%0A%0A        if (defaultValue.length %3E 1) %7B%0A            throw this.runtime.newRaiseTypeError(%22get expected at most 2 arguments, got %22 + (1 + defaultValue.length));%0A        %7D%0A%0A        PyDictObject dict = (PyDictObject) self;%0A        PyObject value = dict.getRawMap().get(key);%0A        if (value == null) %7B%0A            if (defaultValue.length == 1) %7B%0A                value = defaultValue%5B0%5D;%0A            %7D else %7B%0A                value = this.runtime.None();%0A            %7D%0A        %7D%0A%0A        return value;%0A    %7D
 %0A%7D%0A

Add get method to dict