/*******************************************************************************
 * Copyright 2017 Bstek
 * 
 * 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 com.itheima.sfbx.framework.rule.model.rete.builder;

import com.itheima.sfbx.framework.rule.RuleException;
import com.itheima.sfbx.framework.rule.model.library.ResourceLibrary;
import com.itheima.sfbx.framework.rule.model.library.variable.VariableCategory;
import com.itheima.sfbx.framework.rule.model.rete.ObjectTypeNode;
import com.itheima.sfbx.framework.rule.model.rule.*;
import com.itheima.sfbx.framework.rule.model.rule.lhs.*;

import java.util.List;

/**
 * @author Jacky.gao
 * @since 2015年1月6日
 */
public class BuildContextImpl implements BuildContext {
	private ResourceLibrary resourceLibrary;
	private List<ObjectTypeNode> objectTypeNodes;
	private int id=0;
	private Rule currentRule;
	public BuildContextImpl(ResourceLibrary resourceLibrary,List<ObjectTypeNode> objectTypeNodes) {
		this.resourceLibrary = resourceLibrary;
		this.objectTypeNodes = objectTypeNodes;
	}
	
	public boolean assertSameType(BaseCriteria left, BaseCriteria right) {
		VariableCategory leftCategory = fetchVariableCategory(left);
		VariableCategory rightCategory = fetchVariableCategory(right);
		if(leftCategory==null || rightCategory==null){
			return false;
		}
		return leftCategory.getClazz().equals(rightCategory.getClazz());
	}

	private VariableCategory fetchVariableCategory(BaseCriteria criteria) {
		VariableCategory vc=null;
		if(criteria instanceof Criteria){
			Criteria c=(Criteria)criteria;
			LeftPart leftLeftPart=c.getLeft().getLeftPart();
			if(leftLeftPart instanceof VariableLeftPart){
				VariableLeftPart part=(VariableLeftPart)leftLeftPart;
				vc=resourceLibrary.getVariableCategory(part.getVariableCategory());
			}
		}else if(criteria instanceof NamedCriteria){
			NamedCriteria c=(NamedCriteria)criteria;
			vc=resourceLibrary.getVariableCategory(c.getVariableCategory());
		}else{
			throw new RuleException("Unknow Criteria : "+criteria);
		}
		return vc;
	}
	
	public ObjectTypeNode getObjectTypeNode(BaseCriteria criteria) {
		String objectType=getObjectType(criteria);
		return buildObjectTypeNode(objectType);
	}
	
	@Override
	public String getObjectType(BaseCriteria criteria) {
		String className=null;
		if(criteria instanceof Criteria){
			Criteria c=(Criteria)criteria;
			LeftPart leftPart=c.getLeft().getLeftPart();
			if(leftPart instanceof VariableLeftPart){
				VariableLeftPart varPart=(VariableLeftPart)leftPart;
				String variableCategory=varPart.getVariableCategory();
				VariableCategory category=resourceLibrary.getVariableCategory(variableCategory);
				className=category.getClazz();
			}else if(leftPart instanceof AbstractLeftPart){
				AbstractLeftPart lp=(AbstractLeftPart)leftPart;
				VariableCategory category=resourceLibrary.getVariableCategory(lp.getVariableCategory());
				className=category.getClazz();
			}else if(leftPart instanceof CommonFunctionLeftPart){
				CommonFunctionLeftPart funPart=(CommonFunctionLeftPart)leftPart;
				CommonFunctionParameter parameter=funPart.getParameter();
				Value value=parameter.getObjectParameter();
				className = buildValueClass(value);
			}else if(leftPart instanceof MethodLeftPart){
				MethodLeftPart methodLeftPart=(MethodLeftPart)leftPart;
				List<Parameter> params=methodLeftPart.getParameters();
				if(params!=null && params.size()>0){
					Parameter param=params.get(0);
					Value value=param.getValue();
					className = buildValueClass(value);
				}else{
					className=ObjectTypeNode.NON_CLASS;
				}
			}else if(leftPart instanceof FunctionLeftPart){
				FunctionLeftPart funLeftPart=(FunctionLeftPart)leftPart;
				List<Parameter> params=funLeftPart.getParameters();
				if(params!=null && params.size()>0){
					Parameter param=params.get(0);
					Value value=param.getValue();
					className = buildValueClass(value);
				}else{
					className=ObjectTypeNode.NON_CLASS;
				}
			}else{
				className=ObjectTypeNode.NON_CLASS;
			}
		}else if(criteria instanceof NamedCriteria){
			NamedCriteria c=(NamedCriteria)criteria;
			VariableCategory category=resourceLibrary.getVariableCategory(c.getVariableCategory());
			className=category.getClazz();
		}else{
			throw new RuleException("Unknow Criteria : "+criteria);
		}
		return className;
	}

	private String buildValueClass(Value value) {
		String className;
		if(value instanceof VariableValue){
			VariableValue varValue=(VariableValue)value;
			String variableCategory=varValue.getVariableCategory();
			VariableCategory category=resourceLibrary.getVariableCategory(variableCategory);
			className=category.getClazz();
		}else if(value instanceof ParameterValue){
			VariableCategory category=resourceLibrary.getVariableCategory(VariableCategory.PARAM_CATEGORY);
			className=category.getClazz();
		}else if(value instanceof VariableCategoryValue){
			VariableCategoryValue categoryValue=(VariableCategoryValue)value;
			VariableCategory category=resourceLibrary.getVariableCategory(categoryValue.getVariableCategory());
			className=category.getClazz();
		}else{
			className=ObjectTypeNode.NON_CLASS;
		}
		return className;
	}
	
	@Override
	public ObjectTypeNode buildObjectTypeNode(String className){
		ObjectTypeNode targetObjectTypeNode=null;
		for(ObjectTypeNode typeNode:objectTypeNodes){
			if(typeNode.support(className)){
				targetObjectTypeNode=typeNode;
				break;
			}
		}
		if(targetObjectTypeNode==null){
			targetObjectTypeNode=new ObjectTypeNode(className,nextId());
			objectTypeNodes.add(targetObjectTypeNode);
		}
		return targetObjectTypeNode;
	}

	@Override
	public ResourceLibrary getResourceLibrary() {
		return resourceLibrary;
	}
	
	@Override
	public int nextId() {
		return id++;
	}

	@Override
	public void setCurrentRule(Rule rule) {
		this.currentRule=rule;
	}
	@Override
	public boolean currentRuleIsDebug() {
		if(currentRule==null){
			return false;
		}
		if(currentRule.getDebug()!=null && currentRule.getDebug()){
			return true;
		}
		return false;
	}
}
