/*
 * Copyright 2018, TeamDev. 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 io.spine.server.aggregate;

import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.annotations.Immutable;
import io.spine.core.Event;
import io.spine.core.Events;

import java.util.List;

import static com.google.common.collect.ImmutableList.copyOf;

/**
 * The list of uncommitted events of an {@link Aggregate}.
 *
 * @author Dmytro Dashenkov
 */
@Immutable
final class UncommittedEvents {

    private static final UncommittedEvents EMPTY =
            new UncommittedEvents(ImmutableList.of(), false);

    private final ImmutableList<Event> events;
    private final boolean rejection;

    private UncommittedEvents(ImmutableList<Event> events, boolean rejection) {
        this.events = events;
        this.rejection = rejection;
    }

    /**
     * @return empty list of events
     */
    static UncommittedEvents ofNone() {
        return EMPTY;
    }

    /**
     * Tells whether or not this {@code UncommittedEvents} list is empty.
     *
     * @return {@code true} if the list is not empty, {@code false} otherwise
     */
    boolean nonEmpty() {
        return !events.isEmpty() || rejection;
    }

    /**
     * Obtains the list of events.
     *
     * <p>The returned list DOES NOT contain rejection events.
     *
     * @return the list of non-rejection uncommitted events
     */
    List<Event> list() {
        return copyOf(events);
    }

    /**
     * Creates a new instance of {@code UncommittedEvents} with this {@link #list() list} appended
     * with the given {@code events}.
     *
     * <p>If this {@code UncommittedEvents} list is {@link #nonEmpty()} or the given
     * {@code Iterable} is not empty, then the resulting instance is {@link #nonEmpty()} as well.
     *
     * @param events the events to append
     * @return new {@code UncommittedEvents} instance
     */
    UncommittedEvents append(Iterable<Event> events) {
        ImmutableList<Event> newEvents = copyOf(events);
        boolean rejection = rejection(newEvents);
        ImmutableList.Builder<Event> newList = ImmutableList.builder();
        newList.addAll(list());
        if (!rejection) {
            newList.addAll(newEvents);
        }
        boolean newRejection = rejection || this.rejection;
        return new UncommittedEvents(newList.build(), newRejection);
    }

    private static boolean rejection(List<Event> events) {
        boolean singleEvent = events.size() == 1;
        boolean result = singleEvent && Events.isRejection(events.get(0));
        return result;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        UncommittedEvents events1 = (UncommittedEvents) o;
        return rejection == events1.rejection &&
                Objects.equal(events, events1.events);
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(events, rejection);
    }

    @SuppressWarnings("DuplicateStringLiteralInspection") // Common terms used all over the core.
    @Override
    public String toString() {
        return MoreObjects.toStringHelper(this)
                          .add("events", events)
                          .add("rejection", rejection)
                          .toString();
    }
}

@@ -1255,37 +1255,8 @@
 ent;
-%0Aimport io.spine.core.Events;
 %0A%0Aim
@@ -1589,15 +1589,8 @@
 of()
-, false
 );%0A%0A
@@ -1639,45 +1639,8 @@
 nts;
-%0A    private final boolean rejection;
 %0A%0A  
@@ -1698,27 +1698,8 @@
 ents
-, boolean rejection
 ) %7B%0A
@@ -1732,44 +1732,8 @@
 ts;%0A
-        this.rejection = rejection;%0A
     
@@ -2092,21 +2092,8 @@
 ty()
- %7C%7C rejection
 ;%0A  
@@ -2151,78 +2151,8 @@
   *%0A
-     * %3Cp%3EThe returned list DOES NOT contain rejection events.%0A     *%0A
     
@@ -2178,22 +2178,8 @@
  of 
-non-rejection 
 unco
@@ -2826,58 +2826,8 @@
 s);%0A
-        boolean rejection = rejection(newEvents);%0A
     
@@ -2847,16 +2847,8 @@
 List
-.Builder
 %3CEve
@@ -2870,25 +2870,49 @@
 mmutableList
-.
+%0A                .%3CEvent%3E
 builder();%0A 
@@ -2908,26 +2908,26 @@
 uilder()
-;
 %0A
+ 
         
 newList.
@@ -2910,39 +2910,39 @@
 lder()%0A         
-newList
+       
 .addAll(list());
@@ -2940,17 +2940,16 @@
 (list())
-;
 %0A       
@@ -2953,45 +2953,16 @@
     
-if (!rejection) %7B%0A            newList
+        
 .add
@@ -2971,25 +2971,24 @@
 l(newEvents)
-;
 %0A        %7D%0A 
@@ -2984,26 +2984,24 @@
         
-%7D%0A
         
 boolean 
@@ -2996,341 +2996,63 @@
     
-boolean newRejection = rejection %7C%7C this.rejection;%0A        return new UncommittedEvents(newList.build(), newRejection);%0A    %7D%0A%0A    private static boolean rejection(List%3CEvent%3E events) %7B%0A        boolean singleEvent = events.size() == 1;%0A        boolean result = singleEvent && Events.isRejection(events.get(0));%0A        return result
+.build();%0A        return new UncommittedEvents(newList)
 ;%0A  
@@ -3337,58 +3337,8 @@
 urn 
-rejection == events1.rejection &&%0A                
 Obje
@@ -3459,27 +3459,16 @@
 e(events
-, rejection
 );%0A    %7D
@@ -3713,63 +3713,8 @@
 ts)%0A
-                          .add(%22rejection%22, rejection)%0A
     

Remove the concept of `rejection` from `UncommittedEvents`.