# -*- mode:python; coding:utf-8; tab-width:4 -*-

from unittest import TestCase

from hamcrest import assert_that, anything
from doublex import Spy, Stub, called, ANY_ARG

import Ice
Ice.loadSlice('-I {} cannon.ice'.format(Ice.getSliceDir()))
import Cannon

from processor import ProcessorI

from common import M1, M2


class ProcessorServantTests(TestCase):
    """
    These are NOT remote tests. We directly instantiate servants here.
    """
    def test_processors_rings(self):
        # given
        P0 = ProcessorI()
        P1 = Spy()
        P2 = Spy()
        collector = Stub()

        A0 = M1(1)
        B0 = M1(5)

        # when
        P0.init(1, 1, P2, P1, 2, collector)
        P0.injectFirst(A0, 0)
        P0.injectSecond(B0, 0)

        # then
        assert_that(P1.injectFirst, called().async(1).with_args(A0, 1, ANY_ARG))
        assert_that(P2.injectSecond, called().async(1).with_args(B0, 1, ANY_ARG))

    def assert_collector_called(self, collector, *args):
        assert_that(collector.injectSubmatrix,
                    called().with_args(*args))

    def test_2x2_processors_1x1_blocks_with_collector_injection(self):
        '''
        A = (1, 2,
             3, 4)

        B = (5, 6,
             7, 8)
        '''
        nprocs = 4

        # given
        P = [ProcessorI() for i in range(nprocs)]
        collector = Spy(Cannon.Collector)

        # by-hand shift and split
        A0 = M1(1)
        A1 = M1(2)
        A2 = M1(4)
        A3 = M1(3)

        B0 = M1(5)
        B1 = M1(8)
        B2 = M1(7)
        B3 = M1(6)

        # when
        P[0].init(0, 0, P[2], P[1], 2, collector)
        P[1].init(0, 1, P[3], P[0], 2, collector)
        P[2].init(1, 0, P[0], P[3], 2, collector)
        P[3].init(1, 1, P[1], P[2], 2, collector)

        P[0].injectFirst(A0, 0); P[0].injectSecond(B0, 0)
        P[1].injectFirst(A1, 0); P[1].injectSecond(B1, 0)
        P[2].injectFirst(A2, 0); P[2].injectSecond(B2, 0)
        P[3].injectFirst(A3, 0); P[3].injectSecond(B3, 0)

        # then
        C0 = M1(19)
        C1 = M1(22)
        C2 = M1(43)
        C3 = M1(50)

        self.assert_collector_called(collector, C0, 0, 0, anything())
        self.assert_collector_called(collector, C1, 0, 1, anything())
        self.assert_collector_called(collector, C2, 1, 0, anything())
        self.assert_collector_called(collector, C3, 1, 1, anything())

    def test_2x2_processors_2x2_blocks_with_collector_injection(self):
        '''
        A = (1,  2,  3,  4,
             5,  6,  7,  8,
             9, 10, 11, 12,