/** @file
 *
 *  A brief file description
 *
 *  @section license License
 *
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you 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.
 */

#include "QUICStreamState.h"
#include "tscore/ink_assert.h"

// ---------QUICReceiveStreamState -----------

bool
QUICReceiveStreamStateMachine::is_allowed_to_send(const QUICFrame &frame) const
{
  return this->is_allowed_to_send(frame.type());
}

bool
QUICReceiveStreamStateMachine::is_allowed_to_send(QUICFrameType type) const
{
  if (type != QUICFrameType::STOP_SENDING && type != QUICFrameType::MAX_STREAM_DATA) {
    return false;
  }

  QUICReceiveStreamState state = this->get();
  // The receiver only sends MAX_STREAM_DATA in the "Recv" state.
  if (type == QUICFrameType::MAX_STREAM_DATA && state == QUICReceiveStreamState::Recv) {
    return true;
  }

  // A receiver can send STOP_SENDING in any state where it has not received a RESET_STREAM frame; that is states other than "Reset
  // Recvd" or "Reset Read".
  if (type == QUICFrameType::STOP_SENDING && state != QUICReceiveStreamState::ResetRecvd &&
      state != QUICReceiveStreamState::ResetRead) {
    return true;
  }

  return false;
}

bool
QUICReceiveStreamStateMachine::is_allowed_to_receive(const QUICFrame &frame) const
{
  return this->is_allowed_to_receive(frame.type());
}

bool
QUICReceiveStreamStateMachine::is_allowed_to_receive(QUICFrameType type) const
{
  // always allow receive these frames.
  if (type == QUICFrameType::STREAM || type == QUICFrameType::STREAM_DATA_BLOCKED || type == QUICFrameType::RESET_STREAM) {
    return true;
  }

  return false;
}

bool
QUICReceiveStreamStateMachine::update_with_sending_frame(const QUICFrame &frame)
{
  return false;
}

bool
QUICReceiveStreamStateMachine::update_with_receiving_frame(const QUICFrame &frame)
{
  bool state_changed = false;
  // The receiving part of a stream initiated by a peer (types 1 and 3 for a client, or 0 and 2 for a server) is created when the
  // first STREAM, STREAM_DATA_BLOCKED, or RESET_STREAM is received for that stream.
  QUICReceiveStreamState state = this->get();
  QUICFrameType type           = frame.type();

  if (state == QUICReceiveStreamState::Init &&
      (type == QUICFrameType::STREAM || type == QUICFrameType::STREAM_DATA_BLOCKED || type == QUICFrameType::RESET_STREAM)) {
    state_changed |= this->_set_state(QUICReceiveStreamState::Recv);
  }

  switch (this->get()) {
  case QUICReceiveStreamState::Recv:
    if (type == QUICFrameType::STREAM) {
      if (static_cast<const QUICStreamFrame &>(frame).has_fin_flag()) {
        state_changed |= this->_set_state(QUICReceiveStreamState::SizeKnown);
        if (this->_in_progress->is_transfer_complete()) {
          state_changed |= this->_set_state(QUICReceiveStreamState::DataRecvd);
        }
      }
    } else if (type == QUICFrameType::RESET_STREAM) {
      state_changed |= this->_set_state(QUICReceiveStreamState::ResetRecvd);
    }
    break;
  case QUICReceiveStreamState::SizeKnown:
    if (type == QUICFrameType::STREAM && this->_in_progress->is_transfer_complete()) {
      state_changed |= this->_set_state(QUICReceiveStreamState::DataRecvd);
    } else if (type == QUICFrameType::RESET_STREAM) {
      state_changed |= this->_set_state(QUICReceiveStreamState::ResetRecvd);
    }
    break;
  case QUICReceiveStreamState::DataRecvd:
    if (type == QUICFrameType::STREAM && this->_in_progress->is_transfer_complete()) {
      state_changed |= this->_set_state(QUICReceiveStreamState::ResetRecvd);
    }
    break;
  case QUICReceiveStreamState::Init:
  case QUICReceiveStreamState::ResetRecvd:
  case QUICReceiveStreamState::DataRead:
  case QUICReceiveStreamState::ResetRead:
    break;
  default:
    ink_assert(!"Unknown state");
    break;
  }
  return state_changed;
}

