/*----------------------------------------------------------------------
|   NPT_String::Format
+---------------------------------------------------------------------*/
NPT_String
NPT_String::Format(const char* format, ...)
{
    NPT_String result;
    NPT_Size   buffer_size = NPT_STRING_FORMAT_BUFFER_DEFAULT_SIZE; // default value
    
    va_list  args;

    for(;;) {
        /* try to format (it might not fit) */
        result.Reserve(buffer_size);
        char* buffer = result.UseChars();
        va_start(args, format);
        int f_result = NPT_FormatStringVN(buffer, buffer_size, format, args);
        va_end(args);
        if (f_result >= (int)(buffer_size)) f_result = -1;
        if (f_result >= 0) {
            result.SetLength(f_result);
            break;
        }
        
        /* the buffer was too small, try something bigger         */
        /* (we don't trust the return value of NPT_FormatStringVN */
        /* for the actual size needed)                            */
        buffer_size *= 2;
        if (buffer_size > NPT_STRING_FORMAT_BUFFER_MAX_SIZE) break;
    }
    
    return result;
}
/*----------------------------------------------------------------------
|   NPT_Logger::Log
+---------------------------------------------------------------------*/
void
NPT_Logger::Log(int          level, 
                const char*  source_file,
                unsigned int source_line,
                const char*  source_function,
                const char*  msg, 
                             ...)
{
    // this is a no-op if the log manager is disabled
    if (!LogManager.IsEnabled()) return;
    
    /* check the log level (in case filtering has not already been done) */
    if (level < m_Level) return;

    // we need to disable the log manager while someone is logging
    NPT_LogManagerAutoDisabler autodisabler;
    
    /* format the message */
    char     buffer[NPT_LOG_STACK_BUFFER_MAX_SIZE];
    NPT_Size buffer_size = sizeof(buffer);
    char*    message = buffer;
    int      result;        
    va_list  args;
    for(;;) {
        /* try to format the message (it might not fit) */
        va_start(args, msg);
        result = NPT_FormatStringVN(message, buffer_size-1, msg, args);
        va_end(args);
        if (result >= (int)(buffer_size-1)) result = -1;
        message[buffer_size-1] = 0; /* force a NULL termination */
        if (result >= 0) break;

        /* the buffer was too small, try something bigger */
        buffer_size = (buffer_size+NPT_LOG_HEAP_BUFFER_INCREMENT)*2;
        if (buffer_size > NPT_LOG_HEAP_BUFFER_MAX_SIZE) break;
        if (message != buffer) delete[] message;
        message = new char[buffer_size];
        if (message == NULL) return;
    }

    /* the message is formatted, publish it to the handlers */
    NPT_LogRecord record;
    NPT_Logger*   logger = this;
    
    /* setup the log record */
    record.m_LoggerName     = logger->m_Name,
    record.m_Level          = level;
    record.m_Message        = message;
    record.m_SourceFile     = source_file;
    record.m_SourceLine     = source_line;
    record.m_SourceFunction = source_function;
    NPT_System::GetCurrentTimeStamp(record.m_TimeStamp);
    record.m_ThreadId       = NPT_Thread::GetCurrentThreadId();

    /* call all handlers for this logger and parents */
    m_Manager.Lock();
    while (logger) {
        /* call all handlers for the current logger */
        for (NPT_List<NPT_LogHandler*>::Iterator i = logger->m_Handlers.GetFirstItem();
             i;
             ++i) {
            NPT_LogHandler* handler = *i;
            handler->Log(record);
        }

        /* forward to the parent unless this logger does not forward */
        if (logger->m_ForwardToParent) {
            logger = logger->m_Parent;
        } else {
            break;
        }
    }
    m_Manager.Unlock();

    /* free anything we may have allocated */
    if (message != buffer) delete[] message;
}