/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

package config

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/cgrates/cgrates/utils"
)

var (
	DBDefaults               DbDefaults
	cgrCfg                   *CGRConfig  // will be shared
	dfltFsConnConfig         *FsConnCfg  // Default FreeSWITCH Connection configuration, built out of json default configuration
	dfltKamConnConfig        *KamConnCfg // Default Kamailio Connection configuration
	dfltRemoteHost           *RemoteHost
	dfltAstConnCfg           *AsteriskConnCfg
	dfltLoaderConfig         *LoaderSCfg
	dfltLoaderDataTypeConfig *LoaderDataType
)

func NewDbDefaults() DbDefaults {
	deflt := DbDefaults{
		utils.MYSQL: map[string]string{
			"DbName": "cgrates",
			"DbPort": "3306",
			"DbPass": "CGRateS.org",
		},
		utils.POSTGRES: map[string]string{
			"DbName": "cgrates",
			"DbPort": "5432",
			"DbPass": "CGRateS.org",
		},
		utils.MONGO: map[string]string{
			"DbName": "cgrates",
			"DbPort": "27017",
			"DbPass": "",
		},
		utils.REDIS: map[string]string{
			"DbName": "10",
			"DbPort": "6379",
			"DbPass": "",
		},
		utils.INTERNAL: map[string]string{
			"DbName": "internal",
			"DbPort": "internal",
			"DbPass": "internal",
		},
	}
	return deflt
}

type DbDefaults map[string]map[string]string

func (dbDflt DbDefaults) DBName(dbType string, flagInput string) string {
	if flagInput != utils.MetaDynamic {
		return flagInput
	}
	return dbDflt[dbType]["DbName"]
}

func (DbDefaults) DBUser(dbType string, flagInput string) string {
	if flagInput != utils.MetaDynamic {
		return flagInput
	}
	return utils.CGRATES
}

func (DbDefaults) DBHost(dbType string, flagInput string) string {
	if flagInput != utils.MetaDynamic {
		return flagInput
	}
	return utils.LOCALHOST
}

func (self DbDefaults) DBPort(dbType string, flagInput string) string {
	if flagInput != utils.MetaDynamic {
		return flagInput
	}
	return self[dbType]["DbPort"]
}

func (self DbDefaults) DBPass(dbType string, flagInput string) string {
	if flagInput != utils.MetaDynamic {
		return flagInput
	}
	return self[dbType]["DbPass"]
}

func init() {
	cgrCfg, _ = NewDefaultCGRConfig()
	DBDefaults = NewDbDefaults()
}

// Used to retrieve system configuration from other packages
func CgrConfig() *CGRConfig {
	return cgrCfg
}

// Used to set system configuration from other places
func SetCgrConfig(cfg *CGRConfig) {
	cgrCfg = cfg
}

func NewDefaultCGRConfig() (cfg *CGRConfig, err error) {
	cfg = new(CGRConfig)
	cfg.initChanels()
	cfg.DataFolderPath = "/usr/share/cgrates/"
	cfg.MaxCallDuration = time.Duration(3) * time.Hour // Hardcoded for now

	cfg.generalCfg = new(GeneralCfg)
	cfg.generalCfg.NodeID = utils.UUIDSha1Prefix()
	cfg.dataDbCfg = new(DataDbCfg)
	cfg.storDbCfg = new(StorDbCfg)
	cfg.tlsCfg = new(TlsCfg)
	cfg.cacheCfg = make(CacheCfg)
	cfg.listenCfg = new(ListenCfg)
	cfg.httpCfg = new(HTTPCfg)
	cfg.filterSCfg = new(FilterSCfg)
	cfg.ralsCfg = new(RalsCfg)
	cfg.ralsCfg.MaxComputedUsage = make(map[string]time.Duration)
	cfg.ralsCfg.BalanceRatingSubject = make(map[string]string)
	cfg.schedulerCfg = new(SchedulerCfg)
	cfg.cdrsCfg = new(CdrsCfg)
	cfg.CdreProfiles = make(map[string]*CdreCfg)
	cfg.CdrcProfiles = make(map[string][]*CdrcCfg)
	cfg.analyzerSCfg = new(AnalyzerSCfg)
	cfg.sessionSCfg = new(SessionSCfg)
	cfg.fsAgentCfg = new(FsAgentCfg)
	cfg.kamAgentCfg = new(KamAgentCfg)
	cfg.asteriskAgentCfg = new(AsteriskAgentCfg)
	cfg.diameterAgentCfg = new(DiameterAgentCfg)
	cfg.radiusAgentCfg = new(RadiusAgentCfg)
	cfg.dnsAgentCfg = new(DNSAgentCfg)
	cfg.attributeSCfg = new(AttributeSCfg)
	cfg.chargerSCfg = new(ChargerSCfg)
	cfg.resourceSCfg = new(ResourceSConfig)
	cfg.statsCfg = new(StatSCfg)
	cfg.thresholdSCfg = new(ThresholdSCfg)
	cfg.supplierSCfg = new(SupplierSCfg)
	cfg.sureTaxCfg = new(SureTaxCfg)
	cfg.dispatcherSCfg = new(DispatcherSCfg)
	cfg.loaderCgrCfg = new(LoaderCgrCfg)
	cfg.migratorCgrCfg = new(MigratorCgrCfg)
	cfg.mailerCfg = new(MailerCfg)
	cfg.loaderCfg = make([]*LoaderSCfg, 0)
	cfg.apier = new(ApierCfg)
	cfg.ersCfg = new(ERsCfg)

	cfg.ConfigReloads = make(map[string]chan struct{})
	cfg.ConfigReloads[utils.CDRC] = make(chan struct{}, 1)
	cfg.ConfigReloads[utils.CDRC] <- struct{}{} // Unlock the channel
	cfg.ConfigReloads[utils.CDRE] = make(chan struct{}, 1)
	cfg.ConfigReloads[utils.CDRE] <- struct{}{} // Unlock the channel
	cfg.ConfigReloads[utils.DIAMETER_AGENT] = make(chan struct{}, 1)
	cfg.ConfigReloads[utils.DIAMETER_AGENT] <- struct{}{} // Unlock the channel
	cfg.ConfigReloads[utils.SMAsterisk] = make(chan struct{}, 1)
	cfg.ConfigReloads[utils.SMAsterisk] <- struct{}{} // Unlock the channel

	var cgrJsonCfg *CgrJsonCfg
	if cgrJsonCfg, err = NewCgrJsonCfgFromBytes([]byte(CGRATES_CFG_JSON)); err != nil {
		return
	}
	if err = cfg.loadFromJsonCfg(cgrJsonCfg); err != nil {
		return
	}

	cfg.dfltCdreProfile = cfg.CdreProfiles[utils.META_DEFAULT].Clone() // So default will stay unique, will have nil pointer in case of no defaults loaded which is an extra check
	cfg.dfltCdrcProfile = cfg.CdrcProfiles["/var/spool/cgrates/cdrc/in"][0].Clone()
	// populate default ERs reader
	for _, ersRdr := range cfg.ersCfg.Readers {
		if ersRdr.ID == utils.MetaDefault {
			cfg.dfltEvRdr = ersRdr.Clone()
			break
		}
	}
	dfltFsConnConfig = cfg.fsAgentCfg.EventSocketConns[0] // We leave it crashing here on purpose if no Connection defaults defined
	dfltKamConnConfig = cfg.kamAgentCfg.EvapiConns[0]
	dfltAstConnCfg = cfg.asteriskAgentCfg.AsteriskConns[0]
	dfltLoaderConfig = cfg.loaderCfg[0].Clone()
	err = cfg.checkConfigSanity()
	return
}

func NewCGRConfigFromJsonStringWithDefaults(cfgJsonStr string) (cfg *CGRConfig, err error) {
	cfg, _ = NewDefaultCGRConfig()
	jsnCfg := new(CgrJsonCfg)
	if err = NewRjReaderFromBytes([]byte(cfgJsonStr)).Decode(jsnCfg); err != nil {
		return
	} else if err = cfg.loadFromJsonCfg(jsnCfg); err != nil {
		return
	}
	return
}

// Reads all .json files out of a folder/subfolders and loads them up in lexical order
func NewCGRConfigFromPath(path string) (cfg *CGRConfig, err error) {
	if cfg, err = NewDefaultCGRConfig(); err != nil {
		return
	}
	cfg.ConfigPath = path

	if err = cfg.loadConfigFromPath(path, []func(*CgrJsonCfg) error{cfg.loadFromJsonCfg}); err != nil {
		return
	}
	err = cfg.checkConfigSanity()
	return
}

func isHidden(fileName string) bool {
	if fileName == "." || fileName == ".." {
		return false
	}
	return strings.HasPrefix(fileName, ".")
}

