// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/******************************************************************************
 *   Copyright (C) 2009-2015, International Business Machines
 *   Corporation and others.  All Rights Reserved.
 *******************************************************************************
 */

#include "flagparser.h"
#include "filestrm.h"
#include "cstring.h"
#include "cmemory.h"

#define DEFAULT_BUFFER_SIZE 512

static int32_t currentBufferSize = DEFAULT_BUFFER_SIZE;

static int32_t extractFlag(char* buffer, int32_t bufferSize, char* flag, int32_t flagSize, const char ** flagNames, int32_t numOfFlags, UErrorCode *status);
static int32_t getFlagOffset(const char *buffer, int32_t bufferSize);

/*
 * Opens the given fileName and reads in the information storing the data in flagBuffer.
 */
U_CAPI int32_t U_EXPORT2
parseFlagsFile(const char *fileName, char **flagBuffer, int32_t flagBufferSize, const char ** flagNames, int32_t numOfFlags, UErrorCode *status) {
    char* buffer = nullptr;
    char* tmpFlagBuffer = nullptr;
    UBool allocateMoreSpace = false;
    int32_t idx, i;
    int32_t result = 0;

    FileStream *f = T_FileStream_open(fileName, "r");
    if (f == nullptr) {
        *status = U_FILE_ACCESS_ERROR;
        goto parseFlagsFile_cleanup;
    }

    buffer = (char *)uprv_malloc(sizeof(char) * currentBufferSize);
    tmpFlagBuffer = (char *)uprv_malloc(sizeof(char) * flagBufferSize);

    if (buffer == nullptr || tmpFlagBuffer == nullptr) {
        *status = U_MEMORY_ALLOCATION_ERROR;
        goto parseFlagsFile_cleanup;
    }

    do {
        if (allocateMoreSpace) {
            allocateMoreSpace = false;
            currentBufferSize *= 2;
            uprv_free(buffer);
            buffer = (char *)uprv_malloc(sizeof(char) * currentBufferSize);
            if (buffer == nullptr) {
                *status = U_MEMORY_ALLOCATION_ERROR;
                goto parseFlagsFile_cleanup;
            }
        }
        for (i = 0; i < numOfFlags;) {
            if (T_FileStream_readLine(f, buffer, currentBufferSize) == nullptr) {
                /* End of file reached. */
                break;
            }
            if (buffer[0] == '#') {
                continue;
            }

            if ((int32_t)uprv_strlen(buffer) == (currentBufferSize - 1) && buffer[currentBufferSize-2] != '\n') {
                /* Allocate more space for buffer if it did not read the entire line */
                allocateMoreSpace = true;
                T_FileStream_rewind(f);
                break;
            } else {
                idx = extractFlag(buffer, currentBufferSize, tmpFlagBuffer, flagBufferSize, flagNames, numOfFlags, status);
                if (U_FAILURE(*status)) {
                    if (*status == U_BUFFER_OVERFLOW_ERROR) {
                        result = currentBufferSize;
                    } else {
                        result = -1;
                    }
                    break;
                } else {
                    if (flagNames != nullptr) {
                        if (idx >= 0) {
                            uprv_strcpy(flagBuffer[idx], tmpFlagBuffer);
                        } else {
                            /* No match found.  Skip it. */
                            continue;
                        }
                    } else {
                        uprv_strcpy(flagBuffer[i++], tmpFlagBuffer);
                    }
                }
            }
        }
    } while (allocateMoreSpace && U_SUCCESS(*status));

parseFlagsFile_cleanup:
    uprv_free(tmpFlagBuffer);
    uprv_free(buffer);

    T_FileStream_close(f);
    
    if (U_FAILURE(*status) && *status != U_BUFFER_OVERFLOW_ERROR) {
        return -1;
    }

    if (U_SUCCESS(*status) && result == 0) {
        currentBufferSize = DEFAULT_BUFFER_SIZE;
    }

    return result;
}


/*
 * Extract the setting after the '=' and store it in flag excluding the newline character.
 */
static int32_t extractFlag(char* buffer, int32_t bufferSize, char* flag, int32_t flagSize, const char **flagNames, int32_t numOfFlags, UErrorCode *status) {
    int32_t i, idx = -1;
    char *pBuffer;
    int32_t offset=0;
    UBool bufferWritten = false;

    if (buffer[0] != 0) {
        /* Get the offset (i.e. position after the '=') */
        offset = getFlagOffset(buffer, bufferSize);
        pBuffer = buffer+offset;
        for(i = 0;;i++) {
            if (i >= flagSize) {
                *status = U_BUFFER_OVERFLOW_ERROR;
                return -1;
            }
            if (pBuffer[i+1] == 0) {
                /* Indicates a new line character. End here. */
                flag[i] = 0;
                break;
            }

            flag[i] = pBuffer[i];
            if (i == 0) {
                bufferWritten = true;
            }
        }
    }

    if (!bufferWritten) {
        flag[0] = 0;
    }

    if (flagNames != nullptr && offset>0) {
        offset--;  /* Move offset back 1 because of '='*/
        for (i = 0; i < numOfFlags; i++) {
            if (uprv_strncmp(buffer, flagNames[i], offset) == 0) {
                idx = i;
                break;
            }
        }
    }

    return idx;
}

/*
 * Get the position after the '=' character.
 */
static int32_t getFlagOffset(const char *buffer, int32_t bufferSize) {
    int32_t offset = 0;

    for (offset = 0; offset < bufferSize;offset++) {
        if (buffer[offset] == '=') {
            offset++;
            break;
        }
    }

    if (offset == bufferSize || (offset - 1) == bufferSize) {
        offset = 0;
    }

    return offset;
}