bool
QUICReceiveStreamStateMachine::update_on_read()
{
  if (this->_in_progress->is_transfer_complete()) {
    return this->_set_state(QUICReceiveStreamState::DataRead);
  }
  return false;
}

bool
QUICReceiveStreamStateMachine::update_on_eos()
{
  return this->_set_state(QUICReceiveStreamState::ResetRead);
}

bool
QUICReceiveStreamStateMachine::update(const QUICSendStreamState state)
{
  // The receiving part of a stream enters the "Recv" state when the sending part of a bidirectional stream initiated by the
  // endpoint (type 0 for a client, type 1 for a server) enters the "Ready" state.
  switch (this->get()) {
  case QUICReceiveStreamState::Init:
    if (state == QUICSendStreamState::Ready) {
      return this->_set_state(QUICReceiveStreamState::Recv);
    }
    break;
  default:
    break;
  }

  return false;
}

// ---------- QUICSendStreamState -------------

bool
QUICSendStreamStateMachine::is_allowed_to_send(const QUICFrame &frame) const
{
  return this->is_allowed_to_send(frame.type());
}

bool
QUICSendStreamStateMachine::is_allowed_to_send(QUICFrameType type) const
{
  if (type != QUICFrameType::STREAM && type != QUICFrameType::STREAM_DATA_BLOCKED && type != QUICFrameType::RESET_STREAM) {
    return false;
  }

  switch (this->get()) {
  case QUICSendStreamState::Ready:
    if (type == QUICFrameType::STREAM || type == QUICFrameType::STREAM_DATA_BLOCKED || type == QUICFrameType::RESET_STREAM) {
      return true;
    }
    break;
  case QUICSendStreamState::Send:
    if (type == QUICFrameType::STREAM || type == QUICFrameType::STREAM_DATA_BLOCKED || type == QUICFrameType::RESET_STREAM) {
      return true;
    }
    break;
  case QUICSendStreamState::DataSent:
    if (type == QUICFrameType::RESET_STREAM) {
      return true;
    }
    break;
  // A sender MUST NOT send any of these frames from a terminal state ("Data Recvd" or "Reset Recvd").
  case QUICSendStreamState::DataRecvd:
  case QUICSendStreamState::ResetRecvd:
    break;
  // A sender MUST NOT send STREAM or STREAM_DATA_BLOCKED after sending a RESET_STREAM; that is, in the terminal states and in the
  // "Reset Sent" state.
  case QUICSendStreamState::ResetSent:
    if (type == QUICFrameType::RESET_STREAM) {
      return true;
    }
    break;
  default:
    ink_assert("This shouldn't be happen");
    break;
  }

  return false;
}

bool
QUICSendStreamStateMachine::is_allowed_to_receive(const QUICFrame &frame) const
{
  return this->is_allowed_to_receive(frame.type());
}

bool
QUICSendStreamStateMachine::is_allowed_to_receive(QUICFrameType type) const
{
  if (type != QUICFrameType::STOP_SENDING && type != QUICFrameType::MAX_STREAM_DATA) {
    return false;
  }

  // A sender could receive either of these two frames(MAX_STREAM_DATA and STOP_SENDING) in any state as a result of delayed
  // delivery of packets.
  // PS: Because we need to reply a RESET_STREAM frame. STOP_SENDING frame is accepted in all states. But we
  // don't need to do anything for MAX_STREAM_DATA frame when we are in terminal state.
  if (type == QUICFrameType::STOP_SENDING) {
    return true;
  }

  switch (this->get()) {
  case QUICSendStreamState::Ready:
  case QUICSendStreamState::Send:
    if (type == QUICFrameType::MAX_STREAM_DATA) {
      return true;
    }
    break;
  // "MAX_STREAM_DATA frames might be received until the peer receives the final stream offset. The endpoint can safely ignore
  // any MAX_STREAM_DATA frames it receives from its peer for a stream in this state."
  case QUICSendStreamState::DataSent:
  case QUICSendStreamState::ResetSent:
  case QUICSendStreamState::DataRecvd:
  case QUICSendStreamState::ResetRecvd:
    if (type == QUICFrameType::MAX_STREAM_DATA) {
      return true;
    }
    break;
  default:
    break;
  }

  return false;
}