// Holds system configuration, defaults are overwritten with values from config file if found
type CGRConfig struct {
	lks             map[string]*sync.RWMutex
	MaxCallDuration time.Duration // The maximum call duration (used by responder when querying DerivedCharging) // ToDo: export it in configuration file
	DataFolderPath  string        // Path towards data folder, for tests internal usage, not loading out of .json options
	ConfigPath      string        // Path towards config

	// Cache defaults loaded from json and needing clones
	dfltCdreProfile *CdreCfg        // Default cdreConfig profile
	dfltCdrcProfile *CdrcCfg        // Default cdrcConfig profile
	dfltEvRdr       *EventReaderCfg // default event reader

	CdreProfiles map[string]*CdreCfg   // Cdre config profiles
	CdrcProfiles map[string][]*CdrcCfg // Number of CDRC instances running imports, format map[dirPath][]{Configs}
	loaderCfg    []*LoaderSCfg         // LoaderS configs
	httpAgentCfg HttpAgentCfgs         // HttpAgent configs

	ConfigReloads map[string]chan struct{} // Signals to specific entities that a config reload should occur
	rldChans      map[string]chan struct{} // index here the channels used for reloads

	generalCfg       *GeneralCfg       // General config
	dataDbCfg        *DataDbCfg        // Database config
	storDbCfg        *StorDbCfg        // StroreDb config
	tlsCfg           *TlsCfg           // TLS config
	cacheCfg         CacheCfg          // Cache config
	listenCfg        *ListenCfg        // Listen config
	httpCfg          *HTTPCfg          // HTTP config
	filterSCfg       *FilterSCfg       // FilterS config
	ralsCfg          *RalsCfg          // Rals config
	schedulerCfg     *SchedulerCfg     // Scheduler config
	cdrsCfg          *CdrsCfg          // Cdrs config
	sessionSCfg      *SessionSCfg      // SessionS config
	fsAgentCfg       *FsAgentCfg       // FreeSWITCHAgent config
	kamAgentCfg      *KamAgentCfg      // KamailioAgent config
	asteriskAgentCfg *AsteriskAgentCfg // AsteriskAgent config
	diameterAgentCfg *DiameterAgentCfg // DiameterAgent config
	radiusAgentCfg   *RadiusAgentCfg   // RadiusAgent config
	dnsAgentCfg      *DNSAgentCfg      // DNSAgent config
	attributeSCfg    *AttributeSCfg    // AttributeS config
	chargerSCfg      *ChargerSCfg      // ChargerS config
	resourceSCfg     *ResourceSConfig  // ResourceS config
	statsCfg         *StatSCfg         // StatS config
	thresholdSCfg    *ThresholdSCfg    // ThresholdS config
	supplierSCfg     *SupplierSCfg     // SupplierS config
	sureTaxCfg       *SureTaxCfg       // SureTax config
	dispatcherSCfg   *DispatcherSCfg   // DispatcherS config
	loaderCgrCfg     *LoaderCgrCfg     // LoaderCgr config
	migratorCgrCfg   *MigratorCgrCfg   // MigratorCgr config
	mailerCfg        *MailerCfg        // Mailer config
	analyzerSCfg     *AnalyzerSCfg     // AnalyzerS config
	apier            *ApierCfg
	ersCfg           *ERsCfg
}

var posibleLoaderTypes = utils.NewStringSet([]string{utils.MetaAttributes,
	utils.MetaResources, utils.MetaFilters, utils.MetaStats,
	utils.MetaSuppliers, utils.MetaThresholds, utils.MetaChargers,
	utils.MetaDispatchers, utils.MetaDispatcherHosts})

var possibleReaderTypes = utils.NewStringSet([]string{utils.MetaFileCSV, utils.MetaKafkajsonMap})

