// Mostly copied from https://github.com/snapview/tungstenite-rs/blob/42b8797e8b7f39efb7d9322dc8af3e9089db4f7d/src/protocol/frame/coding.rs#L117
//
// Copyright (c) 2017 Alexey Galakhov
// Copyright (c) 2016 Jason Housley
// Dual licensed under MIT and Apache 2.0
// ---
// Copyright 2023 Divy Srivastava <dj.srivastava23@gmail.com>
//
// 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.

use self::CloseCode::*;
/// Status code used to indicate why an endpoint is closing the WebSocket connection.
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum CloseCode {
  /// Indicates a normal closure, meaning that the purpose for
  /// which the connection was established has been fulfilled.
  Normal,
  /// Indicates that an endpoint is "going away", such as a server
  /// going down or a browser having navigated away from a page.
  Away,
  /// Indicates that an endpoint is terminating the connection due
  /// to a protocol error.
  Protocol,
  /// Indicates that an endpoint is terminating the connection
  /// because it has received a type of data it cannot accept (e.g., an
  /// endpoint that understands only text data MAY send this if it
  /// receives a binary message).
  Unsupported,
  /// Indicates that no status code was included in a closing frame. This
  /// close code makes it possible to use a single method, `on_close` to
  /// handle even cases where no close code was provided.
  Status,
  /// Indicates an abnormal closure. If the abnormal closure was due to an
  /// error, this close code will not be used. Instead, the `on_error` method
  /// of the handler will be called with the error. However, if the connection
  /// is simply dropped, without an error, this close code will be sent to the
  /// handler.
  Abnormal,
  /// Indicates that an endpoint is terminating the connection
  /// because it has received data within a message that was not
  /// consistent with the type of the message (e.g., non-UTF-8 \[RFC3629\]
  /// data within a text message).
  Invalid,
  /// Indicates that an endpoint is terminating the connection
  /// because it has received a message that violates its policy.  This
  /// is a generic status code that can be returned when there is no
  /// other more suitable status code (e.g., Unsupported or Size) or if there
  /// is a need to hide specific details about the policy.
  Policy,
  /// Indicates that an endpoint is terminating the connection
  /// because it has received a message that is too big for it to
  /// process.
  Size,
  /// Indicates that an endpoint (client) is terminating the
  /// connection because it has expected the server to negotiate one or
  /// more extension, but the server didn't return them in the response
  /// message of the WebSocket handshake.  The list of extensions that
  /// are needed should be given as the reason for closing.
  /// Note that this status code is not used by the server, because it
  /// can fail the WebSocket handshake instead.
  Extension,
  /// Indicates that a server is terminating the connection because
  /// it encountered an unexpected condition that prevented it from
  /// fulfilling the request.
  Error,
  /// Indicates that the server is restarting. A client may choose to reconnect,
  /// and if it does, it should use a randomized delay of 5-30 seconds between attempts.
  Restart,
  /// Indicates that the server is overloaded and the client should either connect
  /// to a different IP (when multiple targets exist), or reconnect to the same IP
  /// when a user has performed an action.
  Again,
  #[doc(hidden)]
  Tls,
  #[doc(hidden)]
  Reserved(u16),
  #[doc(hidden)]
  Iana(u16),
  #[doc(hidden)]
  Library(u16),
  #[doc(hidden)]
  Bad(u16),
}

impl CloseCode {
  /// Check if this CloseCode is allowed.
  pub fn is_allowed(self) -> bool {
    !matches!(self, Bad(_) | Reserved(_) | Status | Abnormal | Tls)
  }
}

impl From<u16> for CloseCode {
  fn from(code: u16) -> CloseCode {
    match code {
      1000 => Normal,
      1001 => Away,
      1002 => Protocol,
      1003 => Unsupported,
      1005 => Status,
      1006 => Abnormal,
      1007 => Invalid,
      1008 => Policy,
      1009 => Size,
      1010 => Extension,
      1011 => Error,
      1012 => Restart,
      1013 => Again,
      1015 => Tls,
      1..=999 => Bad(code),
      1016..=2999 => Reserved(code),
      3000..=3999 => Iana(code),
      4000..=4999 => Library(code),
      _ => Bad(code),
    }
  }
}

impl From<CloseCode> for u16 {
  fn from(code: CloseCode) -> u16 {
    match code {
      Normal => 1000,
      Away => 1001,
      Protocol => 1002,
      Unsupported => 1003,
      Status => 1005,
      Abnormal => 1006,
      Invalid => 1007,
      Policy => 1008,
      Size => 1009,
      Extension => 1010,
      Error => 1011,
      Restart => 1012,
      Again => 1013,
      Tls => 1015,
      Reserved(code) => code,
      Iana(code) => code,
      Library(code) => code,
      Bad(code) => code,
    }
  }
}