bool
QUICSendStreamStateMachine::update_with_sending_frame(const QUICFrame &frame)
{
  bool state_changed        = false;
  QUICSendStreamState state = this->get();
  QUICFrameType type        = frame.type();
  if (state == QUICSendStreamState::Ready &&
      (type == QUICFrameType::STREAM || type == QUICFrameType::STREAM_DATA_BLOCKED || type == QUICFrameType::RESET_STREAM)) {
    state_changed |= this->_set_state(QUICSendStreamState::Send);
  }

  switch (this->get()) {
  case QUICSendStreamState::Send:
    if (type == QUICFrameType::STREAM) {
      if (static_cast<const QUICStreamFrame &>(frame).has_fin_flag()) {
        state_changed |= this->_set_state(QUICSendStreamState::DataSent);
      }
    } else if (type == QUICFrameType::RESET_STREAM) {
      state_changed |= this->_set_state(QUICSendStreamState::ResetSent);
    }
    break;
  case QUICSendStreamState::DataSent:
    if (type == QUICFrameType::RESET_STREAM) {
      state_changed |= this->_set_state(QUICSendStreamState::ResetSent);
    }
    break;
  case QUICSendStreamState::Init:
  case QUICSendStreamState::Ready:
  case QUICSendStreamState::DataRecvd:
  case QUICSendStreamState::ResetSent:
  case QUICSendStreamState::ResetRecvd:
    break;
  default:
    ink_assert(!"Unknown state");
    break;
  }
  return state_changed;
}

bool
QUICSendStreamStateMachine::update_with_receiving_frame(const QUICFrame &frame)
{
  return false;
}

bool
QUICSendStreamStateMachine::update_on_ack()
{
  if (this->_out_progress->is_transfer_complete()) {
    return this->_set_state(QUICSendStreamState::DataRecvd);
  } else if (this->_out_progress->is_cancelled()) {
    return this->_set_state(QUICSendStreamState::ResetRecvd);
  }
  return false;
}

bool
QUICSendStreamStateMachine::update(const QUICReceiveStreamState state)
{
  bool state_changed = false;
  // The sending part of a bidirectional stream initiated by a peer (type 0 for a server, type 1 for a client) enters the "Ready"
  // state then immediately transitions to the "Send" state if the receiving part enters the "Recv" state (Section 3.2).
  switch (this->get()) {
  case QUICSendStreamState::Ready:
    if (state == QUICReceiveStreamState::Recv) {
      state_changed |= this->_set_state(QUICSendStreamState::Send);
    }
    break;
  default:
    break;
  }
  return state_changed;
}

// ---------QUICBidirectionalStreamState -----------

QUICBidirectionalStreamState
QUICBidirectionalStreamStateMachine::get() const
{
  QUICSendStreamState s_state    = this->_send_stream_state.get();
  QUICReceiveStreamState r_state = this->_recv_stream_state.get();

  if (s_state == QUICSendStreamState::Ready || r_state == QUICReceiveStreamState::Init) {
    return QUICBidirectionalStreamState::Idle;
  } else if (s_state == QUICSendStreamState::Ready || s_state == QUICSendStreamState::Send ||
             s_state == QUICSendStreamState::DataSent) {
    if (r_state == QUICReceiveStreamState::Recv || r_state == QUICReceiveStreamState::SizeKnown) {
      return QUICBidirectionalStreamState::Open;
    } else if (r_state == QUICReceiveStreamState::DataRecvd || r_state == QUICReceiveStreamState::DataRead) {
      return QUICBidirectionalStreamState::HC_R;
    } else if (r_state == QUICReceiveStreamState::ResetRecvd || r_state == QUICReceiveStreamState::ResetRead) {
      return QUICBidirectionalStreamState::HC_R;
    } else {
      ink_assert(false);
      return QUICBidirectionalStreamState::Invalid;
    }
  } else if (s_state == QUICSendStreamState::DataRecvd) {
    if (r_state == QUICReceiveStreamState::Recv || r_state == QUICReceiveStreamState::SizeKnown) {
      return QUICBidirectionalStreamState::HC_L;
    } else if (r_state == QUICReceiveStreamState::DataRecvd || r_state == QUICReceiveStreamState::DataRead) {
      return QUICBidirectionalStreamState::Closed;
    } else if (r_state == QUICReceiveStreamState::ResetRecvd || r_state == QUICReceiveStreamState::ResetRead) {
      return QUICBidirectionalStreamState::Closed;
    } else {
      ink_assert(false);
      return QUICBidirectionalStreamState::Invalid;
    }
  } else if (s_state == QUICSendStreamState::ResetSent || s_state == QUICSendStreamState::ResetRecvd) {
    if (r_state == QUICReceiveStreamState::Recv || r_state == QUICReceiveStreamState::SizeKnown) {
      return QUICBidirectionalStreamState::HC_L;
    } else if (r_state == QUICReceiveStreamState::DataRecvd || r_state == QUICReceiveStreamState::DataRead) {
      return QUICBidirectionalStreamState::Closed;
    } else if (r_state == QUICReceiveStreamState::ResetRecvd || r_state == QUICReceiveStreamState::ResetRead) {
      return QUICBidirectionalStreamState::Closed;
    } else {
      ink_assert(false);
      return QUICBidirectionalStreamState::Invalid;
    }
  } else {
    ink_assert(false);
    return QUICBidirectionalStreamState::Invalid;
  }
}