func (self *CGRConfig) checkConfigSanity() error {
	// Rater checks
	if self.ralsCfg.Enabled && !self.dispatcherSCfg.Enabled {
		if !self.statsCfg.Enabled {
			for _, connCfg := range self.ralsCfg.StatSConns {
				if connCfg.Address == utils.MetaInternal {
					return fmt.Errorf("%s not enabled but requested by %s component.",
						utils.StatS, utils.RALService)
				}
			}
		}
		if !self.thresholdSCfg.Enabled {
			for _, connCfg := range self.ralsCfg.ThresholdSConns {
				if connCfg.Address == utils.MetaInternal {
					return fmt.Errorf("%s not enabled but requested by %s component.",
						utils.ThresholdS, utils.RALService)
				}
			}
		}
	}
	// CDRServer checks
	if self.cdrsCfg.Enabled && !self.dispatcherSCfg.Enabled {
		if !self.chargerSCfg.Enabled {
			for _, conn := range self.cdrsCfg.ChargerSConns {
				if conn.Address == utils.MetaInternal {
					return errors.New("ChargerS not enabled but requested by CDRS component.")
				}
			}
		}
		if !self.ralsCfg.Enabled {
			for _, cdrsRaterConn := range self.cdrsCfg.RaterConns {
				if cdrsRaterConn.Address == utils.MetaInternal {
					return errors.New("RALs not enabled but requested by CDRS component.")
				}
			}
		}
		if !self.attributeSCfg.Enabled {
			for _, connCfg := range self.cdrsCfg.AttributeSConns {
				if connCfg.Address == utils.MetaInternal {
					return errors.New("AttributeS not enabled but requested by CDRS component.")
				}
			}
		}
		if !self.statsCfg.Enabled {
			for _, connCfg := range self.cdrsCfg.StatSConns {
				if connCfg.Address == utils.MetaInternal {
					return errors.New("StatS not enabled but requested by CDRS component.")
				}
			}
		}
		for _, cdrePrfl := range self.cdrsCfg.OnlineCDRExports {
			if _, hasIt := self.CdreProfiles[cdrePrfl]; !hasIt {
				return fmt.Errorf("<CDRS> Cannot find CDR export template with ID: <%s>", cdrePrfl)
			}
		}
		if !self.thresholdSCfg.Enabled {
			for _, connCfg := range self.cdrsCfg.ThresholdSConns {
				if connCfg.Address == utils.MetaInternal {
					return errors.New("ThresholdS not enabled but requested by CDRS component.")
				}
			}
		}
	}
	// CDRC sanity checks
	for _, cdrcCfgs := range self.CdrcProfiles {
		for _, cdrcInst := range cdrcCfgs {
			if !cdrcInst.Enabled {
				continue
			}
			if len(cdrcInst.CdrsConns) == 0 {
				return fmt.Errorf("<CDRC> Instance: %s, CdrC enabled but no CDRS defined!", cdrcInst.ID)
			}
			if !self.cdrsCfg.Enabled && !self.dispatcherSCfg.Enabled {
				for _, conn := range cdrcInst.CdrsConns {
					if conn.Address == utils.MetaInternal {
						return errors.New("CDRS not enabled but referenced from CDRC")
					}
				}
			}
			if len(cdrcInst.ContentFields) == 0 {
				return errors.New("CdrC enabled but no fields to be processed defined!")
			}
			if cdrcInst.CdrFormat == utils.MetaFileCSV {
				for _, cdrFld := range cdrcInst.ContentFields {
					for _, rsrFld := range cdrFld.Value {
						if rsrFld.attrName != "" {
							if _, errConv := strconv.Atoi(rsrFld.attrName); errConv != nil {
								return fmt.Errorf("CDR fields must be indices in case of .csv files, have instead: %s", rsrFld.attrName)
							}
						}
					}
				}
			}
		}
	}
	// Loaders sanity checks
	for _, ldrSCfg := range self.loaderCfg {
		if !ldrSCfg.Enabled {
			continue
		}
		for _, dir := range []string{ldrSCfg.TpInDir, ldrSCfg.TpOutDir} {
			if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
				return fmt.Errorf("<%s> Nonexistent folder: %s", utils.LoaderS, dir)
			}
		}
		for _, data := range ldrSCfg.Data {
			if !posibleLoaderTypes.Has(data.Type) {
				return fmt.Errorf("<%s> unsupported data type %s", utils.LoaderS, data.Type)
			}

			for _, field := range data.Fields {
				if field.Type != utils.META_COMPOSED && field.Type != utils.MetaString && field.Type != utils.MetaVariable {
					return fmt.Errorf("<%s> invalid field type %s for %s at %s", utils.LoaderS, field.Type, data.Type, field.Tag)
				}
			}
		}
	}
	// SessionS checks
	if self.sessionSCfg.Enabled && !self.dispatcherSCfg.Enabled {
		if !self.chargerSCfg.Enabled {
			for _, conn := range self.sessionSCfg.ChargerSConns {
				if conn.Address == utils.MetaInternal {
					return fmt.Errorf("<%s> %s not enabled", utils.SessionS, utils.ChargerS)
				}
			}
		}
		if !self.ralsCfg.Enabled {
			for _, smgRALsConn := range self.sessionSCfg.RALsConns {
				if smgRALsConn.Address == utils.MetaInternal {
					return fmt.Errorf("<%s> %s not enabled but requested by SMGeneric component.", utils.SessionS, utils.RALService)
				}
			}
		}
		if !self.resourceSCfg.Enabled {
			for _, conn := range self.sessionSCfg.ResSConns {
				if conn.Address == utils.MetaInternal {
					return fmt.Errorf("<%s> %s not enabled but requested by SMGeneric component.", utils.SessionS, utils.ResourceS)
				}
			}
		}
		if !self.thresholdSCfg.Enabled {
			for _, conn := range self.sessionSCfg.ThreshSConns {
				if conn.Address == utils.MetaInternal {
					return fmt.Errorf("<%s> %s not enabled but requested by SMGeneric component.", utils.SessionS, utils.ThresholdS)
				}
			}
		}
		if !self.statsCfg.Enabled {
			for _, conn := range self.sessionSCfg.StatSConns {
				if conn.Address == utils.MetaInternal {
					return fmt.Errorf("<%s> %s not enabled but requested by SMGeneric component.", utils.SessionS, utils.StatS)
				}
			}
		}
		if !self.supplierSCfg.Enabled {
			for _, conn := range self.sessionSCfg.SupplSConns {
				if conn.Address == utils.MetaInternal {
					return fmt.Errorf("<%s> %s not enabled but requested by SMGeneric component.", utils.SessionS, utils.SupplierS)
				}
			}
		}
		if !self.attributeSCfg.Enabled {
			for _, conn := range self.sessionSCfg.AttrSConns {
				if conn.Address == utils.MetaInternal {
					return fmt.Errorf("<%s> %s not enabled but requested by SMGeneric component.", utils.SessionS, utils.AttributeS)
				}
			}
		}
		if !self.cdrsCfg.Enabled {
			for _, smgCDRSConn := range self.sessionSCfg.CDRsConns {
				if smgCDRSConn.Address == utils.MetaInternal {
					return fmt.Errorf("<%s> CDRS not enabled but referenced by SMGeneric component", utils.SessionS)
				}
			}
		}
	}
	// FreeSWITCHAgent checks
	if self.fsAgentCfg.Enabled {
		if len(self.fsAgentCfg.SessionSConns) == 0 {
			return fmt.Errorf("<%s> no %s connections defined",
				utils.FreeSWITCHAgent, utils.SessionS)
		}
		if !self.dispatcherSCfg.Enabled && // if dispatcher is enabled all internal connections are managed by it
			!self.sessionSCfg.Enabled {
			for _, connCfg := range self.fsAgentCfg.SessionSConns {
				if connCfg.Address == utils.MetaInternal {
					return fmt.Errorf("%s not enabled but referenced by %s",
						utils.SessionS, utils.FreeSWITCHAgent)
				}
			}
		}
	}
	// KamailioAgent checks
	if self.kamAgentCfg.Enabled {
		if len(self.kamAgentCfg.SessionSConns) == 0 {
			return fmt.Errorf("<%s> no %s connections defined",
				utils.KamailioAgent, utils.SessionS)
		}
		if !self.dispatcherSCfg.Enabled && // if dispatcher is enabled all internal connections are managed by it
			!self.sessionSCfg.Enabled {
			for _, connCfg := range self.kamAgentCfg.SessionSConns {
				if connCfg.Address == utils.MetaInternal {
					return fmt.Errorf("%s not enabled but referenced by %s",
						utils.SessionS, utils.KamailioAgent)
				}
			}
		}
	}
	// AsteriskAgent checks
	if self.asteriskAgentCfg.Enabled {
		if len(self.asteriskAgentCfg.SessionSConns) == 0 {
			return fmt.Errorf("<%s> no %s connections defined",
				utils.AsteriskAgent, utils.SessionS)
		}
		if !self.dispatcherSCfg.Enabled && // if dispatcher is enabled all internal connections are managed by it
			!self.sessionSCfg.Enabled {
			for _, smAstSMGConn := range self.asteriskAgentCfg.SessionSConns {
				if smAstSMGConn.Address == utils.MetaInternal {
					return fmt.Errorf("%s not enabled but referenced by %s",
						utils.SessionS, utils.AsteriskAgent)
				}
			}
		}
	}
	// DAgent checks
	if self.diameterAgentCfg.Enabled {
		if len(self.diameterAgentCfg.SessionSConns) == 0 {
			return fmt.Errorf("<%s> no %s connections defined",
				utils.DiameterAgent, utils.SessionS)
		}
		if !self.dispatcherSCfg.Enabled && // if dispatcher is enabled all internal connections are managed by it
			!self.sessionSCfg.Enabled {
			for _, daSMGConn := range self.diameterAgentCfg.SessionSConns {
				if daSMGConn.Address == utils.MetaInternal {
					return fmt.Errorf("%s not enabled but referenced by %s",
						utils.SessionS, utils.DiameterAgent)
				}
			}
		}
	}
	if self.radiusAgentCfg.Enabled {
		if len(self.radiusAgentCfg.SessionSConns) == 0 {
			return fmt.Errorf("<%s> no %s connections defined",
				utils.RadiusAgent, utils.SessionS)
		}
		if !self.dispatcherSCfg.Enabled && // if dispatcher is enabled all internal connections are managed by it
			!self.sessionSCfg.Enabled {
			for _, raSMGConn := range self.radiusAgentCfg.SessionSConns {
				if raSMGConn.Address == utils.MetaInternal {
					return fmt.Errorf("%s not enabled but referenced by %s",
						utils.SessionS, utils.RadiusAgent)
				}
			}
		}
	}
	if self.dnsAgentCfg.Enabled {
		if len(self.dnsAgentCfg.SessionSConns) == 0 {
			return fmt.Errorf("<%s> no %s connections defined",
				utils.DNSAgent, utils.SessionS)
		}
		if !self.dispatcherSCfg.Enabled && // if dispatcher is enabled all internal connections are managed by it
			!self.sessionSCfg.Enabled {
			for _, sSConn := range self.dnsAgentCfg.SessionSConns {
				if sSConn.Address == utils.MetaInternal {
					return fmt.Errorf("%s not enabled but referenced by %s", utils.SessionS, utils.DNSAgent)
				}
			}
		}
	}
	// HTTPAgent checks
	for _, httpAgentCfg := range self.httpAgentCfg {
		// httpAgent checks
		if !self.dispatcherSCfg.Enabled && // if dispatcher is enabled all internal connections are managed by it
			self.sessionSCfg.Enabled {
			for _, sSConn := range httpAgentCfg.SessionSConns {
				if sSConn.Address == utils.MetaInternal {
					return errors.New("SessionS not enabled but referenced by HttpAgent component")
				}
			}
		}
		if !utils.SliceHasMember([]string{utils.MetaUrl, utils.MetaXml}, httpAgentCfg.RequestPayload) {
			return fmt.Errorf("<%s> unsupported request payload %s",
				utils.HTTPAgent, httpAgentCfg.RequestPayload)
		}
		if !utils.SliceHasMember([]string{utils.MetaTextPlain, utils.MetaXml}, httpAgentCfg.ReplyPayload) {
			return fmt.Errorf("<%s> unsupported reply payload %s",
				utils.HTTPAgent, httpAgentCfg.ReplyPayload)
		}
	}
	if self.attributeSCfg.Enabled {
		if self.attributeSCfg.ProcessRuns < 1 {
			return fmt.Errorf("<%s> process_runs needs to be bigger than 0", utils.AttributeS)
		}
	}
	if self.chargerSCfg.Enabled && !self.dispatcherSCfg.Enabled &&
		(self.attributeSCfg == nil || !self.attributeSCfg.Enabled) {
		for _, connCfg := range self.chargerSCfg.AttributeSConns {
			if connCfg.Address == utils.MetaInternal {
				return errors.New("AttributeS not enabled but requested by ChargerS component.")
			}
		}
	}
	// ResourceLimiter checks
	if self.resourceSCfg.Enabled && !self.thresholdSCfg.Enabled && !self.dispatcherSCfg.Enabled {
		for _, connCfg := range self.resourceSCfg.ThresholdSConns {
			if connCfg.Address == utils.MetaInternal {
				return errors.New("ThresholdS not enabled but requested by ResourceS component.")
			}
		}
	}
	// StatS checks
	if self.statsCfg.Enabled && !self.thresholdSCfg.Enabled && !self.dispatcherSCfg.Enabled {
		for _, connCfg := range self.statsCfg.ThresholdSConns {
			if connCfg.Address == utils.MetaInternal {
				return errors.New("ThresholdS not enabled but requested by StatS component.")
			}
		}
	}
	// SupplierS checks
	if self.supplierSCfg.Enabled && !self.dispatcherSCfg.Enabled {
		if !self.resourceSCfg.Enabled {
			for _, connCfg := range self.supplierSCfg.ResourceSConns {
				if connCfg.Address == utils.MetaInternal {
					return errors.New("ResourceS not enabled but requested by SupplierS component.")
				}
			}
		}
		if !self.statsCfg.Enabled {
			for _, connCfg := range self.supplierSCfg.StatSConns {
				if connCfg.Address == utils.MetaInternal {
					return errors.New("StatS not enabled but requested by SupplierS component.")
				}
			}
		}
		if !self.attributeSCfg.Enabled {
			for _, connCfg := range self.supplierSCfg.AttributeSConns {
				if connCfg.Address == utils.MetaInternal {
					return errors.New("AttributeS not enabled but requested by SupplierS component.")
				}
			}
		}
	}
	// Scheduler check connection with CDR Server
	if !self.cdrsCfg.Enabled && !self.dispatcherSCfg.Enabled {
		for _, connCfg := range self.schedulerCfg.CDRsConns {
			if connCfg.Address == utils.MetaInternal {
				return errors.New("CDR Server not enabled but requested by Scheduler")
			}
		}
	}
	// EventReader sanity checks
	if self.ersCfg.Enabled {
		if !self.sessionSCfg.Enabled {
			for _, connCfg := range self.ersCfg.SessionSConns {
				if connCfg.Address == utils.MetaInternal {
					return errors.New("SessionS not enabled but requested by EventReader component.")
				}
			}
		}
		for _, rdr := range self.ersCfg.Readers {
			if !possibleReaderTypes.Has(rdr.Type) {
				return fmt.Errorf("<%s> unsupported data type: %s for reader with ID: %s", utils.ERs, rdr.Type, rdr.ID)
			}

			if rdr.Type == utils.MetaFileCSV {
				for _, dir := range []string{rdr.ProcessedPath, rdr.SourcePath} {
					if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
						return fmt.Errorf("<%s> Nonexistent folder: %s for reader with ID: %s", utils.ERs, dir, rdr.ID)
					}
				}
				if rdr.FieldSep == utils.EmptyString {
					return fmt.Errorf("<%s> empty FieldSep for reader with ID: %s", utils.ERs, rdr.ID)
				}
			}
			if rdr.Type == utils.MetaKafkajsonMap && rdr.RunDelay > 0 {
				return fmt.Errorf("<%s> RunDelay field can not be bigger than zero for reader with ID: %s", utils.ERs, rdr.ID)
			}
		}
	}

	return nil
}

