/*
 * Copyright 2019, 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.code.proto;

import io.spine.code.proto.ref.TypeRef;
import io.spine.option.LifecycleOption;
import io.spine.option.OptionsProto;

import java.util.Optional;
import java.util.function.Function;

public final class EntityLifecycleOption extends MessageOption<LifecycleOption> {

    public EntityLifecycleOption() {
        super(OptionsProto.lifecycle);
    }

    public boolean hasLifecycle(MessageType type) {
        Optional<LifecycleOption> option = valueFrom(type.descriptor());
        return option.isPresent();
    }

    public Optional<TypeRef> archiveUpon(MessageType type) {
        Optional<TypeRef> result = optionIfPresent(type, LifecycleOption::getArchiveUpon);
        return result;
    }

    public Optional<TypeRef> deleteUpon(MessageType type) {
        Optional<TypeRef> result = optionIfPresent(type, LifecycleOption::getDeleteUpon);
        return result;
    }

    private Optional<TypeRef> optionIfPresent(MessageType type,
                                              Function<LifecycleOption, String> optionGetter) {
        Optional<LifecycleOption> option = valueFrom(type.descriptor());
        Optional<TypeRef> result =
                option.map(optionGetter)
                      .flatMap(EntityLifecycleOption::typeRefIfPresent)
                      .map(typeRef -> ensureSamePackage(typeRef, type));
        return result;
    }

    private static Optional<TypeRef> typeRefIfPresent(String fieldValue) {
        Optional<TypeRef> result =
                Optional.of(fieldValue)
                        .map(String::trim)
                        .filter(str -> !str.isEmpty())
                        .map(TypeRef::parse);
        return result;
    }

    private static TypeRef ensureSamePackage(TypeRef typeRef, MessageType enclosing) {
        PackageName packageName = PackageName.of(enclosing.descriptor());
        TypeRef result = typeRef.withPackage(packageName);
        return result;
    }
}

@@ -1218,16 +1218,75 @@
 ction;%0A%0A
+/**%0A * A parser of the %7B@link LifecycleOption%7D values.%0A */%0A
 public f
@@ -1435,32 +1435,133 @@
 ecycle);%0A    %7D%0A%0A
+    /**%0A     * Checks if a given message has any of the %7B@code lifecycle%7D options specified.%0A     */%0A
     public boole
@@ -1719,185 +1719,938 @@
     
-public Optional%3CTypeRef%3E archiveUpon(MessageType type) %7B%0A        Optional%3CTypeRef%3E result = optionIfPresent(type, LifecycleOption::getArchiveUpon);%0A        return result;%0A    %7D%0A
+/**%0A     * Obtains %7B@link LifecycleOption#getArchiveUpon()%7D archive_upon%7D option value.%0A     *%0A     * %3Cp%3EIf the %7B@code TypeRef%7D in option doesn't have package (i.e. is a raw type name), the%0A     * package of enclosing %7B@code MessageType%7D is assumed.%0A     *%0A     * %3Cp%3EIf the option is set to empty %7B@code String%7D, the method returns an empty%0A     * %7B@code Optional%7D.%0A     */%0A    public Optional%3CTypeRef%3E archiveUpon(MessageType type) %7B%0A        Optional%3CTypeRef%3E result = optionIfPresent(type, LifecycleOption::getArchiveUpon);%0A        return result;%0A    %7D%0A%0A    /**%0A     * Obtains %7B@link LifecycleOption#getDeleteUpon() delete_upon%7D option value.%0A     *%0A     * %3Cp%3EIf the %7B@code TypeRef%7D in option doesn't have package (i.e. is a raw type name), the%0A     * package of enclosing %7B@code MessageType%7D is assumed.%0A     *%0A     * %3Cp%3EIf the option is set to empty %7B@code String%7D, the method returns an empty%0A     * %7B@code Optional%7D.%0A     */
 %0A   
@@ -3245,26 +3245,27 @@
 eRef -%3E 
-ensureSame
+provideWith
 Package(
@@ -3627,32 +3627,169 @@
  result;%0A    %7D%0A%0A
+    /**%0A     * Provides the given %7B@code TypeRef%7D with the %7B@code enclosing%7D message package, if it doesn't%0A     * have its own.%0A     */%0A
     private stat
@@ -3803,18 +3803,19 @@
 Ref 
-ensureSame
+provideWith
 Pack

Add doc to `EntityLifecycleOption`