bool
QUICBidirectionalStreamStateMachine::update_with_sending_frame(const QUICFrame &frame)
{
  bool state_changed = false;

  // The receiving part of a stream enters the "Recv" state when the sending part of a bidirectional stream initiated by the
  // endpoint (type 0 for a client, type 1 for a server) enters the "Ready" state.
  state_changed |= this->_send_stream_state.update_with_sending_frame(frame);
  // PS: It should not happen because we initialize the send side and read side together. And the SendState has the default state
  // "Ready". But to obey the specs, we do this as follow.
  if (this->_send_stream_state.get() == QUICSendStreamState::Ready &&
      this->_recv_stream_state.get() == QUICReceiveStreamState::Init) {
    state_changed |= this->_recv_stream_state.update(this->_send_stream_state.get());
  }

  return state_changed;
}

bool
QUICBidirectionalStreamStateMachine::update_with_receiving_frame(const QUICFrame &frame)
{
  bool state_changed = false;

  // The sending part of a bidirectional stream initiated by a peer (type 0 for a server, type 1 for a client) enters the "Ready"
  // state then immediately transitions to the "Send" state if the receiving part enters the "Recv" state (Section 3.2).
  state_changed |= this->_recv_stream_state.update_with_receiving_frame(frame);
  if (this->_send_stream_state.get() == QUICSendStreamState::Ready &&
      this->_recv_stream_state.get() == QUICReceiveStreamState::Recv) {
    state_changed |= this->_send_stream_state.update(this->_recv_stream_state.get());
  }

  return state_changed;
}

bool
QUICBidirectionalStreamStateMachine::update_on_ack()
{
  return this->_send_stream_state.update_on_ack();
}

bool
QUICBidirectionalStreamStateMachine::update_on_read()
{
  return this->_recv_stream_state.update_on_read();
}

bool
QUICBidirectionalStreamStateMachine::update_on_eos()
{
  return this->_recv_stream_state.update_on_eos();
}

bool
QUICBidirectionalStreamStateMachine::is_allowed_to_send(const QUICFrame &frame) const
{
  return this->is_allowed_to_send(frame.type());
}

bool
QUICBidirectionalStreamStateMachine::is_allowed_to_send(QUICFrameType type) const
{
  return this->_send_stream_state.is_allowed_to_send(type) || this->_recv_stream_state.is_allowed_to_send(type);
}

bool
QUICBidirectionalStreamStateMachine::is_allowed_to_receive(const QUICFrame &frame) const
{
  return this->is_allowed_to_receive(frame.type());
}

bool
QUICBidirectionalStreamStateMachine::is_allowed_to_receive(QUICFrameType type) const
{
  return this->_send_stream_state.is_allowed_to_receive(type) || this->_recv_stream_state.is_allowed_to_receive(type);
}
