/*
 * Copyright 2017, 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 io.spine.server.event;

import com.google.protobuf.Timestamp;
import io.spine.annotation.Internal;
import io.spine.core.Event;
import io.spine.core.EventContext;
import io.spine.core.EventEnvelope;
import io.spine.core.EventId;
import io.spine.core.Events;
import io.spine.core.RejectionContext;
import io.spine.server.entity.AbstractEntity;
import io.spine.server.entity.storage.Column;
import io.spine.type.TypeName;

import javax.annotation.Nullable;
import java.util.Comparator;

import static io.spine.validate.Validate.isDefault;

/**
 * Stores an event.
 *
 * @author Alexander Yevsyukov
 * @author Dmytro Dashenkov
 */
@Internal
public class EEntity extends AbstractEntity<EventId, Event> {

    /**
     * The name of the entity column representing the time, when the event was fired.
     *
     * @see #getCreated()
     */
    static final String CREATED_TIME_COLUMN = "created";

    /**
     * The name of the entity column representing the Protobuf type name of the event.
     *
     * <p>For example, an Event of type {@code io.spine.test.TaskAdded} whose definition
     * is enclosed in the {@code spine.test} Protobuf package would have this entity column
     * equal to {@code "spine.test.TaskAdded"}.
     *
     * @see #getType()
     */
    static final String TYPE_COLUMN = "type";

    /** Cached value of the event message type name. */
    @Nullable
    private TypeName typeName;

    /**
     * Compares event entities by timestamps of events.
     */
    private static final Comparator<EEntity> comparator = new Comparator<EEntity>() {
        @Override
        public int compare(EEntity e1, EEntity e2) {
            final Event event1 = e1.getState();
            final Event event2 = e2.getState();
            final int result = Events.eventComparator()
                                     .compare(event1, event2);
            return result;
        }
    };

    EEntity(EventId id) {
        super(id);
    }

    EEntity(Event event) {
        this(event.getId());
        final Event compactedEvent = compact(event);
        updateState(compactedEvent);
    }

    /**
     * Returns comparator which sorts event entities chronologically.
     */
    static Comparator<EEntity> comparator() {
        return comparator;
    }

    /**
     * Retrieves the time of the event occurrence.
     *
     * <p>This method represents an entity column {@code created}.
     *
     * @return the time when the underlying event was fired
     * @see #CREATED_TIME_COLUMN
     */
    @Column
    public Timestamp getCreated() {
        return getState().getContext()
                         .getTimestamp();
    }

    /**
     * Retrieves the Protobuf type name of the enclosed event.
     *
     * <p>This method represents an entity column {@link TypeName}.
     *
     * @return the {@link TypeName} value of the event represented by this entity
     * @see #TYPE_COLUMN
     */
    @Column
    public String getType() {
        if (typeName == null) {
            typeName = EventEnvelope.of(getState())
                                    .getTypeName();
        }
        return typeName.value();
    }

    /**
     * Obtains the compacted version of the event.
     *
     * <p>A compacted version doesn't contain:
     * <ul>
     *     <li>the enrichment from the event context</li>
     *     <li>the enrichment from the origin</li>
     *     <li>nested origins if the origin is {@link EventContext}</li>
     * </ul>
     *
     * @param event the event to compact
     * @return the compacted event
     */
    private static Event compact(Event event) {
        final EventContext context = event.getContext();
        final EventContext.Builder resultContext = context.toBuilder()
                                                          .clearEnrichment();
        final EventContext originEventContext = context.getEventContext();
        if (!isDefault(originEventContext)) {
            resultContext.setEventContext(originEventContext.toBuilder()
                                                            .clearOrigin()
                                                            .clearEnrichment());
        }
        final RejectionContext originRejectionContext = context.getRejectionContext();
        if (!isDefault(originRejectionContext)) {
            resultContext.setRejectionContext(originRejectionContext.toBuilder()
                                                                    .clearEnrichment());
        }
        return event.toBuilder()
                    .setContext(resultContext)
                    .build();
    }
}

@@ -1279,47 +1279,8 @@
 ts;%0A
-import io.spine.core.RejectionContext;%0A
 impo
@@ -1490,35 +1490,48 @@
 ine.
-validate.Validate.isDefault
+util.Exceptions.newIllegalStateException
 ;%0A%0A/
@@ -4813,121 +4813,85 @@
     
-final EventContext originEventContext = context.getEventContext();%0A        if (!isDefault(originEventContext)) %7B%0A
+switch (resultContext.getOriginCase()) %7B%0A            case EVENT_CONTEXT:%0A    
     
@@ -4928,22 +4928,27 @@
 Context(
-origin
+context.get
 EventCon
@@ -4947,24 +4947,80 @@
 EventContext
+()%0A                                                     
 .toBuilder()
@@ -5012,39 +5012,32 @@
    .toBuilder()%0A
-       
                 
@@ -5133,39 +5133,32 @@
                 
-       
 .clearEnrichment
@@ -5174,47 +5174,92 @@
     
-%7D%0A        final RejectionContext origin
+        break;%0A            case REJECTION_CONTEXT:%0A                resultContext.set
 Reje
@@ -5266,27 +5266,25 @@
 ctionContext
- = 
+(
 context.getR
@@ -5292,33 +5292,32 @@
 jectionContext()
-;
 %0A        if (!is
@@ -5313,130 +5313,147 @@
     
-if (!isDefault(originRejectionContext)) %7B%0A            resultContext.setRejectionContext(originRejectionContext.toBuilder()
+                                                 .toBuilder()%0A                                                         .clearEnrichment());
 %0A   
@@ -5457,32 +5457,39 @@
                 
+break;%0A
                 
@@ -5488,66 +5488,202 @@
     
-                                        .clearEnrichment()
+case COMMAND_CONTEXT:%0A                // Does nothing.%0A                break;%0A            case ORIGIN_NOT_SET:%0A                throw newIllegalStateException(%22EventContext.origin should be set.%22
 );%0A 

Replace `if` statements by `switch`