/*
 * 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.event.error;

import com.google.common.collect.ImmutableMap;
import com.google.protobuf.GeneratedMessageV3;
import com.google.protobuf.Message;
import com.google.protobuf.Value;
import org.spine3.base.Error;
import org.spine3.protobuf.TypeUrl;

import java.io.Serializable;
import java.util.Map;

/**
 * A base for exceptions related to events.
 *
 * @author Alexander Litus
 */
public abstract class EventException extends RuntimeException {

    /** Use {@link GeneratedMessageV3} because it is {@link Serializable}. */
    private final GeneratedMessageV3 eventMessage;

    private final Error error;

    /**
     * Creates a new instance.
     *
     * @param messageText  an error message text
     * @param eventMessage a related event
     * @param error        an error occurred
     */
    protected EventException(String messageText, Message eventMessage, Error error) {
        super(messageText);
        //TODO:2016-08-04:alexander.yevsyukov: What do we do if the passed message isn't of type GeneratedMessageV3?
        this.eventMessage = (GeneratedMessageV3) eventMessage;
        this.error = error;
    }

    /**
     * Returns a map with an event type attribute.
     *
     * @param eventMessage an event message to get the type from
     */
    public static Map<String, Value> eventTypeAttribute(Message eventMessage) {
        final String type = TypeUrl.of(eventMessage).getTypeName();
        final Value value = Value.newBuilder()
                                 .setStringValue(type)
                                 .build();
        return ImmutableMap.of(Attribute.EVENT_TYPE_NAME, value);
    }

    /** Returns a related event. */
    public Message getEventMessage() {
        return eventMessage;
    }

    /** Returns an error occurred. */
    public Error getError() {
        return error;
    }

    /** Attribute names for event-related business failures. */
    public interface Attribute {
        String EVENT_TYPE_NAME = "eventType";
    }

    private static final long serialVersionUID = 0L;
}

@@ -1097,16 +1097,48 @@
 bleMap;%0A
+import com.google.protobuf.Any;%0A
 import c
@@ -1276,16 +1276,54 @@
 .Error;%0A
+import org.spine3.protobuf.AnyPacker;%0A
 import o
@@ -1849,16 +1849,24 @@
 ed event
+ message
 %0A     * 
@@ -2037,179 +2037,330 @@
     
-//TODO:2016-08-04:alexander.yevsyukov: What do we do if the passed message isn't of type GeneratedMessageV3?%0A        this.eventMessage = (GeneratedMessageV3) eventMessage;
+if (eventMessage instanceof GeneratedMessageV3) %7B%0A            this.eventMessage = (GeneratedMessageV3) eventMessage;%0A        %7D else %7B%0A            // In an unlikely case on encountering a message, which is not %60GeneratedMessageV3%60, wrap it into %60Any%60.%0A            this.eventMessage = AnyPacker.pack(eventMessage);%0A        %7D
 %0A   
@@ -2895,32 +2895,39 @@
 ;%0A    %7D%0A%0A    /**
+%0A     *
  Returns a relat
@@ -2934,17 +2934,30 @@
 ed event
-.
+ message.%0A    
  */%0A    
@@ -2987,24 +2987,208 @@
 Message() %7B%0A
+        if (eventMessage instanceof Any) %7B%0A            final Any any = (Any) eventMessage;%0A            Message unpacked = AnyPacker.unpack(any);%0A            return unpacked;%0A        %7D%0A
         retu
@@ -3218,16 +3218,23 @@
 %0A    /**
+%0A     *
  Returns
@@ -3252,16 +3252,21 @@
 ccurred.
+%0A    
  */%0A    
@@ -3327,16 +3327,23 @@
 %0A    /**
+%0A     *
  Attribu
@@ -3387,16 +3387,21 @@
 ailures.
+%0A    
  */%0A    

Handle passing non-`GeneratedMessageV3` instance to `EventException`.