func (self *CGRConfig) LazySanityCheck() {
	for _, cdrePrfl := range self.cdrsCfg.OnlineCDRExports {
		if cdreProfile, hasIt := self.CdreProfiles[cdrePrfl]; hasIt && (cdreProfile.ExportFormat == utils.MetaS3jsonMap || cdreProfile.ExportFormat == utils.MetaSQSjsonMap) {
			poster := utils.SQSPoster
			if cdreProfile.ExportFormat == utils.MetaS3jsonMap {
				poster = utils.S3Poster
			}
			argsMap := utils.GetUrlRawArguments(cdreProfile.ExportPath)
			for _, arg := range []string{utils.AWSRegion, utils.AWSKey, utils.AWSSecret} {
				if _, has := argsMap[arg]; !has {
					utils.Logger.Warning(fmt.Sprintf("<%s> No %s present for AWS for cdre: <%s>.", poster, arg, cdrePrfl))
				}
			}
		}
	}
}

// Loads from json configuration object, will be used for defaults, config from file and reload, might need lock
func (cfg *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) (err error) {
	// Load sections out of JSON config, stop on error
	for _, loadFunc := range []func(*CgrJsonCfg) error{
		cfg.loadGeneralCfg, cfg.loadCacheCfg, cfg.loadListenCfg,
		cfg.loadHttpCfg, cfg.loadDataDBCfg, cfg.loadStorDBCfg,
		cfg.loadFilterSCfg, cfg.loadRalSCfg, cfg.loadSchedulerCfg,
		cfg.loadCdrsCfg, cfg.loadCdreCfg, cfg.loadCdrcCfg,
		cfg.loadSessionSCfg, cfg.loadFreeswitchAgentCfg, cfg.loadKamAgentCfg,
		cfg.loadAsteriskAgentCfg, cfg.loadDiameterAgentCfg, cfg.loadRadiusAgentCfg,
		cfg.loadDNSAgentCfg, cfg.loadHttpAgentCfg, cfg.loadAttributeSCfg,
		cfg.loadChargerSCfg, cfg.loadResourceSCfg, cfg.loadStatSCfg,
		cfg.loadThresholdSCfg, cfg.loadSupplierSCfg, cfg.loadLoaderSCfg,
		cfg.loadMailerCfg, cfg.loadSureTaxCfg, cfg.loadDispatcherSCfg,
		cfg.loadLoaderCgrCfg, cfg.loadMigratorCgrCfg, cfg.loadTlsCgrCfg,
		cfg.loadAnalyzerCgrCfg, cfg.loadApierCfg, cfg.loadErsCfg} {
		if err = loadFunc(jsnCfg); err != nil {
			return
		}
	}
	return
}

// loadGeneralCfg loads the General section of the configuration
func (cfg *CGRConfig) loadGeneralCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnGeneralCfg *GeneralJsonCfg
	if jsnGeneralCfg, err = jsnCfg.GeneralJsonCfg(); err != nil {
		return
	}
	return cfg.generalCfg.loadFromJsonCfg(jsnGeneralCfg)
}

// loadCacheCfg loads the Cache section of the configuration
func (cfg *CGRConfig) loadCacheCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnCacheCfg *CacheJsonCfg
	if jsnCacheCfg, err = jsnCfg.CacheJsonCfg(); err != nil {
		return
	}
	return cfg.cacheCfg.loadFromJsonCfg(jsnCacheCfg)
}

// loadListenCfg loads the Listen section of the configuration
func (cfg *CGRConfig) loadListenCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnListenCfg *ListenJsonCfg
	if jsnListenCfg, err = jsnCfg.ListenJsonCfg(); err != nil {
		return
	}
	return cfg.listenCfg.loadFromJsonCfg(jsnListenCfg)
}

// loadHttpCfg loads the Http section of the configuration
func (cfg *CGRConfig) loadHttpCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnHttpCfg *HTTPJsonCfg
	if jsnHttpCfg, err = jsnCfg.HttpJsonCfg(); err != nil {
		return
	}
	return cfg.httpCfg.loadFromJsonCfg(jsnHttpCfg)
}

// loadDataDBCfg loads the DataDB section of the configuration
func (cfg *CGRConfig) loadDataDBCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnDataDbCfg *DbJsonCfg
	if jsnDataDbCfg, err = jsnCfg.DbJsonCfg(DATADB_JSN); err != nil {
		return
	}
	return cfg.dataDbCfg.loadFromJsonCfg(jsnDataDbCfg)
}

// loadStorDBCfg loads the StorDB section of the configuration
func (cfg *CGRConfig) loadStorDBCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnDataDbCfg *DbJsonCfg
	if jsnDataDbCfg, err = jsnCfg.DbJsonCfg(STORDB_JSN); err != nil {
		return
	}
	return cfg.storDbCfg.loadFromJsonCfg(jsnDataDbCfg)
}

// loadFilterSCfg loads the FilterS section of the configuration
func (cfg *CGRConfig) loadFilterSCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnFilterSCfg *FilterSJsonCfg
	if jsnFilterSCfg, err = jsnCfg.FilterSJsonCfg(); err != nil {
		return
	}
	return cfg.filterSCfg.loadFromJsonCfg(jsnFilterSCfg)
}

// loadRalSCfg loads the RalS section of the configuration
func (cfg *CGRConfig) loadRalSCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnRALsCfg *RalsJsonCfg
	if jsnRALsCfg, err = jsnCfg.RalsJsonCfg(); err != nil {
		return
	}
	return cfg.ralsCfg.loadFromJsonCfg(jsnRALsCfg)
}

// loadSchedulerCfg loads the Scheduler section of the configuration
func (cfg *CGRConfig) loadSchedulerCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnSchedCfg *SchedulerJsonCfg
	if jsnSchedCfg, err = jsnCfg.SchedulerJsonCfg(); err != nil {
		return
	}
	return cfg.schedulerCfg.loadFromJsonCfg(jsnSchedCfg)
}

// loadCdrsCfg loads the Cdrs section of the configuration
func (cfg *CGRConfig) loadCdrsCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnCdrsCfg *CdrsJsonCfg
	if jsnCdrsCfg, err = jsnCfg.CdrsJsonCfg(); err != nil {
		return
	}
	return cfg.cdrsCfg.loadFromJsonCfg(jsnCdrsCfg)
}

// loadCdreCfg loads the Cdre section of the configuration
func (cfg *CGRConfig) loadCdreCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnCdreCfg map[string]*CdreJsonCfg
	if jsnCdreCfg, err = jsnCfg.CdreJsonCfgs(); err != nil {
		return
	}
	if jsnCdreCfg != nil {
		for profileName, jsnCdre1Cfg := range jsnCdreCfg {
			if _, hasProfile := cfg.CdreProfiles[profileName]; !hasProfile { // New profile, create before loading from json
				cfg.CdreProfiles[profileName] = new(CdreCfg)
				if profileName != utils.META_DEFAULT {
					cfg.CdreProfiles[profileName] = cfg.dfltCdreProfile.Clone() // Clone default so we do not inherit pointers
				}
			}
			if err = cfg.CdreProfiles[profileName].loadFromJsonCfg(jsnCdre1Cfg, cfg.generalCfg.RSRSep); err != nil { // Update the existing profile with content from json config
				return
			}
		}
	}
	return
}

// loadCdrcCfg loads the Cdrc section of the configuration
func (cfg *CGRConfig) loadCdrcCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnCdrcCfg []*CdrcJsonCfg
	if jsnCdrcCfg, err = jsnCfg.CdrcJsonCfg(); err != nil {
		return
	}
	if jsnCdrcCfg != nil {
		for _, jsnCrc1Cfg := range jsnCdrcCfg {
			if jsnCrc1Cfg.Id == nil || *jsnCrc1Cfg.Id == "" {
				return utils.ErrCDRCNoProfileID
			}
			if *jsnCrc1Cfg.Id == utils.META_DEFAULT {
				if cfg.dfltCdrcProfile == nil {
					cfg.dfltCdrcProfile = new(CdrcCfg)
				}
			}
			indxFound := -1 // Will be different than -1 if an instance with same id will be found
			pathFound := "" // Will be populated with the path where slice of cfgs was found
			var cdrcInstCfg *CdrcCfg
			for path := range cfg.CdrcProfiles {
				for i := range cfg.CdrcProfiles[path] {
					if cfg.CdrcProfiles[path][i].ID == *jsnCrc1Cfg.Id {
						indxFound = i
						pathFound = path
						cdrcInstCfg = cfg.CdrcProfiles[path][i]
						break
					}
				}
			}
			if cdrcInstCfg == nil {
				cdrcInstCfg = cfg.dfltCdrcProfile.Clone()
			}
			if err := cdrcInstCfg.loadFromJsonCfg(jsnCrc1Cfg, cfg.generalCfg.RSRSep); err != nil {
				return err
			}
			if cdrcInstCfg.CDRInPath == "" {
				return utils.ErrCDRCNoInPath
			}
			if _, hasDir := cfg.CdrcProfiles[cdrcInstCfg.CDRInPath]; !hasDir {
				cfg.CdrcProfiles[cdrcInstCfg.CDRInPath] = make([]*CdrcCfg, 0)
			}
			if indxFound != -1 { // Replace previous config so we have inheritance
				cfg.CdrcProfiles[pathFound][indxFound] = cdrcInstCfg
			} else {
				cfg.CdrcProfiles[cdrcInstCfg.CDRInPath] = append(cfg.CdrcProfiles[cdrcInstCfg.CDRInPath], cdrcInstCfg)
			}
		}
	}
	return
}

// loadSessionSCfg loads the SessionS section of the configuration
func (cfg *CGRConfig) loadSessionSCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnSessionSCfg *SessionSJsonCfg
	if jsnSessionSCfg, err = jsnCfg.SessionSJsonCfg(); err != nil {
		return
	}
	return cfg.sessionSCfg.loadFromJsonCfg(jsnSessionSCfg)
}

