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.iterator.PyStrIteratorObject;
import org.cafebabepy.runtime.object.java.PyBytesObject;
import org.cafebabepy.runtime.object.java.PyStrObject;

import java.util.Arrays;

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

/**
 * Created by yotchang4s on 2018/10/08.
 */
@DefinePyType(name = "builtins.bytes")
public final class PyBytesType extends AbstractCafeBabePyType {

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

    @DefinePyFunction(name = __str__)
    public PyObject __str__(PyObject self) {
        PyObject strType = this.runtime.typeOrThrow("builtins.str");

        if (!this.runtime.isInstance(self, strType)) {
            throw this.runtime.newRaiseTypeError("descriptor '__str__' requires a 'str' object but received a '" + self.getFullName() + "'");
        }

        return self;
    }

    @DefinePyFunction(name = __add__)
    public PyObject __add__(PyObject self, PyObject other) {
        PyObject strType = this.runtime.typeOrThrow("builtins.str");

        if (!(self instanceof PyStrObject)) {
            throw this.runtime.newRaiseTypeError(
                    "descriptor '__add__' requires a 'str' object but received a '" + self.getFullName() + "'");

        } else if (!(other instanceof PyStrObject)) {
            throw this.runtime.newRaiseTypeError("must be str, not " + other);
        }

        return ((PyStrObject) self).add((PyStrObject) other);
    }

    @DefinePyFunction(name = __iter__)
    public PyObject __iter__(PyObject self) {
        if (!(self instanceof PyStrObject)) {
            throw this.runtime.newRaiseTypeError(
                    "descriptor '__iter__' requires a 'str' object but received a '"
                            + self.getType().getFullName()
                            + "'");
        }

        return new PyStrIteratorObject(this.runtime, (PyStrObject) self);
    }

    @DefinePyFunction(name = __eq__)
    public PyObject __eq__(PyObject self, PyObject other) {
        if (this.runtime.isInstance(self, "bytes")) {
            if (!(self instanceof PyBytesObject)) {
                throw new CafeBabePyException("self is not PyBytesObject " + self);

            } else if (this.runtime.isInstance(other, "bytes")) {
                if (!(other instanceof PyBytesObject)) {
                    throw new CafeBabePyException("other is not PyBytesObject " + other);
                }
                int[] jself = ((PyBytesObject) self).getValue();
                int[] jother = ((PyBytesObject) other).getValue();

                return Arrays.equals(jself, jother) ? this.runtime.True() : this.runtime.False();

            } else {
                return this.runtime.NotImplemented();
            }

        } else {
            throw this.runtime.newRaiseTypeError("TypeError: descriptor '__eq__' requires a 'str' object but received a '" + self.getName() + "'");
        }
    }

    @DefinePyFunction(name = __hash__)
    public PyObject __hash__(PyObject self) {
        if (this.runtime.isInstance(self, "bytes")) {
            if (!(self instanceof PyBytesObject)) {
                throw new CafeBabePyException("self is not PyStrObject " + self);
            }

            int hashCode = Arrays.hashCode(((PyBytesObject) self).getValue());

            return this.runtime.number(hashCode);

        } else {
            throw this.runtime.newRaiseTypeError("TypeError: descriptor '__hash__' requires a 'str' object but received a '" + self.getName() + "'");
        }
    }
}

@@ -386,19 +386,21 @@
 rator.Py
-Str
+Bytes
 Iterator
@@ -467,63 +467,8 @@
 ect;
-%0Aimport org.cafebabepy.runtime.object.java.PyStrObject;
 %0A%0Aim
@@ -1160,600 +1160,8 @@
  %7D%0A%0A
-    @DefinePyFunction(name = __add__)%0A    public PyObject __add__(PyObject self, PyObject other) %7B%0A        PyObject strType = this.runtime.typeOrThrow(%22builtins.str%22);%0A%0A        if (!(self instanceof PyStrObject)) %7B%0A            throw this.runtime.newRaiseTypeError(%0A                    %22descriptor '__add__' requires a 'str' object but received a '%22 + self.getFullName() + %22'%22);%0A%0A        %7D else if (!(other instanceof PyStrObject)) %7B%0A            throw this.runtime.newRaiseTypeError(%22must be str, not %22 + other);%0A        %7D%0A%0A        return ((PyStrObject) self).add((PyStrObject) other);%0A    %7D%0A%0A
     
@@ -1273,19 +1273,21 @@
 nceof Py
-Str
+Bytes
 Object))
@@ -1386,35 +1386,37 @@
 __' requires a '
-str
+bytes
 ' object but rec
@@ -1553,19 +1553,21 @@
 n new Py
-Str
+Bytes
 Iterator
@@ -1590,19 +1590,21 @@
 ime, (Py
-Str
+Bytes
 Object) 

Fix bytes