
/**
 * Copyright (c) 2011-2019, James Zhan 詹波 (jfinal@126.com).
 *
 * 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 org.tio.jfinal.template.stat.ast;

import org.tio.jfinal.template.Env;
import org.tio.jfinal.template.expr.ast.Expr;
import org.tio.jfinal.template.expr.ast.ForCtrl;
import org.tio.jfinal.template.expr.ast.Logic;
import org.tio.jfinal.template.io.Writer;
import org.tio.jfinal.template.stat.Ctrl;
import org.tio.jfinal.template.stat.Scope;

import java.util.Iterator;

/**
 * For 循环控制，支持 List、Map、数组、Collection、Iterator、Iterable
 * Enumeration、null 以及任意单个对象的迭代，简单说是支持所有对象迭代
 * 
 * 主要用法：
 * 1：#for(item : list) #(item) #end
 * 2：#for(item : list) #(item) #else content #end
 * 3：#for(i=0; i<9; i++) #(item) #end
 * 4：#for(i=0; i<9; i++) #(item) #else content #end
 */
public class For extends Stat {
	
	private ForCtrl forCtrl;
	private Stat stat;
	private Stat _else;
	
	public For(ForCtrl forCtrl, StatList statList, Stat _else) {
		this.forCtrl = forCtrl;
		this.stat = statList.getActualStat();
		this._else = _else;
	}
	
	public void exec(Env env, Scope scope, Writer writer) {
		scope = new Scope(scope);
		if (forCtrl.isIterator()) {
			forIterator(env, scope, writer);
		} else {
			forLoop(env, scope, writer);
		}
	}
	
	/**
	 * #for( id : expr)
	 */
	private void forIterator(Env env, Scope scope, Writer writer) {
		Ctrl ctrl = scope.getCtrl();
		Object outer = scope.get("for");
		ctrl.setLocalAssignment();
		ForIteratorStatus forIteratorStatus = new ForIteratorStatus(outer, forCtrl.getExpr().eval(scope), location);
		ctrl.setWisdomAssignment();
		scope.setLocal("for", forIteratorStatus);
		
		Iterator<?> it = forIteratorStatus.getIterator();
		String itemName = forCtrl.getId();
		while(it.hasNext()) {
			scope.setLocal(itemName, it.next());
			stat.exec(env, scope, writer);
			forIteratorStatus.nextState();
			
			if (ctrl.isJump()) {
				if (ctrl.isBreak()) {
					ctrl.setJumpNone();
					break ;
				} else if (ctrl.isContinue()) {
					ctrl.setJumpNone();
					continue ;
				} else {
					return ;
				}
			}
		}
		
		if (_else != null && forIteratorStatus.getIndex() == 0) {
			_else.exec(env, scope, writer);
		}
	}
	
	/**
	 * #for(exprList; cond; update)
	 */
	private void forLoop(Env env, Scope scope, Writer writer) {
		Ctrl ctrl = scope.getCtrl();
		Object outer = scope.get("for");
		ForLoopStatus forLoopStatus = new ForLoopStatus(outer);
		scope.setLocal("for", forLoopStatus);
		
		Expr init = forCtrl.getInit();
		Expr cond = forCtrl.getCond();
		Expr update = forCtrl.getUpdate();
		
		ctrl.setLocalAssignment();
		for (init.eval(scope); cond == null || Logic.isTrue(cond.eval(scope)); update.eval(scope)) {
			ctrl.setWisdomAssignment();
			stat.exec(env, scope, writer);
			ctrl.setLocalAssignment();
			forLoopStatus.nextState();
			
			if (ctrl.isJump()) {
				if (ctrl.isBreak()) {
					ctrl.setJumpNone();
					break ;
				} else if (ctrl.isContinue()) {
					ctrl.setJumpNone();
					continue ;
				} else {
					ctrl.setWisdomAssignment();
					return ;
				}
			}
		}
		
		ctrl.setWisdomAssignment();
		if (_else != null && forLoopStatus.getIndex() == 0) {
			_else.exec(env, scope, writer);
		}
	}
}