// loadFreeswitchAgentCfg loads the FreeswitchAgent section of the configuration
func (cfg *CGRConfig) loadFreeswitchAgentCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnSmFsCfg *FreeswitchAgentJsonCfg
	if jsnSmFsCfg, err = jsnCfg.FreeswitchAgentJsonCfg(); err != nil {
		return
	}
	return cfg.fsAgentCfg.loadFromJsonCfg(jsnSmFsCfg)
}

// loadKamAgentCfg loads the KamAgent section of the configuration
func (cfg *CGRConfig) loadKamAgentCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnKamAgentCfg *KamAgentJsonCfg
	if jsnKamAgentCfg, err = jsnCfg.KamAgentJsonCfg(); err != nil {
		return
	}
	return cfg.kamAgentCfg.loadFromJsonCfg(jsnKamAgentCfg)
}

// loadAsteriskAgentCfg loads the AsteriskAgent section of the configuration
func (cfg *CGRConfig) loadAsteriskAgentCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnSMAstCfg *AsteriskAgentJsonCfg
	if jsnSMAstCfg, err = jsnCfg.AsteriskAgentJsonCfg(); err != nil {
		return
	}
	return cfg.asteriskAgentCfg.loadFromJsonCfg(jsnSMAstCfg)
}

// loadDiameterAgentCfg loads the DiameterAgent section of the configuration
func (cfg *CGRConfig) loadDiameterAgentCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnDACfg *DiameterAgentJsonCfg
	if jsnDACfg, err = jsnCfg.DiameterAgentJsonCfg(); err != nil {
		return
	}
	return cfg.diameterAgentCfg.loadFromJsonCfg(jsnDACfg, cfg.GeneralCfg().RSRSep)
}

// loadRadiusAgentCfg loads the RadiusAgent section of the configuration
func (cfg *CGRConfig) loadRadiusAgentCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnRACfg *RadiusAgentJsonCfg
	if jsnRACfg, err = jsnCfg.RadiusAgentJsonCfg(); err != nil {
		return
	}
	return cfg.radiusAgentCfg.loadFromJsonCfg(jsnRACfg, cfg.GeneralCfg().RSRSep)
}

// loadDNSAgentCfg loads the DNSAgent section of the configuration
func (cfg *CGRConfig) loadDNSAgentCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnDNSCfg *DNSAgentJsonCfg
	if jsnDNSCfg, err = jsnCfg.DNSAgentJsonCfg(); err != nil {
		return
	}
	return cfg.dnsAgentCfg.loadFromJsonCfg(jsnDNSCfg, cfg.GeneralCfg().RSRSep)
}

// loadHttpAgentCfg loads the HttpAgent section of the configuration
func (cfg *CGRConfig) loadHttpAgentCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnHttpAgntCfg *[]*HttpAgentJsonCfg
	if jsnHttpAgntCfg, err = jsnCfg.HttpAgentJsonCfg(); err != nil {
		return
	}
	return cfg.httpAgentCfg.loadFromJsonCfg(jsnHttpAgntCfg, cfg.GeneralCfg().RSRSep)
}

// loadAttributeSCfg loads the AttributeS section of the configuration
func (cfg *CGRConfig) loadAttributeSCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnAttributeSCfg *AttributeSJsonCfg
	if jsnAttributeSCfg, err = jsnCfg.AttributeServJsonCfg(); err != nil {
		return
	}
	return cfg.attributeSCfg.loadFromJsonCfg(jsnAttributeSCfg)
}

// loadChargerSCfg loads the ChargerS section of the configuration
func (cfg *CGRConfig) loadChargerSCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnChargerSCfg *ChargerSJsonCfg
	if jsnChargerSCfg, err = jsnCfg.ChargerServJsonCfg(); err != nil {
		return
	}
	return cfg.chargerSCfg.loadFromJsonCfg(jsnChargerSCfg)
}

// loadResourceSCfg loads the ResourceS section of the configuration
func (cfg *CGRConfig) loadResourceSCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnRLSCfg *ResourceSJsonCfg
	if jsnRLSCfg, err = jsnCfg.ResourceSJsonCfg(); err != nil {
		return
	}
	return cfg.resourceSCfg.loadFromJsonCfg(jsnRLSCfg)
}

// loadStatSCfg loads the StatS section of the configuration
func (cfg *CGRConfig) loadStatSCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnStatSCfg *StatServJsonCfg
	if jsnStatSCfg, err = jsnCfg.StatSJsonCfg(); err != nil {
		return
	}
	return cfg.statsCfg.loadFromJsonCfg(jsnStatSCfg)
}

// loadThresholdSCfg loads the ThresholdS section of the configuration
func (cfg *CGRConfig) loadThresholdSCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnThresholdSCfg *ThresholdSJsonCfg
	if jsnThresholdSCfg, err = jsnCfg.ThresholdSJsonCfg(); err != nil {
		return
	}
	return cfg.thresholdSCfg.loadFromJsonCfg(jsnThresholdSCfg)
}

// loadSupplierSCfg loads the SupplierS section of the configuration
func (cfg *CGRConfig) loadSupplierSCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnSupplierSCfg *SupplierSJsonCfg
	if jsnSupplierSCfg, err = jsnCfg.SupplierSJsonCfg(); err != nil {
		return
	}
	return cfg.supplierSCfg.loadFromJsonCfg(jsnSupplierSCfg)
}

// loadLoaderSCfg loads the LoaderS section of the configuration
func (cfg *CGRConfig) loadLoaderSCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnLoaderCfg []*LoaderJsonCfg
	if jsnLoaderCfg, err = jsnCfg.LoaderJsonCfg(); err != nil {
		return
	}
	if jsnLoaderCfg != nil {
		// cfg.loaderCfg = make([]*LoaderSCfg, len(jsnLoaderCfg))
		for _, profile := range jsnLoaderCfg {
			loadSCfgp := NewDfltLoaderSCfg()
			loadSCfgp.loadFromJsonCfg(profile, cfg.GeneralCfg().RSRSep)
			cfg.loaderCfg = append(cfg.loaderCfg, loadSCfgp) // use apend so the loaderS profile to be loaded from multiple files
		}
	}
	return
}

// loadMailerCfg loads the Mailer section of the configuration
func (cfg *CGRConfig) loadMailerCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnMailerCfg *MailerJsonCfg
	if jsnMailerCfg, err = jsnCfg.MailerJsonCfg(); err != nil {
		return
	}
	return cfg.mailerCfg.loadFromJsonCfg(jsnMailerCfg)
}

// loadSureTaxCfg loads the SureTax section of the configuration
func (cfg *CGRConfig) loadSureTaxCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnSureTaxCfg *SureTaxJsonCfg
	if jsnSureTaxCfg, err = jsnCfg.SureTaxJsonCfg(); err != nil {
		return
	}
	return cfg.sureTaxCfg.loadFromJsonCfg(jsnSureTaxCfg)
}

// loadDispatcherSCfg loads the DispatcherS section of the configuration
func (cfg *CGRConfig) loadDispatcherSCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnDispatcherSCfg *DispatcherSJsonCfg
	if jsnDispatcherSCfg, err = jsnCfg.DispatcherSJsonCfg(); err != nil {
		return
	}
	return cfg.dispatcherSCfg.loadFromJsonCfg(jsnDispatcherSCfg)
}

// loadLoaderCgrCfg loads the Loader section of the configuration
func (cfg *CGRConfig) loadLoaderCgrCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnLoaderCgrCfg *LoaderCfgJson
	if jsnLoaderCgrCfg, err = jsnCfg.LoaderCfgJson(); err != nil {
		return
	}
	return cfg.loaderCgrCfg.loadFromJsonCfg(jsnLoaderCgrCfg)
}

// loadMigratorCgrCfg loads the Migrator section of the configuration
func (cfg *CGRConfig) loadMigratorCgrCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnMigratorCgrCfg *MigratorCfgJson
	if jsnMigratorCgrCfg, err = jsnCfg.MigratorCfgJson(); err != nil {
		return
	}
	return cfg.migratorCgrCfg.loadFromJsonCfg(jsnMigratorCgrCfg)
}

// loadTlsCgrCfg loads the Tls section of the configuration
func (cfg *CGRConfig) loadTlsCgrCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnTlsCgrCfg *TlsJsonCfg
	if jsnTlsCgrCfg, err = jsnCfg.TlsCfgJson(); err != nil {
		return
	}
	return cfg.tlsCfg.loadFromJsonCfg(jsnTlsCgrCfg)
}

// loadAnalyzerCgrCfg loads the Analyzer section of the configuration
func (cfg *CGRConfig) loadAnalyzerCgrCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnAnalyzerCgrCfg *AnalyzerSJsonCfg
	if jsnAnalyzerCgrCfg, err = jsnCfg.AnalyzerCfgJson(); err != nil {
		return
	}
	return cfg.analyzerSCfg.loadFromJsonCfg(jsnAnalyzerCgrCfg)
}

// loadApierCfg loads the Apier section of the configuration
func (cfg *CGRConfig) loadApierCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnApierCfg *ApierJsonCfg
	if jsnApierCfg, err = jsnCfg.ApierCfgJson(); err != nil {
		return
	}
	return cfg.apier.loadFromJsonCfg(jsnApierCfg)
}

// loadErsCfg loads the Ers section of the configuration
func (cfg *CGRConfig) loadErsCfg(jsnCfg *CgrJsonCfg) (err error) {
	var jsnERsCfg *ERsJsonCfg
	if jsnERsCfg, err = jsnCfg.ERsJsonCfg(); err != nil {
		return
	}
	return cfg.ersCfg.loadFromJsonCfg(jsnERsCfg, cfg.GeneralCfg().RSRSep, cfg.dfltEvRdr)
}

