/*
 * 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 org.spine3.server.event.enrich;

import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;

import java.util.Properties;
import java.util.Set;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.spine3.io.IoUtil.loadAllProperties;

/**
 * A map from an event enrichment Protobuf type name to the corresponding type name(s) of event(s) to enrich.
 *
 * <p>Example:
 * <p>{@code proto.type.MyEventEnrichment} - {@code proto.type.FirstEvent},{@code proto.type.SecondEvent}
 *
 * @author Alexander Litus
 */
class EventEnrichmentsMap {

    /**
     * A path to the file which contains enrichment and event Protobuf type names.
     * Is generated by Gradle during the build process.
     */
    private static final String PROPS_FILE_PATH = "enrichments.properties";

    /** A separator between event types in the `.properties` file. */
    private static final String EVENT_TYPE_SEPARATOR = ",";

    private static final ImmutableMultimap<String, String> enrichmentsMap = buildEnrichmentsMap();

    private EventEnrichmentsMap() {
    }

    /** Returns the immutable map instance. */
    static ImmutableMultimap<String, String> getInstance() {
        return enrichmentsMap;
    }

    private static ImmutableMultimap<String, String> buildEnrichmentsMap() {
        final ImmutableSet<Properties> propertiesSet = loadAllProperties(PROPS_FILE_PATH);
        final Builder builder = new Builder(propertiesSet);
        final ImmutableMultimap<String, String> result = builder.build();
        return result;
    }

    private static class Builder {

        private static final String PACKAGE_WILDCARD_INDICATOR = ".*";

        private final Iterable<Properties> properties;
        private final ImmutableMultimap.Builder<String, String> builder;

        Builder(Iterable<Properties> properties) {
            this.properties = properties;
            this.builder = ImmutableMultimap.builder();
        }

        ImmutableMultimap<String, String> build() {
            for (Properties props : this.properties) {
                put(props);
            }
            return builder.build();
        }

        private void put(Properties props) {
            final Set<String> enrichmentTypes = props.stringPropertyNames();
            for (String enrichmentType : enrichmentTypes) {
                final String eventTypesStr = props.getProperty(enrichmentType);
                final Iterable<String> eventTypes = FluentIterable.from(eventTypesStr.split(EVENT_TYPE_SEPARATOR));

                if (isPackage(eventTypesStr)) {
                    putAll(eventTypesStr, eventTypes);
                } else {
                    put(enrichmentType, eventTypes);
                }
            }
        }

        private void put(String enrichmentType, Iterable<String> eventTypes) {
            for (String eventType : eventTypes) {
                builder.put(enrichmentType, eventType);
            }
        }

        private void putAll(String packageQualifier, Iterable<String> eventTypes) {

        }

        private static boolean isPackage(String qualifier) {
            checkNotNull(qualifier);
            checkArgument(!qualifier.isEmpty());

            final int indexOfWildcardChar = qualifier.indexOf(PACKAGE_WILDCARD_INDICATOR);
            final int qualifierLength = qualifier.length();

            final boolean result = indexOfWildcardChar == (qualifierLength - PACKAGE_WILDCARD_INDICATOR.length() - 1);
            return result;
        }
    }
}

@@ -1195,24 +1195,128 @@
 mutableSet;%0A
+import org.spine3.protobuf.KnownTypes;%0Aimport org.spine3.protobuf.TypeUrl;%0A%0Aimport java.util.Collection;
 %0Aimport java
@@ -3794,24 +3794,226 @@
 EPARATOR));%0A
+                put(enrichmentType, eventTypes);%0A            %7D%0A        %7D%0A%0A        private void put(String enrichmentType, Iterable%3CString%3E eventTypes) %7B%0A            for (String eventType : eventTypes) %7B
 %0A           
@@ -4040,20 +4040,16 @@
 ventType
-sStr
 )) %7B%0A   
@@ -4073,28 +4073,29 @@
 putAll(e
-v
+nrichm
 entType
-sStr
 , eventT
@@ -4089,33 +4089,32 @@
 tType, eventType
-s
 );%0A             
@@ -4137,32 +4137,40 @@
                 
+builder.
 put(enrichmentTy
@@ -4174,33 +4174,32 @@
 tType, eventType
-s
 );%0A             
@@ -4244,32 +4244,35 @@
 private void put
+All
 (String enrichme
@@ -4283,35 +4283,28 @@
 pe, 
-Iterable%3C
 String
-%3E
  event
-Types
+sPackage
 ) %7B%0A
@@ -4320,88 +4320,124 @@
    f
-or (String eventType : eventTypes) %7B%0A                builder.put(enrichmentType,
+inal Collection%3CTypeUrl%3E eventTypes = KnownTypes.getTypesFromPackage(eventsPackage);%0A            for (TypeUrl type :
  eve
@@ -4442,32 +4442,20 @@
 ventType
-);%0A            %7D
+s) %7B
 %0A       
@@ -4451,35 +4451,32 @@
 ) %7B%0A        
-%7D%0A%0A
         
 private void
@@ -4467,41 +4467,24 @@
     
-private void putAll(
+final 
 String 
-packag
+typ
 eQua
@@ -4493,41 +4493,104 @@
 fier
-, Iterable%3CString%3E eventTypes) %7B%0A
+ = type.getTypeName();%0A                builder.put(enrichmentType, typeQualifier);%0A            %7D
 %0A   

Implement type storing through the package.