/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Microsoft Corporation
 *
 * -=- Robust Distributed System Nucleus (rDSN) -=-
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#pragma once

#include <dsn/utility/config_api.h>
#include <dsn/utility/strings.h>

/// you can use following macros to implement a function called "read_config"
/// to initialize a structure from the configuration file quickly
///
/// please refer to "task_spec.h". it's a very good example

#define CONFIG_BEGIN(t_struct)                                                                     \
    inline bool read_config(                                                                       \
        const char *section, /*out*/ t_struct &val, t_struct *default_value = nullptr)             \
    {

#define CONFIG_END                                                                                 \
    return true;                                                                                   \
    }

// type fld = xyz
#define CONFIG_FLD(real_type, config_type, fld, default_fld_value, dsptr)                          \
    val.fld = (real_type)dsn_config_get_value_##config_type(                                       \
        section, #fld, default_value ? default_value->fld : default_fld_value, dsptr);

#define CONFIG_FLD_STRING(fld, default_fld_value, dsptr)                                           \
    val.fld = dsn_config_get_value_string(                                                         \
        section,                                                                                   \
        #fld,                                                                                      \
        (val.fld.length() > 0 && val.fld != std::string(default_fld_value))                        \
            ? val.fld.c_str()                                                                      \
            : (default_value ? default_value->fld.c_str() : default_fld_value),                    \
        dsptr);

// customized_id<type> fld = xyz
#define CONFIG_FLD_ID(type, fld, default_fld_value, defined_before_read_config, dsptr)             \
    {                                                                                              \
        std::string v = dsn_config_get_value_string(section, #fld, "", dsptr);                     \
        if (v == "") {                                                                             \
            if (!defined_before_read_config) {                                                     \
                if (default_value)                                                                 \
                    val.fld = default_value->fld;                                                  \
                else                                                                               \
                    val.fld = default_fld_value;                                                   \
            }                                                                                      \
        } else {                                                                                   \
            if (!type::is_exist(v.c_str())) {                                                      \
                printf("invalid enum configuration '[%s] %s = %s'\n", section, #fld, v.c_str());   \
                return false;                                                                      \
            } else                                                                                 \
                val.fld = type(v.c_str());                                                         \
        }                                                                                          \
    }

// enum type fld = xyz
#define CONFIG_FLD_ENUM(                                                                           \
    type, fld, default_fld_value, invalid_enum, defined_before_read_config, dsptr)                 \
    {                                                                                              \
        std::string v = dsn_config_get_value_string(section, #fld, "", dsptr);                     \
        if (v == "") {                                                                             \
            if (!defined_before_read_config) {                                                     \
                if (default_value)                                                                 \
                    val.fld = default_value->fld;                                                  \
                else                                                                               \
                    val.fld = default_fld_value;                                                   \
            }                                                                                      \
        } else {                                                                                   \
            auto v2 = enum_from_string(v.c_str(), invalid_enum);                                   \
            if (v2 == invalid_enum) {                                                              \
                printf("invalid enum configuration '[%s] %s = %s'\n", section, #fld, v.c_str());   \
                return false;                                                                      \
            } else                                                                                 \
                val.fld = v2;                                                                      \
        }                                                                                          \
    }

// list<customized_id<type>> fld = x,y,z
#define CONFIG_FLD_ID_LIST(type, fld, dsptr)                                                       \
    {                                                                                              \
        val.fld.clear();                                                                           \
        std::string vv = dsn_config_get_value_string(section, #fld, "", dsptr);                    \
        std::list<std::string> lv;                                                                 \
        ::dsn::utils::split_args(vv.c_str(), lv, ',');                                             \
        for (auto &v : lv) {                                                                       \
            if (!type::is_exist(v.c_str())) {                                                      \
                printf("invalid enum configuration '[%s] %s = %s'\n", section, #fld, v.c_str());   \
                return false;                                                                      \
            } else                                                                                 \
                val.fld.push_back(type(v.c_str()));                                                \
        }                                                                                          \
        if (val.fld.size() == 0 && default_value)                                                  \
            val.fld = default_value->fld;                                                          \
    }

// list<type> fld = x,y,z
#define CONFIG_FLD_STRING_LIST(fld, dsptr)                                                         \
    {                                                                                              \
        std::string vv = dsn_config_get_value_string(section, #fld, "", dsptr);                    \
        ::dsn::utils::split_args(vv.c_str(), val.fld, ',');                                        \
        if (val.fld.size() == 0 && default_value)                                                  \
            val.fld = default_value->fld;                                                          \
    }

// cb: std::list<int>& => fld value
#define CONFIG_FLD_INT_LIST(fld, dsptr)                                                            \
    {                                                                                              \
        std::string vv = dsn_config_get_value_string(section, #fld, "", dsptr);                    \
        std::list<std::string> lv;                                                                 \
        ::dsn::utils::split_args(vv.c_str(), lv, ',');                                             \
        if (lv.size() == 0 && default_value)                                                       \
            val.fld = default_value->fld;                                                          \
        else {                                                                                     \
            for (auto &s : lv) {                                                                   \
                val.fld.push_back(atoi(s.c_str()));                                                \
            }                                                                                      \
        }                                                                                          \
    }