// SureTaxCfg use locking to retrieve the configuration, possibility later for runtime reload
func (cfg *CGRConfig) SureTaxCfg() *SureTaxCfg {
	cfg.lks[SURETAX_JSON].Lock()
	defer cfg.lks[SURETAX_JSON].Unlock()
	return cfg.sureTaxCfg
}

// DiameterAgentCfg returns the config for Diameter Agent
func (cfg *CGRConfig) DiameterAgentCfg() *DiameterAgentCfg {
	cfg.lks[DA_JSN].Lock()
	defer cfg.lks[DA_JSN].Unlock()
	return cfg.diameterAgentCfg
}

// RadiusAgentCfg returns the config for Radius Agent
func (cfg *CGRConfig) RadiusAgentCfg() *RadiusAgentCfg {
	cfg.lks[RA_JSN].Lock()
	defer cfg.lks[RA_JSN].Unlock()
	return cfg.radiusAgentCfg
}

// DNSAgentCfg returns the config for DNS Agent
func (cfg *CGRConfig) DNSAgentCfg() *DNSAgentCfg {
	cfg.lks[DNSAgentJson].Lock()
	defer cfg.lks[DNSAgentJson].Unlock()
	return cfg.dnsAgentCfg
}

// AttributeSCfg returns the config for AttributeS
func (cfg *CGRConfig) AttributeSCfg() *AttributeSCfg {
	cfg.lks[ATTRIBUTE_JSN].Lock()
	defer cfg.lks[ATTRIBUTE_JSN].Unlock()
	return cfg.attributeSCfg
}

// ChargerSCfg returns the config for ChargerS
func (cfg *CGRConfig) ChargerSCfg() *ChargerSCfg {
	cfg.lks[ChargerSCfgJson].Lock()
	defer cfg.lks[ChargerSCfgJson].Unlock()
	return cfg.chargerSCfg
}

// ResourceSCfg returns the config for ResourceS
func (cfg *CGRConfig) ResourceSCfg() *ResourceSConfig { // not done
	cfg.lks[RESOURCES_JSON].Lock()
	defer cfg.lks[RESOURCES_JSON].Unlock()
	return cfg.resourceSCfg
}

// StatSCfg returns the config for StatS
func (cfg *CGRConfig) StatSCfg() *StatSCfg { // not done
	cfg.lks[STATS_JSON].Lock()
	defer cfg.lks[STATS_JSON].Unlock()
	return cfg.statsCfg
}

// ThresholdSCfg returns the config for ThresholdS
func (cfg *CGRConfig) ThresholdSCfg() *ThresholdSCfg {
	cfg.lks[THRESHOLDS_JSON].Lock()
	defer cfg.lks[THRESHOLDS_JSON].Unlock()
	return cfg.thresholdSCfg
}

// SupplierSCfg returns the config for SupplierS
func (cfg *CGRConfig) SupplierSCfg() *SupplierSCfg {
	cfg.lks[SupplierSJson].Lock()
	defer cfg.lks[SupplierSJson].Unlock()
	return cfg.supplierSCfg
}

// SessionSCfg returns the config for SessionS
func (cfg *CGRConfig) SessionSCfg() *SessionSCfg {
	cfg.lks[SessionSJson].Lock()
	defer cfg.lks[SessionSJson].Unlock()
	return cfg.sessionSCfg
}

func (self *CGRConfig) FsAgentCfg() *FsAgentCfg {
	return self.fsAgentCfg
}

func (self *CGRConfig) KamAgentCfg() *KamAgentCfg {
	return self.kamAgentCfg
}

// ToDo: fix locking here
func (self *CGRConfig) AsteriskAgentCfg() *AsteriskAgentCfg {
	return self.asteriskAgentCfg
}

func (self *CGRConfig) HttpAgentCfg() []*HttpAgentCfg {
	return self.httpAgentCfg
}

func (cfg *CGRConfig) FilterSCfg() *FilterSCfg {
	return cfg.filterSCfg
}

// CacheCfg returns the config for Cache
func (cfg *CGRConfig) CacheCfg() CacheCfg {
	cfg.lks[CACHE_JSN].Lock()
	defer cfg.lks[CACHE_JSN].Unlock()
	return cfg.cacheCfg
}

func (cfg *CGRConfig) LoaderCfg() []*LoaderSCfg {
	return cfg.loaderCfg
}

func (cfg *CGRConfig) DispatcherSCfg() *DispatcherSCfg {
	return cfg.dispatcherSCfg
}

func (cfg *CGRConfig) LoaderCgrCfg() *LoaderCgrCfg {
	return cfg.loaderCgrCfg
}

func (cfg *CGRConfig) MigratorCgrCfg() *MigratorCgrCfg {
	return cfg.migratorCgrCfg
}

// SchedulerCfg returns the config for Scheduler
func (cfg *CGRConfig) SchedulerCfg() *SchedulerCfg {
	cfg.lks[SCHEDULER_JSN].Lock()
	defer cfg.lks[SCHEDULER_JSN].Unlock()
	return cfg.schedulerCfg
}

func (cfg *CGRConfig) DataDbCfg() *DataDbCfg {
	return cfg.dataDbCfg
}

func (cfg *CGRConfig) StorDbCfg() *StorDbCfg {
	return cfg.storDbCfg
}

func (cfg *CGRConfig) GeneralCfg() *GeneralCfg {
	return cfg.generalCfg
}

func (cfg *CGRConfig) TlsCfg() *TlsCfg {
	return cfg.tlsCfg
}

func (cfg *CGRConfig) ListenCfg() *ListenCfg {
	return cfg.listenCfg
}

func (cfg *CGRConfig) HTTPCfg() *HTTPCfg {
	return cfg.httpCfg
}

// RalsCfg returns the config for Ral Service
func (cfg *CGRConfig) RalsCfg() *RalsCfg {
	cfg.lks[RALS_JSN].Lock()
	defer cfg.lks[RALS_JSN].Unlock()
	return cfg.ralsCfg
}

// CdrsCfg returns the config for CDR Server
func (cfg *CGRConfig) CdrsCfg() *CdrsCfg {
	cfg.lks[CDRS_JSN].Lock()
	defer cfg.lks[CDRS_JSN].Unlock()
	return cfg.cdrsCfg
}

func (cfg *CGRConfig) MailerCfg() *MailerCfg {
	return cfg.mailerCfg
}

func (cfg *CGRConfig) AnalyzerSCfg() *AnalyzerSCfg {
	return cfg.analyzerSCfg
}

func (cfg *CGRConfig) ApierCfg() *ApierCfg {
	return cfg.apier
}

// ERsCfg reads the EventReader configuration
func (cfg *CGRConfig) ERsCfg() *ERsCfg {
	cfg.lks[ERsJson].RLock()
	defer cfg.lks[ERsJson].RUnlock()
	return cfg.ersCfg
}

func (cfg *CGRConfig) GetReloadChan(sectID string) chan struct{} {
	return cfg.rldChans[sectID]
}

// Call implements rpcclient.RpcClientConnection interface for internal RPC
func (cSv1 *CGRConfig) Call(serviceMethod string,
	args interface{}, reply interface{}) error {
	return utils.APIerRPCCall(cSv1, serviceMethod, args, reply)
}

// ToDo: move this structure in utils as is used in other packages
type StringWithArgDispatcher struct {
	*utils.ArgDispatcher
	utils.TenantArg
	Section string
}

//V1GetConfigSection will retrieve from CGRConfig a section
func (cfg *CGRConfig) V1GetConfigSection(args *StringWithArgDispatcher, reply *map[string]interface{}) (err error) {
	var jsonString string
	switch args.Section {
	case GENERAL_JSN:
		jsonString = utils.ToJSON(cfg.GeneralCfg())
	case DATADB_JSN:
		jsonString = utils.ToJSON(cfg.DataDbCfg())
	case STORDB_JSN:
		jsonString = utils.ToJSON(cfg.StorDbCfg())
	case TlsCfgJson:
		jsonString = utils.ToJSON(cfg.TlsCfg())
	case CACHE_JSN:
		jsonString = utils.ToJSON(cfg.CacheCfg())
	case LISTEN_JSN:
		jsonString = utils.ToJSON(cfg.ListenCfg())
	case HTTP_JSN:
		jsonString = utils.ToJSON(cfg.HTTPCfg())
	case FILTERS_JSON:
		jsonString = utils.ToJSON(cfg.FilterSCfg())
	case RALS_JSN:
		jsonString = utils.ToJSON(cfg.RalsCfg())
	case SCHEDULER_JSN:
		jsonString = utils.ToJSON(cfg.SchedulerCfg())
	case CDRS_JSN:
		jsonString = utils.ToJSON(cfg.CdrsCfg())
	case SessionSJson:
		jsonString = utils.ToJSON(cfg.SessionSCfg())
	case FS_JSN:
		jsonString = utils.ToJSON(cfg.FsAgentCfg())
	case KamailioAgentJSN:
		jsonString = utils.ToJSON(cfg.KamAgentCfg())
	case AsteriskAgentJSN:
		jsonString = utils.ToJSON(cfg.AsteriskAgentCfg())
	case DA_JSN:
		jsonString = utils.ToJSON(cfg.DiameterAgentCfg())
	case RA_JSN:
		jsonString = utils.ToJSON(cfg.RadiusAgentCfg())
	case DNSAgentJson:
		jsonString = utils.ToJSON(cfg.DNSAgentCfg())
	case ATTRIBUTE_JSN:
		jsonString = utils.ToJSON(cfg.AttributeSCfg())
	case ChargerSCfgJson:
		jsonString = utils.ToJSON(cfg.ChargerSCfg())
	case RESOURCES_JSON:
		jsonString = utils.ToJSON(cfg.ResourceSCfg())
	case STATS_JSON:
		jsonString = utils.ToJSON(cfg.StatSCfg())
	case THRESHOLDS_JSON:
		jsonString = utils.ToJSON(cfg.ThresholdSCfg())
	case SupplierSJson:
		jsonString = utils.ToJSON(cfg.SupplierSCfg())
	case SURETAX_JSON:
		jsonString = utils.ToJSON(cfg.SureTaxCfg())
	case DispatcherSJson:
		jsonString = utils.ToJSON(cfg.DispatcherSCfg())
	case LoaderJson:
		jsonString = utils.ToJSON(cfg.LoaderCfg())
	case CgrLoaderCfgJson:
		jsonString = utils.ToJSON(cfg.LoaderCgrCfg())
	case CgrMigratorCfgJson:
		jsonString = utils.ToJSON(cfg.MigratorCgrCfg())
	case Apier:
		jsonString = utils.ToJSON(cfg.ApierCfg())
	case CDRC_JSN:
		jsonString = utils.ToJSON(cfg.CdrcProfiles)
	case CDRE_JSN:
		jsonString = utils.ToJSON(cfg.CdreProfiles)
	case ERsJson:
		jsonString = utils.ToJSON(cfg.ERsCfg())
	default:
		return errors.New("Invalid section")
	}
	json.Unmarshal([]byte(jsonString), reply)
	return
}

