/*
 * Copyright (C) 2020 ActiveJ LLC.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.activej.reactor.net;

import io.activej.common.ApplicationSettings;
import io.activej.common.MemSize;
import io.activej.common.builder.AbstractBuilder;
import org.jetbrains.annotations.Nullable;

import java.io.IOException;
import java.nio.channels.ServerSocketChannel;

import static java.net.StandardSocketOptions.SO_RCVBUF;
import static java.net.StandardSocketOptions.SO_REUSEADDR;

/**
 * This class used to change settings for server socket. It will be applying with creating new server socket
 */
public final class ServerSocketSettings {
	public static final int DEFAULT_BACKLOG = ApplicationSettings.getInt(ServerSocketSettings.class, "backlog", 16384);
	public static final @Nullable MemSize DEFAULT_RECEIVE_BUFFER_SIZE = ApplicationSettings.getMemSize(ServerSocketSettings.class, "receiveBufferSize", null);
	public static final @Nullable Boolean DEFAULT_REUSE_ADDRESS = ApplicationSettings.getBoolean(ServerSocketSettings.class, "reuseAddress", null);

	private static final ServerSocketSettings DEFAULT_INSTANCE = builder().build();

	private int backlog = DEFAULT_BACKLOG;
	private @Nullable MemSize receiveBufferSize = DEFAULT_RECEIVE_BUFFER_SIZE;
	private @Nullable Boolean reuseAddress = DEFAULT_REUSE_ADDRESS;

	private ServerSocketSettings() {
	}

	public static ServerSocketSettings create(int backlog) {
		return builder().withBacklog(backlog).build();
	}

	public static ServerSocketSettings defaultInstance() {
		return DEFAULT_INSTANCE;
	}

	public static Builder builder() {
		return new ServerSocketSettings().new Builder();
	}

	public final class Builder extends AbstractBuilder<Builder, ServerSocketSettings> {
		private Builder() {}

		public Builder withBacklog(int backlog) {
			checkNotBuilt(this);
			ServerSocketSettings.this.backlog = backlog;
			return this;
		}

		public Builder withReceiveBufferSize(MemSize receiveBufferSize) {
			checkNotBuilt(this);
			ServerSocketSettings.this.receiveBufferSize = receiveBufferSize;
			return this;
		}

		public Builder withReuseAddress(boolean reuseAddress) {
			checkNotBuilt(this);
			ServerSocketSettings.this.reuseAddress = reuseAddress;
			return this;
		}

		@Override
		protected ServerSocketSettings doBuild() {
			return ServerSocketSettings.this;
		}
	}

	public void applySettings(ServerSocketChannel channel) throws IOException {
		if (receiveBufferSize != null) {
			channel.setOption(SO_RCVBUF, receiveBufferSize.toInt());
		}
		if (reuseAddress != null) {
			channel.setOption(SO_REUSEADDR, reuseAddress);
		}
	}

	public int getBacklog() {
		return backlog;
	}

	public @Nullable MemSize getReceiveBufferSize() {
		return receiveBufferSize;
	}

	public @Nullable Boolean getReuseAddress() {
		return reuseAddress;
	}

}
