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

import com.google.common.util.concurrent.MoreExecutors;
import com.google.protobuf.Any;

import java.util.concurrent.Executor;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;

/**
 * Delivers the latest {@link org.spine3.server.aggregate.Aggregate} states to the {@link Stand}.
 *
 * <p>Note: Unlike {@link org.spine3.server.event.EventBus} and {@link org.spine3.server.command.CommandBus}, which assume
 * many publishers and many subscribers, the funnel may have zero or more publishers (typically, instances of
 * {@link org.spine3.server.aggregate.AggregateRepository}), but the only subscriber, the instance of {@link Stand}.
 *
 * <p>In scope of a single {@link org.spine3.server.BoundedContext} there can be the only instance of {@code StandFunnel}.
 *
 * @author Alex Tymchenko
 */
public class StandFunnel {

    /**
     * The instance of {@link Stand} to deliver the {@link org.spine3.server.aggregate.Aggregate} state updates to.
     */
    private final Stand stand;

    /**
     * An {@link Executor} used for execution of data delivery methods.
     */
    private final Executor executor;


    private StandFunnel(Builder builder) {
        this.stand = builder.getStand();
        this.executor = builder.getExecutor();
    }

    /**
     * Post the state of an {@link org.spine3.server.entity.Entity} to an instance of {@link Stand}.
     *
     * <p>The state data is posted as {@link Any} to allow transferring over the network.
     *
     * @param id          the id of an entity
     * @param entityState the state of an {@code Entity}
     */
    public void post(final Object id, final Any entityState) {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                stand.update(id, entityState);
            }
        });

    }

    /**
     * Create a new {@code Builder} for {@link StandFunnel}.
     *
     * @return a new {@code Builder} instance.
     */
    public static Builder newBuilder() {
        return new Builder();
    }

    public static class Builder {

        /**
         * The target {@code Stand} to deliver the {@code Aggregate} updates to.
         */
        private Stand stand;

        /**
         * Optional {@code Executor} for delivering the data to {@code Stand}.
         *
         * <p>If not set, a default value will be set by the builder.
         */
        private Executor executor;

        public Stand getStand() {
            return stand;
        }

        /**
         * Set the {@link Stand} instance for this {@code StandFunnel}.
         *
         * <p> The value must not be null.
         *
         * <p> If this method is not used, a default value will be used.
         * // TODO:13-09-16:dmytro.dashenkov: Correct docs. No default value for Stand is used, null value leads to a fail instead.
         *
         * @param stand the instance of {@link Stand}.
         * @return {@code this} instance of {@code Builder}
         */
        public Builder setStand(Stand stand) {
            this.stand = checkNotNull(stand);
            return this;
        }

        public Executor getExecutor() {
            return executor;
        }

        // TODO:13-09-16:dmytro.dashenkov: Complete docs. Here is the place where a default value is used in case of not using the method.
        /**
         * Set the {@code Executor} instance for this {@code StandFunnel}.
         *
         * <p>The value must not be {@code null}.
         *
         * @param executor the instance of {@code Executor}.
         * @return {@code this} instance of {@code Builder}
         */
        public Builder setExecutor(Executor executor) {
            this.executor = checkNotNull(executor);
            return this;
        }

        public StandFunnel build() {
            checkState(stand != null, "Stand must be defined for the funnel");

            if (executor == null) {
                executor = MoreExecutors.directExecutor();
            }

            final StandFunnel result = new StandFunnel(this);
            return result;
        }
    }
}

@@ -3764,224 +3764,8 @@
   *%0A
-         * %3Cp%3E If this method is not used, a default value will be used.%0A         * // TODO:13-09-16:dmytro.dashenkov: Correct docs. No default value for Stand is used, null value leads to a fail instead.%0A         *%0A
     
@@ -4100,147 +4100,8 @@
  %7D%0A%0A
-        // TODO:13-09-16:dmytro.dashenkov: Complete docs. Here is the place where a default value is used in case of not using the method.%0A
     
@@ -4247,32 +4247,116 @@
 ll%7D.%0A         *%0A
+         * %3Cp%3E If this method is not used, a default value will be used.%0A         *%0A
          * @para

Correct docs.