package railo.transformer.bytecode.statement;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.objectweb.asm.Label;
import org.objectweb.asm.commons.GeneratorAdapter;

import railo.transformer.bytecode.Body;
import railo.transformer.bytecode.BytecodeContext;
import railo.transformer.bytecode.BytecodeException;
import railo.transformer.bytecode.Position;
import railo.transformer.bytecode.Statement;
import railo.transformer.bytecode.util.ExpressionUtil;

public final class NativeSwitch extends StatementBaseNoFinal implements FlowControlBreak,FlowControlContinue,HasBodies {

	public static final short LOCAL_REF=0;
	public static final short ARG_REF=1;
	public static final short PRIMITIVE=1;
	
	
	private int value;
	private Label end;
	private Statement defaultCase;
	List<Case> cases=new ArrayList<Case>();
	private Label[] labels=new Label[0];
	private int[] values=new int[0];
	private short type;
	
	public NativeSwitch(int value, short type, Position start, Position end) {
		super(start, end);
		this.value=value;
		this.type=type;
	}

	public void _writeOut(BytecodeContext bc) throws BytecodeException {
		end = new Label();
		GeneratorAdapter adapter = bc.getAdapter();

		if(type==LOCAL_REF) adapter.loadLocal(value);
		else if(type==ARG_REF) adapter.loadArg(value);
		else adapter.push(value);
		
		Label beforeDefault = new Label();
		adapter.visitLookupSwitchInsn(beforeDefault, values, labels);
		
		Iterator<Case> it = cases.iterator();
		Case c;
		while(it.hasNext()) {
			c= it.next();
			adapter.visitLabel(c.label);
			ExpressionUtil.visitLine(bc, c.startPos);
			c.body.writeOut(bc);
			ExpressionUtil.visitLine(bc, c.endPos);
			if(c.doBreak){
				adapter.goTo(end);
			}
		}
		
		
		adapter.visitLabel(beforeDefault);
		if(defaultCase!=null)defaultCase.writeOut(bc);
		adapter.visitLabel(end);

	}
	
	public void addCase(int value, Statement body,Position start,Position end,boolean doBreak) {
		
		Case nc = new Case(value,body,start,end,doBreak);

		Label[] labelsTmp = new Label[cases.size()+1];
		int[] valuesTmp = new int[cases.size()+1];
		
		int count=0;
		boolean hasAdd=false;
		for(int i=0;i<labels.length;i++) {
			if(!hasAdd && nc.value<values[i]) {
				labelsTmp[count]=nc.label;
				valuesTmp[count]=nc.value;
				count++;
				hasAdd=true;
			}
			labelsTmp[count]=labels[i];
			valuesTmp[count]=values[i];
			count++;
		}
		if(!hasAdd) {
			labelsTmp[labels.length]=nc.label;
			valuesTmp[values.length]=nc.value;
		}
		labels=labelsTmp;
		values=valuesTmp;
		
		
		cases.add(nc);
	}
	
	public void addDefaultCase(Statement defaultStatement) {
		this.defaultCase=defaultStatement;
	}
	
	class Case {

		public boolean doBreak;
		private int value;
		private Statement body;
		private Label label=new Label();
		private Position startPos;
		private Position endPos;

		public Case(int value, Statement body,Position startline,Position endline, boolean doBreak) {
			this.value=value;
			this.body=body;
			this.startPos=startline;
			this.endPos=endline;
			this.doBreak=doBreak;
		}

	}

	/**
	 *
	 * @see railo.transformer.bytecode.statement.FlowControl#getBreakLabel()
	 */
	public Label getBreakLabel() {
		return end;
	}

	/**
	 *
	 * @see railo.transformer.bytecode.statement.FlowControl#getContinueLabel()
	 */
	public Label getContinueLabel() {
		return end;
	}

	/**
	 * @see railo.transformer.bytecode.statement.HasBodies#getBodies()
	 */
	public Body[] getBodies() {
		if(cases==null) {
			if(defaultCase!=null) return new Body[]{(Body) defaultCase};
			return new Body[]{};
		}
		
		int len=cases.size(),count=0;
		if(defaultCase!=null)len++;
		Body[] bodies=new Body[len];
		Case c;
		Iterator<Case> it = cases.iterator();
		while(it.hasNext()) {
			c=it.next();
			bodies[count++]=(Body) c.body;
		}
		if(defaultCase!=null)bodies[count++]=(Body) defaultCase;
		
		return bodies;
	}

	@Override
	public String getLabel() {
		return null;
	}
}
