from vyper.exceptions import InvalidAttribute


def test_multi_setter_test(get_contract):
    multi_setter_test = """
dog: int128[3]
bar: int128[3][3]
@external
def foo() -> int128:
    self.dog = [1, 2, 3]
    return(self.dog[0] + self.dog[1] * 10 + self.dog[2] * 100)

@external
def fop() -> int128:
    self.bar[0] = [1, 2, 3]
    self.bar[1] = [4, 5, 6]
    return self.bar[0][0] + self.bar[0][1] * 10 + self.bar[0][2] * 100 + \
        self.bar[1][0] * 1000 + self.bar[1][1] * 10000 + self.bar[1][2] * 100000

@external
def goo() -> int128:
    god: int128[3] = [1, 2, 3]
    return(god[0] + god[1] * 10 + god[2] * 100)

@external
def gop() -> int128: # Following a standard naming scheme; nothing to do with the US republican party  # noqa: E501
    gar: int128[3][3] = [[1, 2, 3], [4, 5, 6], [0, 0, 0]]
    return gar[0][0] + gar[0][1] * 10 + gar[0][2] * 100 + \
        gar[1][0] * 1000 + gar[1][1] * 10000 + gar[1][2] * 100000

@external
def hoo() -> int128:
    self.dog = empty(int128[3])
    return(self.dog[0] + self.dog[1] * 10 + self.dog[2] * 100)

@external
def hop() -> int128:
    self.bar[1] = empty(int128[3])
    return self.bar[0][0] + self.bar[0][1] * 10 + self.bar[0][2] * 100 + \
        self.bar[1][0] * 1000 + self.bar[1][1] * 10000 + self.bar[1][2] * 100000

@external
def joo() -> int128:
    god: int128[3] = [1, 2, 3]
    god = empty(int128[3])
    return(god[0] + god[1] * 10 + god[2] * 100)

@external
def jop() -> int128:
    gar: int128[3][3] = [[1, 2, 3], [4, 5, 6], [0, 0, 0]]
    gar[1] = empty(int128[3])
    return gar[0][0] + gar[0][1] * 10 + gar[0][2] * 100 + \
        gar[1][0] * 1000 + gar[1][1] * 10000 + gar[1][2] * 100000

    """

    c = get_contract(multi_setter_test)
    assert c.foo() == 321
    c.foo()
    assert c.fop() == 654321
    c.fop()
    assert c.goo() == 321
    assert c.gop() == 654321
    assert c.hoo() == 0
    assert c.hop() == 321
    assert c.joo() == 0
    assert c.jop() == 321
    print("Passed multi-setter literal test")


