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.PyStrObject;

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

/**
 * Created by yotchang4s on 2017/05/13.
 */
@DefinePyType(name = "builtins.str")
public final class PyStrType extends AbstractCafeBabePyType {

    public PyStrType(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 = __eq__)
    public PyObject __eq__(PyObject self, PyObject other) {
        if (this.runtime.isInstance(self, "builtins.str")) {
            if (!(self instanceof PyStrObject)) {
                throw new CafeBabePyException("self is not PyStrObject " + self);

            } else if (this.runtime.isInstance(other, "builtins.str")) {
                if (!(other instanceof PyStrObject)) {
                    throw new CafeBabePyException("other is not PyStrObject " + other);
                }
                String jself = ((PyStrObject) self).getValue();
                String jother = ((PyStrObject) other).getValue();

                return jself.equals(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, "builtins.str")) {
            if (!(self instanceof PyStrObject)) {
                throw new CafeBabePyException("self is not PyStrObject " + self);
            }

            return this.runtime.number(((PyStrObject) self).getValue().hashCode());

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

@@ -3224,11 +3224,980 @@
  %7D%0A    %7D
+%0A%0A    @DefinePyFunction(name = %22join%22)%0A    public PyObject join(PyObject self, PyObject iterable) %7B%0A        if (!this.runtime.isInstance(self, %22builtins.str%22)) %7B%0A            throw this.runtime.newRaiseTypeError(%22TypeError: descriptor '__hash__' requires a 'str' object but received a '%22 + self.getName() + %22'%22);%0A        %7D%0A        if (!(self instanceof PyStrObject)) %7B%0A            throw new CafeBabePyException(%22self is not PyStrObject %22 + self);%0A        %7D%0A%0A        if (!this.runtime.isIterable(iterable)) %7B%0A            throw this.runtime.newRaiseTypeError(%22can only join an iterable%22);%0A        %7D%0A%0A        String selfValue = ((PyStrObject) self).getValue();%0A%0A        StringBuilder builder = new StringBuilder();%0A        this.runtime.iterIndex(iterable, (v, i) -%3E %7B%0A            if (i %3E 0) %7B%0A                builder.append(selfValue);%0A            %7D%0A            builder.append(v.toJava(String.class));%0A        %7D);%0A%0A        return this.runtime.str(builder.toString());%0A    %7D
 %0A%7D%0A

Add join method to str