// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
******************************************************************************
*
*   Copyright (C) 1998-2014, International Business Machines
*   Corporation and others.  All Rights Reserved.
*
******************************************************************************
*
* File uprintf.cpp
*
* Modification History:
*
*   Date        Name        Description
*   11/19/98    stephen     Creation.
*   03/12/99    stephen     Modified for new C API.
*                           Added conversion from default codepage.
*   08/07/2003  george      Reunify printf implementations
******************************************************************************
*/

#include "unicode/utypes.h"

#if !UCONFIG_NO_FORMATTING && !UCONFIG_NO_CONVERSION

#include "unicode/ustdio.h"
#include "unicode/ustring.h"
#include "unicode/unum.h"
#include "unicode/udat.h"
#include "unicode/putil.h"

#include "cmemory.h"
#include "locbund.h"
#include "mutex.h"
#include "uassert.h"
#include "uprintf.h"
#include "ufile.h"
#include "ucln_io.h"

U_NAMESPACE_USE

static UFILE *gStdOut = nullptr;
static UInitOnce gStdOutInitOnce {};

static UBool U_CALLCONV uprintf_cleanup()
{
    if (gStdOut != nullptr) {
        u_fclose(gStdOut);
        gStdOut = nullptr;
    }
    gStdOutInitOnce.reset();
    return true;
}

static void U_CALLCONV u_stdout_init() {
    U_ASSERT(gStdOut ==  nullptr);
    gStdOut = u_finit(stdout, nullptr, nullptr);
    ucln_io_registerCleanup(UCLN_IO_PRINTF, &uprintf_cleanup);
}

U_CAPI UFILE * U_EXPORT2
u_get_stdout()
{
    umtx_initOnce(gStdOutInitOnce, &u_stdout_init);
    return gStdOut;
}

static int32_t U_EXPORT2
u_printf_write(void          *context,
               const char16_t   *str,
               int32_t       count)
{
    return u_file_write(str, count, static_cast<UFILE*>(context));
}

static int32_t
u_printf_pad_and_justify(void                        *context,
                         const u_printf_spec_info    *info,
                         const char16_t              *result,
                         int32_t                     resultLen)
{
    UFILE* output = static_cast<UFILE*>(context);
    int32_t written, i;

    /* pad and justify, if needed */
    if(info->fWidth != -1 && resultLen < info->fWidth) {
        /* left justify */
        if(info->fLeft) {
            written = u_file_write(result, resultLen, output);
            for(i = 0; i < info->fWidth - resultLen; ++i) {
                written += u_file_write(&info->fPadChar, 1, output);
            }
        }
        /* right justify */
        else {
            written = 0;
            for(i = 0; i < info->fWidth - resultLen; ++i) {
                written += u_file_write(&info->fPadChar, 1, output);
            }
            written += u_file_write(result, resultLen, output);
        }
    }
    /* just write the formatted output */
    else {
        written = u_file_write(result, resultLen, output);
    }

    return written;
}

U_CAPI int32_t U_EXPORT2 
u_fprintf(    UFILE        *f,
          const char    *patternSpecification,
          ... )
{
    va_list ap;
    int32_t count;

    va_start(ap, patternSpecification);
    count = u_vfprintf(f, patternSpecification, ap);
    va_end(ap);

    return count;
}

U_CAPI int32_t U_EXPORT2
u_printf(const char *patternSpecification,
         ...)
{
    va_list ap;
    int32_t count;
    va_start(ap, patternSpecification);
    count = u_vfprintf(u_get_stdout(), patternSpecification, ap);
    va_end(ap);
    return count;
}

U_CAPI int32_t U_EXPORT2 
u_fprintf_u(    UFILE        *f,
            const char16_t *patternSpecification,
            ... )
{
    va_list ap;
    int32_t count;

    va_start(ap, patternSpecification);
    count = u_vfprintf_u(f, patternSpecification, ap);
    va_end(ap);

    return count;
}

U_CAPI int32_t U_EXPORT2
u_printf_u(const char16_t *patternSpecification,
           ...)
{
    va_list ap;
    int32_t count;
    va_start(ap, patternSpecification);
    count = u_vfprintf_u(u_get_stdout(), patternSpecification, ap);
    va_end(ap);
    return count;
}

U_CAPI int32_t  U_EXPORT2 /* U_CAPI ... U_EXPORT2 added by Peter Kirk 17 Nov 2001 */
u_vfprintf(    UFILE        *f,
           const char    *patternSpecification,
           va_list        ap)
{
    int32_t count;
    char16_t *pattern;
    char16_t buffer[UFMT_DEFAULT_BUFFER_SIZE];
    size_t size = strlen(patternSpecification) + 1;

    /* convert from the default codepage to Unicode */
    if (size >= MAX_UCHAR_BUFFER_SIZE(buffer)) {
        pattern = (char16_t *)uprv_malloc(size * sizeof(char16_t));
        if (pattern == nullptr) {
            return 0;
        }
    }
    else {
        pattern = buffer;
    }
    u_charsToUChars(patternSpecification, pattern, static_cast<int32_t>(size));

    /* do the work */
    count = u_vfprintf_u(f, pattern, ap);

    /* clean up */
    if (pattern != buffer) {
        uprv_free(pattern);
    }

    return count;
}

static const u_printf_stream_handler g_stream_handler = {
    u_printf_write,
    u_printf_pad_and_justify
};

U_CAPI int32_t  U_EXPORT2 /* U_CAPI ... U_EXPORT2 added by Peter Kirk 17 Nov 2001 */
u_vfprintf_u(    UFILE        *f,
             const char16_t *patternSpecification,
             va_list        ap)
{
    int32_t          written = 0;   /* haven't written anything yet */

    /* parse and print the whole format string */
    u_printf_parse(&g_stream_handler, patternSpecification, f, nullptr, &f->str.fBundle, &written, ap);

    /* return # of UChars written */
    return written;
}

#endif /* #if !UCONFIG_NO_FORMATTING */

