/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "Win32SerialParityCheckStream.h"

#include "SerialLogging.h"
#include "Win32SerialOverlappedIO.h"
#include "nsError.h"
#include "nsStreamUtils.h"

namespace mozilla::dom {

NS_IMPL_ISUPPORTS(Win32SerialParityCheckStream, nsIInputStream,
                  nsIAsyncInputStream)

Win32SerialParityCheckStream::Win32SerialParityCheckStream(
    nsCOMPtr<nsIAsyncInputStream> aInner, UniqueFileHandle aCommHandle)
    : mInner(std::move(aInner)), mCommHandle(std::move(aCommHandle)) {
  MOZ_ASSERT(mCommHandle);
}

void Win32SerialParityCheckStream::CheckForParityError() {
  if (MOZ_UNLIKELY(mParityErrorLatched)) {
    return;
  }
  if (!Win32SerialOverlappedIO::SyncCheckParity(mCommHandle.get())) {
    MOZ_LOG(gWebSerialLog, LogLevel::Debug,
            ("Win32SerialParityCheckStream[%p] detected SERIAL_ERROR_PARITY",
             this));
    mParityErrorLatched = true;
  }
}

NS_IMETHODIMP Win32SerialParityCheckStream::ReadSegments(
    nsWriteSegmentFun aWriter, void* aClosure, uint32_t aCount,
    uint32_t* aResult) {
  *aResult = 0;
  if (MOZ_UNLIKELY(mParityErrorLatched)) {
    return NS_ERROR_DOM_SERIAL_PARITY_ERROR;
  }

  nsresult rv = mInner->ReadSegments(aWriter, aClosure, aCount, aResult);
  if (NS_SUCCEEDED(rv)) {
    // If there is a parity error, record it so we can return it next time.
    // We do this instead of returning the error now to avoid a confusing
    // return from ReadSegments() that would return data but also an error.
    CheckForParityError();
  }
  return rv;
}

NS_IMETHODIMP Win32SerialParityCheckStream::Read(char* aBuf, uint32_t aCount,
                                                 uint32_t* aResult) {
  return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, aResult);
}

NS_IMETHODIMP Win32SerialParityCheckStream::Available(uint64_t* aResult) {
  if (MOZ_UNLIKELY(mParityErrorLatched)) {
    return NS_ERROR_DOM_SERIAL_PARITY_ERROR;
  }
  return mInner->Available(aResult);
}

NS_IMETHODIMP Win32SerialParityCheckStream::StreamStatus() {
  if (MOZ_UNLIKELY(mParityErrorLatched)) {
    return NS_ERROR_DOM_SERIAL_PARITY_ERROR;
  }
  return mInner->StreamStatus();
}

NS_IMETHODIMP Win32SerialParityCheckStream::Close() { return mInner->Close(); }

NS_IMETHODIMP Win32SerialParityCheckStream::IsNonBlocking(bool* aResult) {
  return mInner->IsNonBlocking(aResult);
}

NS_IMETHODIMP Win32SerialParityCheckStream::CloseWithStatus(nsresult aStatus) {
  return mInner->CloseWithStatus(aStatus);
}

NS_IMETHODIMP Win32SerialParityCheckStream::AsyncWait(
    nsIInputStreamCallback* aCallback, uint32_t aFlags,
    uint32_t aRequestedCount, nsIEventTarget* aTarget) {
  // We never buffer and never return WOULD_BLOCK while a parity error is
  // latched (ReadSegments returns the error directly), so delegating the wait
  // to the inner stream always wakes the consumer at the right time.
  return mInner->AsyncWait(aCallback, aFlags, aRequestedCount, aTarget);
}

}  // namespace mozilla::dom
