/**
 * This file has no copyright assigned and is placed in the Public Domain.
 * This file is part of the mingw-w64 runtime package.
 * No warranty is given; refer to the file DISCLAIMER.PD within this package.
 */
#include <locale.h>
#include <stdlib.h>
#include <wchar.h>

#include "mingw-wchar.h"

size_t mbsrtowcs (
  wchar_t *wcs,
  const char **__restrict__ mbs,
  size_t count,
  mbstate_t *__restrict__ state
) {
  /* Use private `mbstate_t` if caller did not supply one */
  if (state == NULL) {
    static mbstate_t state_mbsrtowcs = {0};
    state = &state_mbsrtowcs;
  }

  mbstate_t conversion_state = *state;

  /* Total number of wide character written to `wcs` */
  size_t  wcConverted = 0;
  /* Converted wide character */
  wchar_t wc = 0;

  /* Next multibyte character to convert */
  const char *mbc = *mbs;

  /* Code page used by current locale */
  unsigned cp = ___lc_codepage_func ();

  /* Maximum character length in `cp` */
  int mb_cur_max = ___mb_cur_max_func();

  while (1) {
    const size_t length = __mingw_mbrtowc_cp (
      &wc, mbc, mb_cur_max, &conversion_state, cp, mb_cur_max
    );

    /* Conversion failed */
    if (length == (size_t) -1) {
      if (wcs != NULL) {
        *mbs = mbc;
        *state = conversion_state;
      }
      return (size_t) -1;
    }

    /* POSIX and ISO C are silent about this */
    if (wcs != NULL && count == 0) {
      return 0;
    }

    /* Store terminating L'\0' and stop */
    if (length == 0) {
      if (wcs != NULL) {
        *wcs = L'\0';
        *mbs = NULL;
      }
      break;
    }

    /* Store converted `wc` in `wcs` */
    if (wcs != NULL) {
      *wcs = wc;
      wcs += 1;
    }

    wcConverted += 1;
    mbc += length;

    /* `count` wide characters have been stored in `wcs`, stop */
    if (wcs != NULL && wcConverted == count) {
      *mbs = mbc;
      break;
    }
  }

  if (wcs != NULL) {
    *state = conversion_state;
  }

  return wcConverted;
}

size_t (__cdecl *__MINGW_IMP_SYMBOL (mbsrtowcs)) (wchar_t *, const char **, size_t, mbstate_t *) = mbsrtowcs;
