package dyvil.tools.compiler.ast.pattern;

import dyvil.reflect.Opcodes;
import dyvil.tools.asm.Label;
import dyvil.tools.compiler.ast.context.IContext;
import dyvil.tools.compiler.ast.field.IDataMember;
import dyvil.tools.compiler.ast.type.IType;
import dyvil.tools.compiler.ast.type.builtin.Types;
import dyvil.tools.compiler.backend.MethodWriter;
import dyvil.tools.compiler.backend.exception.BytecodeException;
import dyvil.tools.compiler.util.Markers;
import dyvil.tools.parsing.Name;
import dyvil.tools.parsing.marker.MarkerList;
import dyvil.tools.parsing.position.ICodePosition;

public class TypeCheckPattern implements IPattern
{
	private IPattern pattern;
	private IType    type;

	// Metadata
	private IType         fromType;
	private ICodePosition position;

	public TypeCheckPattern(ICodePosition position, IPattern pattern)
	{
		this.position = position;
		this.pattern = pattern;
	}

	public TypeCheckPattern(IPattern pattern, IType fromType, IType toType)
	{
		this.pattern = pattern;
		this.fromType = fromType;
		this.type = toType;
	}

	@Override
	public ICodePosition getPosition()
	{
		return this.position;
	}

	@Override
	public void setPosition(ICodePosition position)
	{
		this.position = position;
	}

	@Override
	public int getPatternType()
	{
		return TYPECHECK;
	}

	@Override
	public void setType(IType type)
	{
		this.type = type;
	}

	@Override
	public IType getType()
	{
		return this.type;
	}

	@Override
	public IPattern withType(IType type, MarkerList markers)
	{
		if (Types.isSuperType(type, this.type))
		{
			this.fromType = type;
			return this;
		}
		return null;
	}

	@Override
	public boolean isType(IType type)
	{
		return !type.isPrimitive();
	}

	@Override
	public IDataMember resolveField(Name name)
	{
		return this.pattern == null ? null : this.pattern.resolveField(name);
	}

	@Override
	public IPattern resolve(MarkerList markers, IContext context)
	{
		if (this.pattern != null)
		{
			this.pattern = this.pattern.resolve(markers, context);
		}

		if (this.type != null)
		{
			this.type = this.type.resolveType(markers, context);

			if (this.pattern != null && this.pattern.getPatternType() != WILDCARD)
			{
				this.pattern = this.pattern.withType(this.type, markers);
			}
		}
		else
		{
			markers.add(Markers.semantic(this.position, "pattern.typecheck.invalid"));
		}

		return this;
	}

	@Override
	public void writeInvJump(MethodWriter writer, int varIndex, IType matchedType, Label elseLabel)
		throws BytecodeException
	{
		varIndex = IPattern.ensureVar(writer, varIndex, matchedType);

		if (!this.fromType.isPrimitive())
		{
			writer.visitVarInsn(Opcodes.ALOAD, varIndex);
			writer.visitTypeInsn(Opcodes.INSTANCEOF, this.type.getInternalName());
			writer.visitJumpInsn(Opcodes.IFEQ, elseLabel);
		}

		if (this.pattern.getPatternType() != WILDCARD)
		{
			writer.visitVarInsn(Opcodes.ALOAD, varIndex);
			this.fromType.writeCast(writer, this.type, this.getLineNumber());
			this.pattern.writeInvJump(writer, -1, this.type, elseLabel);
		}
	}

	@Override
	public void toString(String prefix, StringBuilder buffer)
	{
		if (this.pattern != null)
		{
			this.pattern.toString(prefix, buffer);
			int patternType = this.pattern.getPatternType();

			if (patternType == BINDING || patternType != CASE_CLASS && !this.pattern.isType(this.type))
			{
				buffer.append(" as ");
				this.type.toString(prefix, buffer);
			}
		}
	}
}

@@ -2486,19 +2486,121 @@
 %7B%0A%09%09
-varIndex = 
+if (this.fromType.isPrimitive())%0A%09%09%7B%0A%09%09%09if (this.pattern.getPatternType() == WILDCARD)%0A%09%09%09%7B%0A%09%09%09%09return;%0A%09%09%09%7D%0A%0A%09%09%09
 IPat
@@ -2608,14 +2608,12 @@
 ern.
-ensure
+load
 Var(
@@ -2648,48 +2648,88 @@
 e);%0A
-%0A%09%09if (!this.fromType.isPrimitive())%0A%09%09%7B
+%09%09%7D%0A%09%09else%0A%09%09%7B%0A%09%09%09varIndex = IPattern.ensureVar(writer, varIndex, matchedType);%0A
 %0A%09%09%09
@@ -2898,21 +2898,18 @@
 Label);%0A
-%09%09%7D%0A
 %0A
+%09
 %09%09if (th
@@ -2928,33 +2928,33 @@
 etPatternType() 
-!
+=
 = WILDCARD)%0A%09%09%7B%0A
@@ -2951,18 +2951,37 @@
 CARD)%0A%09%09
+%09
 %7B%0A
+%09%09%09%09return;%0A%09%09%09%7D%0A%0A
 %09%09%09write
@@ -3018,24 +3018,28 @@
 varIndex);%0A%09
+%09%7D%0A%0A
 %09%09this.fromT
@@ -3096,17 +3096,16 @@
 r());%0A%09%09
-%09
 this.pat
@@ -3157,20 +3157,16 @@
 Label);%0A
-%09%09%7D%0A
 %09%7D%0A%0A%09@Ov

Fix TypeCheckPattern for Primitives