// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

part of dart.io;

// Constants used when working with native ports.
// These must match the constants in runtime/bin/dartutils.h class CObject.
const int _successResponse = 0;
const int _illegalArgumentResponse = 1;
const int _osErrorResponse = 2;
const int _fileClosedResponse = 3;

const int _errorResponseErrorType = 0;
const int _osErrorResponseErrorCode = 1;
const int _osErrorResponseMessage = 2;

// POSIX error codes.
// See https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html
const _ePerm = 1;
const _eNoEnt = 2;
const _eAccess = 13;
const _eExist = 17;

// Windows error codes.
// See https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-
const _errorFileNotFound = 2;
const _errorPathNotFound = 3;
const _errorAccessDenied = 5;
const _errorInvalidDrive = 15;
const _errorCurrentDirectory = 16;
const _errorNoMoreFiles = 18;
const _errorWriteProtect = 19;
const _errorBadLength = 24;
const _errorSharingViolation = 32;
const _errorLockViolation = 33;
const _errorBadNetpath = 53;
const _errorNetworkAccessDenied = 65;
const _errorBadNetName = 67;
const _errorFileExists = 80;
const _errorDriveLocked = 108;
const _errorInvalidName = 123;
const _errorBadPathName = 161;
const _errorAlreadyExists = 183;
const _errorFilenameExedRange = 206;

/// If the [response] is an error, throws an [Exception] or an [Error].
void _checkForErrorResponse(Object? response, String message, String path) {
  if (response is List<Object?> && response[0] != _successResponse) {
    switch (response[_errorResponseErrorType]) {
      case _illegalArgumentResponse:
        throw ArgumentError("$message: $path");
      case _osErrorResponse:
        var err = OSError(
          response[_osErrorResponseMessage] as String,
          response[_osErrorResponseErrorCode] as int,
        );
        throw FileSystemException._fromOSError(err, message, path);
      case _fileClosedResponse:
        throw FileSystemException("File closed", path);
      default:
        throw AssertionError("Unknown error");
    }
  }
}

/// Base class for all IO related exceptions.
abstract class IOException implements Exception {
  String toString() => "IOException";
}

/// An [Exception] holding information about an error from the
/// operating system.
@pragma("vm:entry-point")
class OSError implements Exception {
  /// Constant used to indicate that no OS error code is available.
  static const int noErrorCode = -1;

  /// Error message supplied by the operating system. This will be empty if no
  /// message is associated with the error.
  final String message;

  /// Error code supplied by the operating system.
  ///
  /// Will have the value [OSError.noErrorCode] if there is no error code
  /// associated with the error.
  final int errorCode;

  /// Creates an OSError object from a message and an errorCode.
  @pragma("vm:entry-point")
  const OSError([this.message = "", this.errorCode = noErrorCode]);

  /// Converts an OSError object to a string representation.
  String toString() {
    StringBuffer sb = StringBuffer();
    sb.write("OS Error");
    if (message.isNotEmpty) {
      sb
        ..write(": ")
        ..write(message);
      if (errorCode != noErrorCode) {
        sb
          ..write(", errno = ")
          ..write(errorCode.toString());
      }
    } else if (errorCode != noErrorCode) {
      sb
        ..write(": errno = ")
        ..write(errorCode.toString());
    }
    return sb.toString();
  }
}

// Object for holding a buffer and an offset.
class _BufferAndStart {
  List<int> buffer;
  int start;
  _BufferAndStart(this.buffer, this.start);
}

// Ensure that the input List can be serialized through a native port.
_BufferAndStart _ensureFastAndSerializableByteData(
  List<int> buffer,
  int start,
  int end,
) {
  if ((buffer is Uint8List) && (buffer.buffer.lengthInBytes == buffer.length)) {
    // Send typed data directly, unless it is a partial view, in which case we
    // would rather copy than drag in the potentially much large backing store.
    // See issue 50206.
    return _BufferAndStart(buffer, start);
  }
  int length = end - start;
  var newBuffer = Uint8List(length);
  newBuffer.setRange(0, length, buffer, start);
  return _BufferAndStart(newBuffer, 0);
}

class _IOCrypto {
  external static Uint8List getRandomBytes(int count);
}
