/*
 * Copyright 2019 DLR, Germany
 * 
 * SPDX-License-Identifier: EPL-1.0
 * 
 * https://rcenvironment.de/
 */

package de.rcenvironment.core.communication.uplink.network.internal;

import java.nio.charset.Charset;

import org.apache.commons.io.Charsets;

/**
 * Constants for the uplink protocol.
 *
 * @author Robert Mischke
 */
public final class UplinkProtocolConstants {

    /**
     * The version of the low-level protocol to send and expect. If this does not match, the connection can only be dropped/refused, as
     * there is no agreed way to transmit an error message.
     */
    public static final String LOW_LEVEL_PROTOCOL_VERSION = "1.0";

    /**
     * The high-level protocol version to send for compatibility checking. If this does not match, the connection can be refused gracefully,
     * as the matching low-level protocol allows to transmit an error message.
     * <p>
     * Note that currently, the server expects an exact match using this same constant; this could be extended in the future to make the
     * server side accept multiple protocol versions.
     */
    public static final String HIGH_LEVEL_PROTOCOL_VERSION = "10.0.0";

    /**
     * Handshake data key for the detailed protocol version (used for compatibility checking).
     */
    public static final String HANDSHAKE_KEY_HIGH_LEVEL_PROTOCOL_VERSION = "protocolVersion";

    /**
     * Handshake data key for the the "session qualifier"/"client id" allowing multiple logins using the same account while keeping them
     * distinguishable.
     */
    public static final String HANDSHAKE_KEY_SESSION_QUALIFIER = "sessionQualifier";

    /**
     * Handshake key for the client's namespace id (which is also the prefix to use for all destination ids of that client) assigned by the
     * relay.
     */
    public static final String HANDSHAKE_KEY_ASSIGNED_NAMESPACE_ID = "namespace";

    /**
     * A client can send this to trigger a handshake failure with the associated value used as the message.
     */
    public static final String HANDSHAKE_KEY_SIMULATE_HANDSHAKE_FAILURE = "simulateHandshakeFailure";

    /**
     * A client can send this to trigger a connection refusal with the associated value used as the message.
     */
    public static final String HANDSHAKE_KEY_SIMULATE_REFUSED_CONNECTION = "simulateRefusedConnection";

    /**
     * A client can send this to trigger a long server-side delay before sending the handshake response.
     */
    public static final String HANDSHAKE_KEY_SIMULATE_HANDSHAKE_RESPONSE_DELAY_ABOVE_TIMEOUT = "simulateHandshakeTimeout";

    /**
     * The number of characters of the login account name (starting from left) that are actually relevant for the client's identity.
     * <p>
     * SECURITY ASSESSMENT: Impersonation attacks exploiting this are currently unlikely as accounts on a relay are not created by users,
     * but manually by administrators. If this ever changes, precautions against these attacks must be taken. -- misc_ro, Sept 2019
     */
    public static final int LOGIN_ACCOUNT_NAME_SIGNIFICANT_CHARACTERS = 8;

    /**
     * The number of characters of the session qualifier/"client id" (starting from left) that are actually relevant for the client's
     * identity.
     * <p>
     * SECURITY ASSESSMENT: As this identifier can be arbitrarily chosen by the client anyway, and is only relevant to distinguish users of
     * the same, already authenticated account, this does not pose an additional risk. -- misc_ro, Sept 2019
     */
    public static final int SESSION_QUALIFIER_SIGNIFICANT_CHARACTERS = 8;

    /**
     * The total length of the (internal) destination id prefix, which is the concatenation of the above parts, each padded to maximum
     * length.
     */
    public static final int DESTINATION_ID_PREFIX_LENGTH =
        LOGIN_ACCOUNT_NAME_SIGNIFICANT_CHARACTERS + SESSION_QUALIFIER_SIGNIFICANT_CHARACTERS;

    /**
     * The character to right-pad the login name and session qualifier/"client id" (individually) before concatenating them into the
     * destination id prefix.
     */
    // TODO (p1) 10.1/11.0: preliminary; align this with SSH login and client identifier rules
    public static final char DESTINATION_ID_PREFIX_PADDING_CHARACTER = '-';

    /**
     * The character to right-pad the login name and session qualifier/"client id" (individually) before concatenating them into the
     * destination id prefix, as a String instead of a character.
     */
    public static final String DESTINATION_ID_PREFIX_PADDING_CHARACTER_AS_STRING =
        Character.toString(DESTINATION_ID_PREFIX_PADDING_CHARACTER);

    /**
     * The default "session qualifier"/"client id" value, both for usage as a GUI default, and as the default value if none is specified via
     * configuration.
     */
    public static final String SESSION_QUALIFIER_DEFAULT = "default";

    /**
     * The lowest valid message type value.
     */
    public static final int MIN_MESSAGE_TYPE_VALUE = 1;

    /**
     * The highest valid message type value.
     */
    public static final int MAX_MESSAGE_TYPE_VALUE = 127;

    /**
     * The lowest valid non-reserved message type value.
     */
    public static final int MIN_CONTENT_TYPE_VALUE = 1;

    /**
     * The highest valid non-reserved message type value.
     */
    public static final int MAX_CONTENT_TYPE_VALUE = 99;

    /**
     * The message code for the initial handshake's data exchange. Usage: C2R, R2C.
     * <p>
     * Note that this special message's code MUST be OUTSIDE the "content" message type range defined above
     */
    public static final byte MESSAGE_TYPE_HANDSHAKE = 121;

    /**
     * The message code for the "end of session" goodbye message. Usage: C2R, R2C.
     * <p>
     * Note that this special message's code MUST be OUTSIDE the "content" message type range defined above
     */
    public static final byte MESSAGE_TYPE_GOODBYE = 127;

    /**
     * The "default" message channel; this is the one for exchanging general messages, e.g. the initial connection handshake, tool
     * descriptor list updates, channel management, and session shutdowns.
     */
    public static final long DEFAULT_CHANNEL_ID = 0L;

    /**
     * A placeholder for an undefined or invalid channel id, for example when sending a failed channel creation response.
     */
    public static final long UNDEFINED_CHANNEL_ID = -1L;

    /**
     * The maximum valid length of the data byte array.
     */
    public static final int MAX_MESSAGE_BLOCK_DATA_LENGTH = 256 * 1024; // 256 kb

    /**
     * The maximum expected time between sending one's own handshake data and receiving a response. As this should be a very fast and
     * low-bandwidth operation, this interval should be fairly short even for connections outside of an organization's network.
     */
    public static final int HANDSHAKE_RESPONSE_TIMEOUT_MSEC = 2000;

    protected static final int HANDSHAKE_FORMAT_VERSION = 1;

    // note: header version "0" for development; the first release version should be "1"
    protected static final String HANDSHAKE_HEADER_STRING = "INIT v0 "; // IMPORTANT: must be padded to result in 8 UTF-8 bytes

    protected static final int HANDSHAKE_INIT_STRING_BYTE_LENGTH = 8; // must match the above

    protected static final Charset DEFAULT_CHARSET = Charsets.UTF_8;

    /**
     * The lowest valid message type value.
     */
    protected static final int MIN_TYPE_VALUE = 1;

    /**
     * The highest valid message type value.
     */
    protected static final int MAX_TYPE_VALUE = 127;

    private UplinkProtocolConstants() {}
}