def test_multi_setter_struct_test(get_contract):
    multi_setter_struct_test = """
struct Dog:
    foo: int128
    bar: int128
struct Bar:
    a: int128
    b: int128
struct Z:
    foo: int128[3]
    bar: Bar[2]
struct Goo:
    foo: int128
    bar: int128
struct Zed:
    foo: int128[3]
    bar: Bar[2]
dog: Dog[3]
z: Z[2]

@external
def foo() -> int128:
    foo0: int128 = 1
    self.dog[0] = Dog(foo=foo0, bar=2)
    self.dog[1] = Dog(foo=3, bar=4)
    self.dog[2] = Dog(foo=5, bar=6)
    return self.dog[0].foo + self.dog[0].bar * 10 + self.dog[1].foo * 100 + \
        self.dog[1].bar * 1000 + self.dog[2].foo * 10000 + self.dog[2].bar * 100000

@external
def fop() -> int128:
    self.z = [Z(foo=[1, 2, 3], bar=[Bar(a=4, b=5), Bar(a=2, b=3)]),
              Z(foo=[6, 7, 8], bar=[Bar(a=9, b=1), Bar(a=7, b=8)])]
    return self.z[0].foo[0] + self.z[0].foo[1] * 10 + self.z[0].foo[2] * 100 + \
        self.z[0].bar[0].a * 1000 + \
        self.z[0].bar[0].b * 10000 + \
        self.z[0].bar[1].a * 100000 + \
        self.z[0].bar[1].b * 1000000 + \
        self.z[1].foo[0] * 10000000 + \
        self.z[1].foo[1] * 100000000 + \
        self.z[1].foo[2] * 1000000000 + \
        self.z[1].bar[0].a * 10000000000 + \
        self.z[1].bar[0].b * 100000000000 + \
        self.z[1].bar[1].a * 1000000000000 + \
        self.z[1].bar[1].b * 10000000000000

@external
def goo() -> int128:
    god: Goo[3] = [Goo(foo=1, bar=2), Goo(foo=3, bar=4), Goo(foo=5, bar=6)]
    return god[0].foo + god[0].bar * 10 + god[1].foo * 100 + \
        god[1].bar * 1000 + god[2].foo * 10000 + god[2].bar * 100000

@external
def gop() -> int128:
    zed: Zed[2] = [
        Zed(foo=[1, 2, 3], bar=[Bar(a=4, b=5), Bar(a=2, b=3)]),
        Zed(foo=[6, 7, 8], bar=[Bar(a=9, b=1), Bar(a=7, b=8)])
    ]
    return zed[0].foo[0] + zed[0].foo[1] * 10 + \
        zed[0].foo[2] * 100 + \
        zed[0].bar[0].a * 1000 + \
        zed[0].bar[0].b * 10000 + \
        zed[0].bar[1].a * 100000 + \
        zed[0].bar[1].b * 1000000 + \
        zed[1].foo[0] * 10000000 + \
        zed[1].foo[1] * 100000000 + \
        zed[1].foo[2] * 1000000000 + \
        zed[1].bar[0].a * 10000000000 + \
        zed[1].bar[0].b * 100000000000 + \
        zed[1].bar[1].a * 1000000000000 + \
        zed[1].bar[1].b * 10000000000000
    """

    c = get_contract(multi_setter_struct_test)
    assert c.foo() == 654321
    assert c.fop() == 87198763254321
    assert c.goo() == 654321
    assert c.gop() == 87198763254321


def test_struct_assignment_order(get_contract, assert_compile_failed):
    code = """
struct Foo:
    a: uint256
    b: uint256

@external
@view
def test2() -> uint256:
    foo: Foo = Foo(b=2, a=297)
    return foo.a
    """
    assert_compile_failed(lambda: get_contract(code), InvalidAttribute)


def test_type_converter_setter_test(get_contract):
    type_converter_setter_test = """
pap: decimal[2][2]

@external
def goo() -> int256:
    self.pap = [[1.0, 2.0], [3.0, 4.0]]
    return floor(
        self.pap[0][0] +
        self.pap[0][1] * 10.0 +
        self.pap[1][0] * 100.0 +
        self.pap[1][1] * 1000.0)
    """

    c = get_contract(type_converter_setter_test)
    assert c.goo() == 4321


def test_composite_setter_test(get_contract):
    composite_setter_test = """
struct C:
    c: int128
struct Mom:
    a: C[3]
    b: int128
mom: Mom
qoq: C

@external
def foo() -> int128:
    self.mom = Mom(a=[C(c=1), C(c=2), C(c=3)], b=4)
    non: C = C(c=5)
    self.mom.a[0] = non
    non = C(c=6)
    self.mom.a[2] = non
    return self.mom.a[0].c + self.mom.a[1].c * 10 + self.mom.a[2].c * 100 + self.mom.b * 1000

@external
def fop() -> int128:
    popp: Mom = Mom(a=[C(c=1), C(c=2), C(c=3)], b=4)
    self.qoq = C(c=5)
    popp.a[0] = self.qoq
    self.qoq = C(c=6)
    popp.a[2] = self.qoq
    return popp.a[0].c + popp.a[1].c * 10 + popp.a[2].c * 100 + popp.b * 1000

@external
def foq() -> int128:
    popp: Mom = Mom(a=[C(c=1), C(c=2), C(c=3)], b=4)
    popp.a[0] = empty(C)
    popp.a[2] = empty(C)
    return popp.a[0].c + popp.a[1].c * 10 + popp.a[2].c * 100 + popp.b * 1000
    """

    c = get_contract(composite_setter_test)
    assert c.foo() == 4625
    assert c.fop() == 4625
    assert c.foq() == 4020
