///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2002-2025, Open Design Alliance (the "Alliance").
// All rights reserved.
//
// This software and its documentation and related materials are owned by
// the Alliance. The software may only be incorporated into application
// programs owned by members of the Alliance, subject to a signed
// Membership Agreement and Supplemental Software License Agreement with the
// Alliance. The structure and organization of this software are the valuable
// trade secrets of the Alliance and its suppliers. The software is also
// protected by copyright law and international treaty provisions. Application
// programs incorporating this software must include the following statement
// with their copyright notices:
//
//   This application incorporates Open Design Alliance software pursuant to a
//   license agreement with Open Design Alliance.
//   Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance.
//   All rights reserved.
//
// By use of this software, its documentation or related materials, you
// acknowledge and accept the above terms.
///////////////////////////////////////////////////////////////////////////////

import { FetchError, error400 } from "./FetchError";

function handleXMLHttpError(xhr: XMLHttpRequest): Promise<XMLHttpRequest> {
  if (xhr.status === 0) {
    return Promise.reject(new FetchError(0, "Network error"));
  }
  if (xhr.status < 200 || xhr.status > 299) {
    switch (xhr.status) {
      case 400: {
        console.error(xhr.responseText);
        return Promise.reject(new FetchError(400, error400(xhr.responseText)));
      }
      case 500: {
        console.error(error400(xhr.responseText, xhr.responseText));
        return Promise.reject(new FetchError(500));
      }
      default: {
        return Promise.reject(new FetchError(xhr.status));
      }
    }
  }
  return Promise.resolve(xhr);
}

export function $xmlhttp(
  url: string,
  params: {
    method: "GET" | "POST" | "PUT" | "DELETE" | "HEAD";
    headers?: HeadersInit;
    body?: XMLHttpRequestBodyInit;
    uploadProgress?: (progress: number) => void;
    downloadProgress?: (progress: number) => void;
  } = { method: "GET" }
): Promise<XMLHttpRequest> {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open(params.method, url, true);
    for (const key in params.headers) {
      xhr.setRequestHeader(key, params.headers[key]);
    }
    function calcProgress(event: ProgressEvent): number {
      return event.lengthComputable ? event.loaded / event.total : 1;
    }
    xhr.upload.onprogress = (event) => params.uploadProgress && params.uploadProgress(calcProgress(event));
    xhr.onprogress = (event) => params.downloadProgress && params.downloadProgress(calcProgress(event));
    xhr.onloadend = (event) => handleXMLHttpError(event.target as XMLHttpRequest).then(resolve, reject);
    xhr.send(params.body);
  });
}
