/*
 * Copyright 2016, TeamDev Ltd. All rights reserved.
 *
 * Redistribution and use in source and/or binary forms, with or without
 * modification, must retain the above copyright notice and the following
 * disclaimer.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.spine3.server.projection;

import com.google.common.annotations.VisibleForTesting;
import com.google.protobuf.StringValue;
import com.google.protobuf.UInt32Value;
import org.junit.Before;
import org.junit.Test;
import org.spine3.base.EventContext;
import org.spine3.server.event.Subscribe;

import static org.junit.Assert.assertTrue;
import static org.spine3.protobuf.Values.newStringValue;

@SuppressWarnings("InstanceMethodNamingConvention")
public class ProjectionShould {

    private static class TestProjection extends Projection<Integer, StringValue> {

        protected TestProjection(Integer id) {
            super(id);
        }

        @Subscribe
        public void on(StringValue event, EventContext ignored) {
            final StringValue newSate = createNewState("stringState", event.getValue());
            incrementState(newSate);
        }

        @Subscribe
        public void on(UInt32Value event, EventContext ignored) {
            final StringValue newSate = createNewState("integerState", String.valueOf(event.getValue()));
            incrementState(newSate);
        }

        private StringValue createNewState(String type, String value) {
            final String currentState = getState().getValue();
            final String result = currentState + (currentState.length() > 0 ? " + " : "") +
                    type + '(' + value + ')' + System.lineSeparator();
            return newStringValue(result);
        }

        /**
         * We expose this method to be called directly in {@link #setUp()}.
         * Normally this method would be called by a repository upon creation of a new instance.
         */
        @Override
        @VisibleForTesting
        protected void setDefault() {
            super.setDefault();
        }
    }

    private TestProjection test;

    @Before
    public void setUp() {
        test = new TestProjection(1);
        test.setDefault();
    }

    @Test
    public void handle_events() {
        final String stringValue = "something new";
        test.handle(newStringValue(stringValue), EventContext.getDefaultInstance());
        assertTrue(test.getState().getValue().contains(stringValue));

        final Integer integerValue = 1024;
        test.handle(UInt32Value.newBuilder().setValue(integerValue).build(), EventContext.getDefaultInstance());
        assertTrue(test.getState().getValue().contains(String.valueOf(integerValue)));
    }
}

@@ -1078,37 +1078,28 @@
 mon.
-annotations.VisibleForTesting
+collect.ImmutableSet
 ;%0Aim
@@ -1123,22 +1123,20 @@
 rotobuf.
-String
+Bool
 Value;%0Ai
@@ -1161,17 +1161,16 @@
 rotobuf.
-U
 Int32Val
@@ -1169,24 +1169,100 @@
 Int32Value;%0A
+import com.google.protobuf.Message;%0Aimport com.google.protobuf.StringValue;%0A
 import org.j
@@ -1416,24 +1416,133 @@
 assertTrue;%0A
+import static org.spine3.base.Identifiers.newUuid;%0Aimport static org.spine3.protobuf.Values.newIntegerValue;%0A
 import stati
@@ -1688,62 +1688,1212 @@
 ate 
-static class TestProjection extends Projection%3CInteger
+TestProjection projection;%0A%0A    @Before%0A    public void setUp() %7B%0A        projection = new TestProjection(newUuid());%0A    %7D%0A%0A    @Test%0A    public void handle_events() %7B%0A        final String stringValue = newUuid();%0A        projection.handle(newStringValue(stringValue), EventContext.getDefaultInstance());%0A        assertTrue(projection.getState()%0A                             .getValue()%0A                             .contains(stringValue));%0A%0A        final Integer integerValue = 1024;%0A        projection.handle(newIntegerValue(integerValue), EventContext.getDefaultInstance());%0A        assertTrue(projection.getState()%0A                             .getValue()%0A                             .contains(String.valueOf(integerValue)));%0A    %7D%0A%0A    @Test(expected = IllegalStateException.class)%0A    public void throw_exception_if_no_handler_for_event() %7B%0A        projection.handle(BoolValue.getDefaultInstance(), EventContext.getDefaultInstance());%0A    %7D%0A%0A    @Test%0A    public void return_event_classes_which_it_handles() %7B%0A        final ImmutableSet%3CClass%3C? extends Message%3E%3E classes = Projection.getEventClasses(TestProjection.class);%0A%0A    %7D%0A%0A    private static class TestProjection extends Projection%3CString
 , St
@@ -2939,23 +2939,22 @@
 jection(
-Integer
+String
  id) %7B%0A 
@@ -3250,17 +3250,16 @@
 void on(
-U
 Int32Val
@@ -3266,38 +3266,16 @@
 ue event
-, EventContext ignored
 ) %7B%0A    
@@ -3778,977 +3778,8 @@
    %7D
-%0A%0A        /**%0A         * We expose this method to be called directly in %7B@link #setUp()%7D.%0A         * Normally this method would be called by a repository upon creation of a new instance.%0A         */%0A        @Override%0A        @VisibleForTesting%0A        protected void setDefault() %7B%0A            super.setDefault();%0A        %7D%0A    %7D%0A%0A    private TestProjection test;%0A%0A    @Before%0A    public void setUp() %7B%0A        test = new TestProjection(1);%0A        test.setDefault();%0A    %7D%0A%0A    @Test%0A    public void handle_events() %7B%0A        final String stringValue = %22something new%22;%0A        test.handle(newStringValue(stringValue), EventContext.getDefaultInstance());%0A        assertTrue(test.getState().getValue().contains(stringValue));%0A%0A        final Integer integerValue = 1024;%0A        test.handle(UInt32Value.newBuilder().setValue(integerValue).build(), EventContext.getDefaultInstance());%0A        assertTrue(test.getState().getValue().contains(String.valueOf(integerValue)));
 %0A   

Add projection tests.