type ConfigReloadWithArgDispatcher struct {
	*utils.ArgDispatcher
	utils.TenantArg
	Path    string
	Section string
}

func (cfg *CGRConfig) rLockSections() {
	for _, lk := range cfg.lks {
		lk.RLock()
	}
}

func (cfg *CGRConfig) rUnlockSections() {
	for _, lk := range cfg.lks {
		lk.RUnlock()
	}
}

func (cfg *CGRConfig) lockSections() {
	for _, lk := range cfg.lks {
		lk.Lock()
	}
}

func (cfg *CGRConfig) unlockSections() {
	for _, lk := range cfg.lks {
		lk.Unlock()
	}
}

// V1ReloadConfig reloads the configuration
func (cfg *CGRConfig) V1ReloadConfig(args *ConfigReloadWithArgDispatcher, reply *string) (err error) {
	if missing := utils.MissingStructFields(args, []string{"Path"}); len(missing) != 0 {
		return utils.NewErrMandatoryIeMissing(missing...)
	}
	if err = cfg.loadConfig(args.Path, args.Section); err != nil {
		return
	}
	//  lock all sections
	cfg.rLockSections()

	err = cfg.checkConfigSanity()

	cfg.rUnlockSections() // unlock before checking the error

	if err != nil {
		return
	}

	if err = cfg.reloadSection(args.Section); err != nil {
		return
	}
	*reply = utils.OK
	return
}

func (cfg *CGRConfig) reloadSection(section string) (err error) {
	var fall bool
	switch section {
	default:
		return fmt.Errorf("Invalid section: <%s>", section)
	case utils.EmptyString, "*all": // do constant
		fall = true
		fallthrough
	case GENERAL_JSN: // nothing to reload
		if !fall {
			break
		}
		fallthrough
	case DATADB_JSN:
		if !fall {
			break
		}
		fallthrough
	case STORDB_JSN:
		if !fall {
			break
		}
		fallthrough
	case LISTEN_JSN:
		if !fall {
			break
		}
		fallthrough
	case TlsCfgJson:
		if !fall {
			break
		}
		fallthrough
	case HTTP_JSN:
		if !fall {
			break
		}
		fallthrough
	case SCHEDULER_JSN:
		cfg.rldChans[SCHEDULER_JSN] <- struct{}{}
		if !fall {
			break
		}
		fallthrough
	case CACHE_JSN: // no need to reload
		if !fall {
			break
		}
		fallthrough
	case FILTERS_JSON: // no need to reload
		if !fall {
			break
		}
		fallthrough
	case RALS_JSN:
		cfg.rldChans[RALS_JSN] <- struct{}{}
		if !fall {
			break
		}
		fallthrough
	case CDRS_JSN:
		cfg.rldChans[CDRS_JSN] <- struct{}{}
		if !fall {
			break
		}
		fallthrough
	case CDRE_JSN:
		if !fall {
			break
		}
		fallthrough
	case CDRC_JSN:
		if !fall {
			break
		}
		fallthrough
	case ERsJson:
		cfg.rldChans[ERsJson] <- struct{}{}
		if !fall {
			break
		}
		fallthrough
	case SessionSJson:
		cfg.rldChans[SessionSJson] <- struct{}{}
		if !fall {
			break
		}
		fallthrough
	case AsteriskAgentJSN:
		if !fall {
			break
		}
		fallthrough
	case FreeSWITCHAgentJSN:
		if !fall {
			break
		}
		fallthrough
	case KamailioAgentJSN:
		if !fall {
			break
		}
		fallthrough
	case DA_JSN:
		if !fall {
			break
		}
		fallthrough
	case RA_JSN:
		if !fall {
			break
		}
		fallthrough
	case HttpAgentJson:
		if !fall {
			break
		}
		fallthrough
	case DNSAgentJson:
		cfg.rldChans[DNSAgentJson] <- struct{}{}
		if !fall {
			break
		}
		fallthrough
	case ATTRIBUTE_JSN:
		cfg.rldChans[ATTRIBUTE_JSN] <- struct{}{}
		if !fall {
			break
		}
		fallthrough
	case ChargerSCfgJson:
		cfg.rldChans[ChargerSCfgJson] <- struct{}{}
		if !fall {
			break
		}
		fallthrough
	case RESOURCES_JSON:
		cfg.rldChans[RESOURCES_JSON] <- struct{}{}
		if !fall {
			break
		}
		fallthrough
	case STATS_JSON:
		cfg.rldChans[STATS_JSON] <- struct{}{}
		if !fall {
			break
		}
		fallthrough
	case THRESHOLDS_JSON:
		cfg.rldChans[THRESHOLDS_JSON] <- struct{}{}
		if !fall {
			break
		}
		fallthrough
	case SupplierSJson:
		cfg.rldChans[SupplierSJson] <- struct{}{}
		if !fall {
			break
		}
		fallthrough
	case LoaderJson:
		if !fall {
			break
		}
		fallthrough
	case MAILER_JSN:
		if !fall {
			break
		}
		fallthrough
	case SURETAX_JSON: // doesn't need to be reloaded
		if !fall {
			break
		}
		fallthrough
	case CgrLoaderCfgJson:
		if !fall {
			break
		}
		fallthrough
	case CgrMigratorCfgJson:
		if !fall {
			break
		}
		fallthrough
	case DispatcherSJson:
		if !fall {
			break
		}
		fallthrough
	case AnalyzerCfgJson:
		if !fall {
			break
		}
		fallthrough
	case Apier:
		if !fall {
			break
		}
	}
	return
}

