/*
 * Copyright 2018, 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.tools.proto;

import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
import com.google.protobuf.DescriptorProtos.FileDescriptorSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nullable;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.List;

import static com.google.common.base.Preconditions.checkNotNull;
import static io.spine.util.Exceptions.newIllegalStateException;
import static java.util.Collections.emptyList;

/**
 * A utility class which allows to obtain Protobuf file descriptors.
 *
 * @author Alexander Litus
 * @author Alex Tymchenko
 */
public class FileDescriptors {

    private static final Predicate<FileDescriptorProto> IS_NOT_GOOGLE = new IsNotGoogleProto();

    /** Prevents instantiation of this utility class. */
    private FileDescriptors() {
    }

    /**
     * Returns descriptors of all {@code .proto} files described in the descriptor set file.
     *
     * @param  descriptorSetFile
     *         the path to the file generated by the {@code protobuf-gradle-plugin} which contains
     *         descriptors of the project {@code .proto} files
     * @return a list of descriptors
     */
    public static List<FileDescriptorProto> parse(String descriptorSetFile) {
        return parseAndFilter(descriptorSetFile, Predicates.<FileDescriptorProto>alwaysTrue());
    }

    /**
     * Returns descriptors of `.proto` files described in the descriptor set file
     * which match the filter predicate.
     *
     * @param  descriptorSetFile
     *         the path to the file generated by {@code protobuf-gradle-plugin} which contains
     *         descriptors of the project {@code .proto} files
     * @param  filter
     *         a filter predicate to apply to the files
     * @return a list of descriptors
     */
    public static List<FileDescriptorProto> parseAndFilter(String descriptorSetFile,
                                                           Predicate<FileDescriptorProto> filter) {
        final File descriptorsFile = new File(descriptorSetFile);
        if (!descriptorsFile.exists()) {
            warnOnEnablingDescriptorSetGeneration();
            return emptyList();
        }

        final Logger log = log();
        if (log.isTraceEnabled()) {
            log.trace("Looking up for the proto files matching predicate {} under {}",
                      filter,
                      descriptorSetFile);
        }

        final ImmutableList.Builder<FileDescriptorProto> fileDescriptors = ImmutableList.builder();
        try (final FileInputStream fis = new FileInputStream(descriptorsFile)) {
            final FileDescriptorSet fileDescriptorSet = FileDescriptorSet.parseFrom(fis);
            for (FileDescriptorProto file : fileDescriptorSet.getFileList()) {
                if (filter.apply(file)) {
                    fileDescriptors.add(file);
                }
            }
        } catch (IOException e) {
            throw newIllegalStateException(
                    e, "Cannot get proto file descriptors. Path: %s", descriptorSetFile
            );
        }

        final ImmutableList<FileDescriptorProto> result = fileDescriptors.build();
        log.trace("Found {} files: {}", result.size(), fileDescriptors);
        return result;
    }

    private static void warnOnEnablingDescriptorSetGeneration() {
        log().warn("Please enable descriptor set generation. See: " +
            "https://github.com/google/protobuf-gradle-plugin/blob/master/README.md#generate-descriptor-set-files");
    }

    /**
     * Obtains the predicate that filters out file descriptors for types with {@code "google"}
     * in the package name.
     */
    public static Predicate<FileDescriptorProto> isNotGoogleProto() {
        return IS_NOT_GOOGLE;
    }

    /**
     * Verifies if a package of a file does not contain {@code "google"} in its path.
     */
    private static class IsNotGoogleProto implements Predicate<FileDescriptorProto> {

        private static final String GOOGLE_PACKAGE = "google";

        @Override
        public boolean apply(@Nullable FileDescriptorProto file) {
            checkNotNull(file);
            final boolean result = !file.getPackage()
                                        .contains(GOOGLE_PACKAGE);
            return result;
        }

        @Override
        public String toString() {
            return getClass().getSimpleName();
        }
    }

    private static Logger log() {
        return LogSingleton.INSTANCE.value;
    }

    private enum LogSingleton {
        INSTANCE;
        @SuppressWarnings("NonSerializableFieldInSerializableClass")
        private final Logger value = LoggerFactory.getLogger(FileDescriptors.class);
    }
}

@@ -3702,26 +3702,16 @@
 to%3E file
-Descriptor
 s = Immu
@@ -3852,26 +3852,16 @@
 Set file
-Descriptor
 Set = Fi
@@ -3940,26 +3940,16 @@
 e : file
-Descriptor
 Set.getF
@@ -4027,26 +4027,16 @@
     file
-Descriptor
 s.add(fi
@@ -4326,26 +4326,16 @@
 t = file
-Descriptor
 s.build(
@@ -4396,26 +4396,16 @@
 (), file
-Descriptor
 s);%0A    

Simplify var names