/*
 * Copyright (c) 2014, 2021 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package org.glassfish.tyrus.core;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import jakarta.websocket.Decoder;
import jakarta.websocket.Encoder;
import jakarta.websocket.Extension;
import jakarta.websocket.server.ServerEndpointConfig;

/**
 * Configuration {@link jakarta.websocket.server.ServerEndpointConfig} enhanced
 * to offer tyrus specific attributes like maxSessions.
 * Declarative way to define maxSessions is also available using
 * annotation {@link MaxSessions}.
 *
 * @author Ondrej Kosatka
 * @see MaxSessions
 */
public interface TyrusServerEndpointConfig extends ServerEndpointConfig {

    /**
     * Returns configured maximal number of open sessions.
     *
     * @return tne maximal number of open sessions.
     */
    public int getMaxSessions();

    /**
     * The TyrusServerEndpointConfig.Builder is a class used for creating
     * {@link TyrusServerEndpointConfig.Builder} objects for the purposes of
     * deploying a server endpoint.
     * <p>
     * Here are some examples:
     * <p>
     * Building a plain configuration for an endpoint with just a path.
     * <p>
     * {@code ServerEndpointConfig config = TyrusServerEndpointConfig.Builder.create(ProgrammaticEndpoint.class,
     * "/foo").build();}
     * <p>
     * Building a configuration with no subprotocols, limited number of sessions (100) and a custom configurator.
     * <pre><code>
     * ServerEndpointConfig config = TyrusServerEndpointConfig.Builder.create(ProgrammaticEndpoint.class, "/bar")
     *         .subprotocols(subprotocols)
     *         .maxSessions(100)
     *         .configurator(new MyServerConfigurator())
     *         .build();
     * </code></pre>
     *
     * @author dannycoward
     */
    public final class Builder {
        private String path;
        private Class<?> endpointClass;
        private List<String> subprotocols = Collections.emptyList();
        private List<Extension> extensions = Collections.emptyList();
        private List<Class<? extends Encoder>> encoders = Collections.emptyList();
        private List<Class<? extends Decoder>> decoders = Collections.emptyList();
        private Configurator serverEndpointConfigurator;
        private int maxSessions = 0;

        /**
         * Creates the builder with the mandatory information of the endpoint class
         * (programmatic or annotated), the relative URI or URI-template to use,
         * and with no subprotocols, extensions, encoders, decoders or custom
         * configurator.
         *
         * @param endpointClass the class of the endpoint to configure
         * @param path          The URI or URI template where the endpoint will be deployed.
         *                      A trailing "/" will be ignored and the path must begin with /.
         * @return a new instance of TyrusServerEndpointConfig.Builder .
         */
        public static Builder create(Class<?> endpointClass, String path) {
            return new Builder(endpointClass, path);
        }

        // only one way to build them
        private Builder() {

        }

        /**
         * Builds the configuration object using the current attributes
         * that have been set on this builder object.
         *
         * @return a new TyrusServerEndpointConfig object.
         */
        public TyrusServerEndpointConfig build() {

            final ServerEndpointConfig serverEndpointConfig =
                    ServerEndpointConfig.Builder.create(endpointClass, path).subprotocols(subprotocols)
                                                .extensions(extensions).encoders(encoders).decoders(decoders)
                                                .configurator(serverEndpointConfigurator).build();

            return new DefaultTyrusServerEndpointConfig(
                    serverEndpointConfig,
                    this.maxSessions
            );
        }

        private Builder(Class<?> endpointClass, String path) {
            if (endpointClass == null) {
                throw new IllegalArgumentException("endpointClass cannot be null");
            }
            this.endpointClass = endpointClass;
            if (path == null || !path.startsWith("/")) {
                throw new IllegalStateException("Path cannot be null and must begin with /");
            }
            this.path = path;
        }

        /**
         * Sets the list of encoder implementation classes for this builder.
         *
         * @param encoders the encoders.
         * @return this builder instance.
         */
        public TyrusServerEndpointConfig.Builder encoders(List<Class<? extends Encoder>> encoders) {
            this.encoders = (encoders == null) ? new ArrayList<Class<? extends Encoder>>() : encoders;
            return this;
        }

        /**
         * Sets the decoder implementation classes to use in the configuration.
         *
         * @param decoders the decoders.
         * @return this builder instance.
         */
        public TyrusServerEndpointConfig.Builder decoders(List<Class<? extends Decoder>> decoders) {
            this.decoders = (decoders == null) ? new ArrayList<Class<? extends Decoder>>() : decoders;
            return this;
        }

        /**
         * Sets the subprotocols to use in the configuration.
         *
         * @param subprotocols the subprotocols.
         * @return this builder instance.
         */
        public TyrusServerEndpointConfig.Builder subprotocols(List<String> subprotocols) {
            this.subprotocols = (subprotocols == null) ? new ArrayList<String>() : subprotocols;
            return this;
        }


        /**
         * Sets the extensions to use in the configuration.
         *
         * @param extensions the extensions to use.
         * @return this builder instance.
         */
        public TyrusServerEndpointConfig.Builder extensions(List<Extension> extensions) {
            this.extensions = (extensions == null) ? new ArrayList<Extension>() : extensions;
            return this;
        }

        /**
         * Sets the custom configurator to use on the configuration
         * object built by this builder.
         *
         * @param serverEndpointConfigurator the configurator.
         * @return this builder instance
         */
        public TyrusServerEndpointConfig.Builder configurator(Configurator serverEndpointConfigurator) {
            this.serverEndpointConfigurator = serverEndpointConfigurator;
            return this;
        }

        /**
         * Sets maximal number of open sessions.
         *
         * @param maxSessions maximal number of open session.
         * @return this builder instance.
         */
        public TyrusServerEndpointConfig.Builder maxSessions(final int maxSessions) {
            this.maxSessions = maxSessions;
            return this;
        }
    }
}
