/*!The Treasure Box Library
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Copyright (C) 2009-present, TBOOX Open Source Group.
 *
 * @author      ruki
 * @file        environment.c
 * @ingroup     platform
 */

/* //////////////////////////////////////////////////////////////////////////////////////
 * includes
 */
#include "environment.h"
#include "path.h"

/* //////////////////////////////////////////////////////////////////////////////////////
 * macros
 */

// the separator
#if defined(TB_CONFIG_OS_WINDOWS) && !defined(TB_COMPILER_LIKE_UNIX)
#   define TM_ENVIRONMENT_SEP       ';'
#else
#   define TM_ENVIRONMENT_SEP       ':'
#endif

/* //////////////////////////////////////////////////////////////////////////////////////
 * implementation
 */
#if defined(TB_CONFIG_OS_WINDOWS) && !defined(TB_COMPILER_LIKE_UNIX)
#   include "windows/environment.c"
#else
#   include "libc/environment.c"
#endif
tb_environment_ref_t tb_environment_init()
{
    // init environment
    return tb_vector_init(8, tb_element_str(tb_true));
}
tb_void_t tb_environment_exit(tb_environment_ref_t environment)
{
    // exit environment
    if (environment) tb_vector_exit(environment);
}
tb_size_t tb_environment_size(tb_environment_ref_t environment)
{
    return tb_vector_size(environment);
}
tb_char_t const* tb_environment_at(tb_environment_ref_t environment, tb_size_t index)
{
    // check
    tb_assert_and_check_return_val(environment, tb_null);

    // get the value
    return (index < tb_vector_size(environment))? (tb_char_t const*)tb_iterator_item(environment, index) : tb_null;
}
tb_bool_t tb_environment_replace(tb_environment_ref_t environment, tb_char_t const* value)
{
    // check
    tb_assert_and_check_return_val(environment, tb_false);

    // clear it first
    tb_vector_clear(environment);

    // insert value
    if (value && *value != '\0') tb_vector_insert_tail(environment, value);

    // ok
    return tb_true;
}
tb_bool_t tb_environment_insert(tb_environment_ref_t environment, tb_char_t const* value, tb_bool_t to_head)
{
    // check
    tb_assert_and_check_return_val(environment && value, tb_false);
    tb_check_return_val(*value != '\0', tb_true);

    // insert value into the head
    if (to_head) tb_vector_insert_head(environment, value);
    // insert value into the tail
    else tb_vector_insert_tail(environment, value);

    // ok
    return tb_true;
}
tb_size_t tb_environment_get(tb_char_t const* name, tb_char_t* values, tb_size_t maxn)
{
    // check
    tb_assert_and_check_return_val(name && values && maxn, 0);

    // init values
    tb_string_t valuestrs;
    if (!tb_string_init(&valuestrs)) return 0;

    // init environment
    tb_environment_ref_t environment = tb_environment_init();
    if (environment)
    {
        // load variable
        if (tb_environment_load(environment, name))
        {
            // make values
            tb_bool_t is_first = tb_true;
            tb_for_all_if (tb_char_t const*, value, environment, value)
            {
                // append separator
                if (!is_first) tb_string_chrcat(&valuestrs, TM_ENVIRONMENT_SEP);
                else is_first = tb_false;

                // append value
                tb_string_cstrcat(&valuestrs, value);
            }
        }

        // exit environment
        tb_environment_exit(environment);
    }

    // save result
    tb_size_t           size = tb_string_size(&valuestrs);
    tb_char_t const*    cstr = tb_string_cstr(&valuestrs);
    if (size && cstr)
    {
        // copy it
        size = tb_strlcpy(values, cstr, maxn);
        tb_assert(size < maxn);
    }

    // exit values
    tb_string_exit(&valuestrs);

    // ok?
    return size;
}
tb_bool_t tb_environment_set(tb_char_t const* name, tb_char_t const* values)
{
    // check
    tb_assert_and_check_return_val(name && values, tb_false);

    // find the first separator position
    tb_bool_t ok = tb_false;
    tb_char_t const* p = values? tb_strchr(values, TM_ENVIRONMENT_SEP) : tb_null;
    if (p)
    {
        // init filter
        tb_hash_set_ref_t filter = tb_hash_set_init(8, tb_element_str(tb_true));

        // init environment
        tb_char_t               data[TB_PATH_MAXN];
        tb_environment_ref_t    environment = tb_environment_init();
        if (environment)
        {
            // make environment
            tb_char_t const* b = values;
            tb_char_t const* e = b + tb_strlen(values);
            do
            {
                // not empty?
                if (b < p)
                {
                    // the size
                    tb_size_t size = tb_min(p - b, sizeof(data) - 1);

                    // copy it
                    tb_strncpy(data, b, size);
                    data[size] = '\0';

                    // have been not inserted?
                    if (!filter || !tb_hash_set_get(filter, data))
                    {
                        // append the environment
                        tb_environment_insert(environment, data, tb_false);

                        // save it to the filter
                        tb_hash_set_insert(filter, data);
                    }
                }

                // end?
                tb_check_break(p + 1 < e);

                // find the next separator position
                b = p + 1;
                p = tb_strchr(b, TM_ENVIRONMENT_SEP);
                if (!p) p = e;

            } while (1);

            // set environment variables
            ok = tb_environment_save(environment, name);

            // exit environment
            tb_environment_exit(environment);
        }

        // exit filter
        if (filter) tb_hash_set_exit(filter);
        filter = tb_null;
    }
    // only one?
    else
    {
        // set environment variables
        tb_environment_ref_t environment = tb_environment_init();
        if (environment)
        {
            // append the environment
            tb_environment_insert(environment, values, tb_false);

            // set environment variables
            ok = tb_environment_save(environment, name);

            // exit environment
            tb_environment_exit(environment);
        }
    }

    // ok?
    return ok;
}
tb_bool_t tb_environment_add(tb_char_t const* name, tb_char_t const* values, tb_bool_t to_head)
{
    // check
    tb_assert_and_check_return_val(name && values, tb_false);

    // find the first separator position
    tb_bool_t ok = tb_false;
    tb_char_t const* p = values? tb_strchr(values, TM_ENVIRONMENT_SEP) : tb_null;
    if (p)
    {
        // init filter
        tb_hash_set_ref_t filter = tb_hash_set_init(8, tb_element_str(tb_true));

        // init environment
        tb_char_t               data[TB_PATH_MAXN];
        tb_environment_ref_t    environment = tb_environment_init();
        if (environment)
        {
            // load the previous values
            tb_environment_load(environment, name);

            // make environment
            tb_char_t const* b = values;
            tb_char_t const* e = b + tb_strlen(values);
            do
            {
                // not empty?
                if (b < p)
                {
                    // the size
                    tb_size_t size = tb_min(p - b, sizeof(data) - 1);

                    // copy it
                    tb_strncpy(data, b, size);
                    data[size] = '\0';

                    // have been not inserted?
                    if (!filter || !tb_hash_set_get(filter, data))
                    {
                        // append the environment
                        tb_environment_insert(environment, data, to_head);

                        // save it to the filter
                        tb_hash_set_insert(filter, data);
                    }
                }

                // end?
                tb_check_break(p + 1 < e);

                // find the next separator position
                b = p + 1;
                p = tb_strchr(b, TM_ENVIRONMENT_SEP);
                if (!p) p = e;

            } while (1);

            // set environment variables
            ok = tb_environment_save(environment, name);

            // exit environment
            tb_environment_exit(environment);
        }

        // exit filter
        if (filter) tb_hash_set_exit(filter);
        filter = tb_null;
    }
    // only one?
    else
    {
        // set environment variables
        tb_environment_ref_t environment = tb_environment_init();
        if (environment)
        {
            // load the previous values
            tb_environment_load(environment, name);

            // append the environment
            tb_environment_insert(environment, values, to_head);

            // set environment variables
            ok = tb_environment_save(environment, name);

            // exit environment
            tb_environment_exit(environment);
        }
    }

    // ok?
    return ok;
}
#ifdef __tb_debug__
tb_void_t tb_environment_dump(tb_environment_ref_t environment, tb_char_t const* name)
{
    // trace
    tb_trace_i("%s:", name);

    // dump values
    tb_for_all_if (tb_char_t const*, value, environment, value)
    {
        // trace
        tb_trace_i("    %s", value);
    }
}
#endif