func (cfg *CGRConfig) loadConfig(path, section string) (err error) {
	var loadFuncs []func(*CgrJsonCfg) error
	var fall bool
	switch section {
	default:
		return fmt.Errorf("Invalid section: <%s>", section)
	case utils.EmptyString, "*all": // do constant
		fall = true
		fallthrough
	case GENERAL_JSN:
		cfg.lks[GENERAL_JSN].Lock()
		defer cfg.lks[GENERAL_JSN].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadGeneralCfg)
		if !fall {
			break
		}
		fallthrough
	case DATADB_JSN:
		cfg.lks[DATADB_JSN].Lock()
		defer cfg.lks[DATADB_JSN].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadDataDBCfg)
		if !fall {
			break
		}
		fallthrough
	case STORDB_JSN:
		cfg.lks[STORDB_JSN].Lock()
		defer cfg.lks[STORDB_JSN].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadStorDBCfg)
		if !fall {
			break
		}
		fallthrough
	case LISTEN_JSN:
		cfg.lks[LISTEN_JSN].Lock()
		defer cfg.lks[LISTEN_JSN].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadListenCfg)
		if !fall {
			break
		}
		fallthrough
	case TlsCfgJson:
		cfg.lks[TlsCfgJson].Lock()
		defer cfg.lks[TlsCfgJson].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadTlsCgrCfg)
		if !fall {
			break
		}
		fallthrough
	case HTTP_JSN:
		cfg.lks[HTTP_JSN].Lock()
		defer cfg.lks[HTTP_JSN].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadHttpCfg)
		if !fall {
			break
		}
		fallthrough
	case SCHEDULER_JSN:
		cfg.lks[SCHEDULER_JSN].Lock()
		defer cfg.lks[SCHEDULER_JSN].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadSchedulerCfg)
		if !fall {
			break
		}
		fallthrough
	case CACHE_JSN:
		cfg.lks[CACHE_JSN].Lock()
		defer cfg.lks[CACHE_JSN].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadCacheCfg)
		if !fall {
			break
		}
		fallthrough
	case FILTERS_JSON:
		cfg.lks[FILTERS_JSON].Lock()
		defer cfg.lks[FILTERS_JSON].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadFilterSCfg)
		if !fall {
			break
		}
		fallthrough
	case RALS_JSN:
		cfg.lks[RALS_JSN].Lock()
		defer cfg.lks[RALS_JSN].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadRalSCfg)
		if !fall {
			break
		}
		fallthrough
	case CDRS_JSN:
		cfg.lks[CDRS_JSN].Lock()
		defer cfg.lks[CDRS_JSN].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadCdrsCfg)
		if !fall {
			break
		}
		fallthrough
	case CDRE_JSN:
		cfg.lks[CDRE_JSN].Lock()
		defer cfg.lks[CDRE_JSN].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadCdreCfg)
		if !fall {
			break
		}
		fallthrough
	case CDRC_JSN:
		cfg.lks[CDRC_JSN].Lock()
		defer cfg.lks[CDRC_JSN].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadCdrcCfg)
		if !fall {
			break
		}
		fallthrough
	case ERsJson:
		cfg.lks[ERsJson].Lock()
		defer cfg.lks[ERsJson].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadErsCfg)
		if !fall {
			break
		}
		fallthrough
	case SessionSJson:
		cfg.lks[SessionSJson].Lock()
		defer cfg.lks[SessionSJson].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadSessionSCfg)
		if !fall {
			break
		}
		fallthrough
	case AsteriskAgentJSN:
		cfg.lks[AsteriskAgentJSN].Lock()
		defer cfg.lks[AsteriskAgentJSN].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadAsteriskAgentCfg)
		if !fall {
			break
		}
		fallthrough
	case FreeSWITCHAgentJSN:
		cfg.lks[FreeSWITCHAgentJSN].Lock()
		defer cfg.lks[FreeSWITCHAgentJSN].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadFreeswitchAgentCfg)
		if !fall {
			break
		}
		fallthrough
	case KamailioAgentJSN:
		cfg.lks[KamailioAgentJSN].Lock()
		defer cfg.lks[KamailioAgentJSN].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadKamAgentCfg)
		if !fall {
			break
		}
		fallthrough
	case DA_JSN:
		cfg.lks[DA_JSN].Lock()
		defer cfg.lks[DA_JSN].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadDiameterAgentCfg)
		if !fall {
			break
		}
		fallthrough
	case RA_JSN:
		cfg.lks[RA_JSN].Lock()
		defer cfg.lks[RA_JSN].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadRadiusAgentCfg)
		if !fall {
			break
		}
		fallthrough
	case HttpAgentJson:
		cfg.lks[HttpAgentJson].Lock()
		defer cfg.lks[HttpAgentJson].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadHttpAgentCfg)
		if !fall {
			break
		}
		fallthrough
	case DNSAgentJson:
		cfg.lks[DNSAgentJson].Lock()
		defer cfg.lks[DNSAgentJson].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadDNSAgentCfg)
		if !fall {
			break
		}
		fallthrough
	case ATTRIBUTE_JSN:
		cfg.lks[ATTRIBUTE_JSN].Lock()
		defer cfg.lks[ATTRIBUTE_JSN].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadAttributeSCfg)
		if !fall {
			break
		}
		fallthrough
	case ChargerSCfgJson:
		cfg.lks[ChargerSCfgJson].Lock()
		defer cfg.lks[ChargerSCfgJson].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadChargerSCfg)
		if !fall {
			break
		}
		fallthrough
	case RESOURCES_JSON:
		cfg.lks[RESOURCES_JSON].Lock()
		defer cfg.lks[RESOURCES_JSON].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadResourceSCfg)
		if !fall {
			break
		}
		fallthrough
	case STATS_JSON:
		cfg.lks[STATS_JSON].Lock()
		defer cfg.lks[STATS_JSON].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadStatSCfg)
		if !fall {
			break
		}
		fallthrough
	case THRESHOLDS_JSON:
		cfg.lks[THRESHOLDS_JSON].Lock()
		defer cfg.lks[THRESHOLDS_JSON].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadThresholdSCfg)
		if !fall {
			break
		}
		fallthrough
	case SupplierSJson:
		cfg.lks[SupplierSJson].Lock()
		defer cfg.lks[SupplierSJson].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadSupplierSCfg)
		if !fall {
			break
		}
		fallthrough
	case LoaderJson:
		cfg.lks[LoaderJson].Lock()
		defer cfg.lks[LoaderJson].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadLoaderSCfg)
		if !fall {
			break
		}
		fallthrough
	case MAILER_JSN:
		cfg.lks[MAILER_JSN].Lock()
		defer cfg.lks[MAILER_JSN].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadMailerCfg)
		if !fall {
			break
		}
		fallthrough
	case SURETAX_JSON:
		cfg.lks[SURETAX_JSON].Lock()
		defer cfg.lks[SURETAX_JSON].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadSureTaxCfg)
		if !fall {
			break
		}
		fallthrough
	case CgrLoaderCfgJson:
		cfg.lks[CgrLoaderCfgJson].Lock()
		defer cfg.lks[CgrLoaderCfgJson].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadLoaderCgrCfg)
		if !fall {
			break
		}
		fallthrough
	case CgrMigratorCfgJson:
		cfg.lks[CgrMigratorCfgJson].Lock()
		defer cfg.lks[CgrMigratorCfgJson].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadMigratorCgrCfg)
		if !fall {
			break
		}
		fallthrough
	case DispatcherSJson:
		cfg.lks[DispatcherSJson].Lock()
		defer cfg.lks[DispatcherSJson].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadDispatcherSCfg)
		if !fall {
			break
		}
		fallthrough
	case AnalyzerCfgJson:
		cfg.lks[AnalyzerCfgJson].Lock()
		defer cfg.lks[AnalyzerCfgJson].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadAnalyzerCgrCfg)
		if !fall {
			break
		}
		fallthrough
	case Apier:
		cfg.lks[Apier].Lock()
		defer cfg.lks[Apier].Unlock()
		loadFuncs = append(loadFuncs, cfg.loadApierCfg)
	}
	return cfg.loadConfigFromPath(path, loadFuncs)
}

func (*CGRConfig) loadConfigFromReader(rdr io.Reader, loadFuncs []func(jsnCfg *CgrJsonCfg) error) (err error) {
	jsnCfg := new(CgrJsonCfg)
	var rjr *rjReader
	if rjr, err = NewRjReader(rdr); err != nil {
		return
	}
	defer rjr.Close() // make sure we make the buffer nil
	if err = rjr.Decode(jsnCfg); err != nil {
		return
	}
	for _, loadFunc := range loadFuncs {
		if err = loadFunc(jsnCfg); err != nil {
			return
		}
	}
	return
}

// Reads all .json files out of a folder/subfolders and loads them up in lexical order
func (cfg *CGRConfig) loadConfigFromPath(path string, loadFuncs []func(jsnCfg *CgrJsonCfg) error) (err error) {
	if isUrl(path) {
		return cfg.loadConfigFromHttp(path, loadFuncs) // prefix protocol
	}
	var fi os.FileInfo
	if fi, err = os.Stat(path); err != nil {
		if os.IsNotExist(err) {
			return utils.ErrPathNotReachable(path)
		}
		return
	} else if !fi.IsDir() && path != utils.CONFIG_PATH { // If config dir defined, needs to exist, not checking for default
		return fmt.Errorf("path: %s not a directory", path)
	}
	if fi.IsDir() {
		return cfg.loadConfigFromFolder(path, loadFuncs)
	}
	return
}

func (cfg *CGRConfig) loadConfigFromFolder(cfgDir string, loadFuncs []func(jsnCfg *CgrJsonCfg) error) (err error) {
	jsonFilesFound := false
	if err = filepath.Walk(cfgDir, func(path string, info os.FileInfo, err error) (werr error) {
		if !info.IsDir() || isHidden(info.Name()) { // also ignore hidden files and folders
			return
		}
		var cfgFiles []string
		if cfgFiles, werr = filepath.Glob(filepath.Join(path, "*.json")); werr != nil {
			return
		}
		if cfgFiles == nil { // No need of processing further since there are no config files in the folder
			return
		}
		if !jsonFilesFound {
			jsonFilesFound = true
		}
		for _, jsonFilePath := range cfgFiles {
			var cfgFile *os.File
			cfgFile, werr = os.Open(jsonFilePath)
			if werr != nil {
				return
			}

			werr = cfg.loadConfigFromReader(cfgFile, loadFuncs)
			cfgFile.Close()
			if werr != nil {
				return
			}
		}
		return
	}); err != nil {
		return
	}
	if !jsonFilesFound {
		return fmt.Errorf("No config file found on path %s", cfgDir)
	}
	return
}

func (cfg *CGRConfig) loadConfigFromHttp(urlPaths string, loadFuncs []func(jsnCfg *CgrJsonCfg) error) (err error) {
	for _, urlPath := range strings.Split(urlPaths, utils.INFIELD_SEP) {
		if _, err = url.ParseRequestURI(urlPath); err != nil {
			return
		}
		var myClient = &http.Client{
			Timeout: CgrConfig().GeneralCfg().ReplyTimeout,
		}
		var cfgReq *http.Response
		cfgReq, err = myClient.Get(urlPath)
		if err != nil {
			return utils.ErrPathNotReachable(urlPath)
		}
		err = cfg.loadConfigFromReader(cfgReq.Body, loadFuncs)
		cfgReq.Body.Close()
		if err != nil {
			return
		}
	}
	return
}

// populates the config locks and the reload channels
func (cfg *CGRConfig) initChanels() {
	cfg.lks = make(map[string]*sync.RWMutex)
	cfg.rldChans = make(map[string]chan struct{})
	for _, section := range []string{GENERAL_JSN, DATADB_JSN, STORDB_JSN, LISTEN_JSN, TlsCfgJson, HTTP_JSN, SCHEDULER_JSN, CACHE_JSN, FILTERS_JSON, RALS_JSN,
		CDRS_JSN, CDRE_JSN, CDRC_JSN, ERsJson, SessionSJson, AsteriskAgentJSN, FreeSWITCHAgentJSN, KamailioAgentJSN,
		DA_JSN, RA_JSN, HttpAgentJson, DNSAgentJson, ATTRIBUTE_JSN, ChargerSCfgJson, RESOURCES_JSON, STATS_JSON, THRESHOLDS_JSON,
		SupplierSJson, LoaderJson, MAILER_JSN, SURETAX_JSON, CgrLoaderCfgJson, CgrMigratorCfgJson, DispatcherSJson, AnalyzerCfgJson, Apier} {
		cfg.lks[section] = new(sync.RWMutex)
		cfg.rldChans[section] = make(chan struct{}, 1)
	}
}
