/*
 * Copyright 2008-2009 the original author or authors.
 *
 * 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.
 */
package net.hasor.rsf.address.route.rule;
import net.hasor.core.Settings;
import net.hasor.core.XmlNode;
import net.hasor.core.setting.InputStreamSettings;
import net.hasor.core.setting.StreamType;
import net.hasor.rsf.RsfEnvironment;
import net.hasor.rsf.RsfSettings;
import net.hasor.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 路由规则解析器
 * @version : 2015年3月29日
 * @author 赵永春 (zyc@hasor.net)
 */
public class RuleParser {
    protected Logger                logger      = LoggerFactory.getLogger(getClass());
    private   Map<String, Class<?>> ruleTypeMap = null;

    public RuleParser(RsfEnvironment rsfEnvironment) {
        this.ruleTypeMap = new HashMap<>();
        RsfSettings rsfSettings = rsfEnvironment.getSettings();
        XmlNode[] flowControlNodes = rsfSettings.getXmlNodeArray("hasor.rsfConfig.route.flowcontrol");
        if (flowControlNodes != null) {
            for (XmlNode node : flowControlNodes) {
                List<XmlNode> ruleTypes = node.getChildren();
                ruleTypes = (ruleTypes == null) ? new ArrayList<>(0) : ruleTypes;
                for (XmlNode ruleType : ruleTypes) {
                    String ruleID = ruleType.getName().trim().toLowerCase();
                    String ruleClassName = ruleType.getText().trim();
                    try {
                        Class<?> ruleClass = rsfEnvironment.getClassLoader().loadClass(ruleClassName);
                        ruleTypeMap.put(ruleID, ruleClass);
                    } catch (Throwable e) {
                        logger.error("rule {} load type error -> {}", ruleID, e.getMessage());
                    }
                }
            }
        }
    }

    /**解析规则文本为{@link Settings}*/
    public Rule ruleSettings(String rawRoute) {
        if (StringUtils.isBlank(rawRoute) || !rawRoute.startsWith("<flowControl") || !rawRoute.endsWith("</flowControl>")) {
            logger.info("rule raw format error.");
            return null;
        }
        //
        try {
            InputStreamSettings ruleSettings = new InputStreamSettings();
            ruleSettings.addReader(new StringReader("<xml>" + rawRoute + "</xml>"), StreamType.Xml);
            ruleSettings.loadSettings();
            return ruleSettings(ruleSettings);
        } catch (Exception e) {
            logger.error("rule raw format error. -> {}", e.getMessage(), e);
        }
        return null;
    }

    /**解析规则文本为{@link Settings}*/
    public Rule ruleSettings(Settings ruleSettings) {
        if (ruleSettings == null) {
            logger.info("ruleSettings is null.");
            return null;
        }
        //
        AbstractRule ruleObject = null;
        try {
            String ruleID = ruleSettings.getString("flowControl.type");
            boolean ruleEnable = ruleSettings.getBoolean("flowControl.enable", false);
            //
            if (StringUtils.isBlank(ruleID)) {
                logger.info("flowControl.type is null.");
                return null;
            }
            //
            ruleID = ruleID.trim().toLowerCase();
            logger.info("process rule '{}' -> {}.", ruleID, ruleEnable);
            Class<?> ruleClass = ruleTypeMap.get(ruleID);
            if (ruleClass == null) {
                logger.info("rule type of '{}' is undefined.", ruleID);
                return null;
            }
            //
            ruleObject = (AbstractRule) ruleClass.newInstance();
            ruleObject.setRouteID(ruleID);
            ruleObject.setRouteBody(ruleSettings.getXmlNode("flowControl").getXmlText());
            ruleObject.enable(ruleEnable);
            ruleObject.parseControl(ruleSettings);
        } catch (Exception e) {
            logger.error("rule raw format error -> {}", e.getMessage(), e);
        }
        return ruleObject;
    }

    @Override
    public String toString() {
        return "RuleParser Types:" + ruleTypeMap.keySet();
    }
}
