package dyvil.tools.compiler.ast.access;

import dyvil.reflect.Modifiers;
import dyvil.tools.compiler.ast.classes.IClass;
import dyvil.tools.compiler.ast.context.IContext;
import dyvil.tools.compiler.ast.expression.IValue;
import dyvil.tools.compiler.ast.field.IDataMember;
import dyvil.tools.compiler.ast.generic.ITypeContext;
import dyvil.tools.compiler.ast.method.IMethod;
import dyvil.tools.compiler.ast.parameter.EmptyArguments;
import dyvil.tools.compiler.ast.structure.IClassCompilableList;
import dyvil.tools.compiler.ast.type.IType;
import dyvil.tools.compiler.ast.type.IType.TypePosition;
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.ast.IASTNode;
import dyvil.tools.parsing.marker.MarkerList;
import dyvil.tools.parsing.position.ICodePosition;

public final class ClassAccess implements IValue
{
	protected ICodePosition position;
	protected IType         type;
	
	public ClassAccess(IType type)
	{
		this.type = type;
	}
	
	public ClassAccess(ICodePosition position, IType type)
	{
		this.position = position;
		this.type = type;
	}
	
	@Override
	public ICodePosition getPosition()
	{
		return this.position;
	}
	
	@Override
	public void setPosition(ICodePosition position)
	{
		this.position = position;
	}
	
	@Override
	public boolean isConstant()
	{
		return true;
	}
	
	@Override
	public int valueTag()
	{
		return CLASS_ACCESS;
	}
	
	@Override
	public boolean isPrimitive()
	{
		return false;
	}
	
	@Override
	public boolean isResolved()
	{
		return this.type.isResolved();
	}
	
	@Override
	public IType getType()
	{
		return this.type;
	}
	
	@Override
	public IValue withType(IType type, ITypeContext typeContext, MarkerList markers, IContext context)
	{
		if (type.isSuperTypeOf(this.type))
		{
			return this;
		}
		return null;
	}
	
	@Override
	public boolean isType(IType type)
	{
		return type.isSuperTypeOf(this.type);
	}
	
	@Override
	public void resolveTypes(MarkerList markers, IContext context)
	{
		this.type = this.type.resolveType(null, context);
	}
	
	@Override
	public IValue resolve(MarkerList markers, IContext context)
	{
		this.type.resolve(markers, context);

		final Name name = this.type.getName();
		final IDataMember field = context.resolveField(name);
		if (field != null)
		{
			final FieldAccess fieldAccess = new FieldAccess(this.position);
			fieldAccess.name = name;
			fieldAccess.field = field;
			return fieldAccess;
		}
		
		final IMethod method = IContext.resolveMethod(context, null, name, EmptyArguments.INSTANCE);
		if (method != null)
		{
			final MethodCall methodCall = new MethodCall(this.position);
			methodCall.name = name;
			methodCall.method = method;
			methodCall.dotless = true;
			methodCall.arguments = EmptyArguments.INSTANCE;
			return methodCall;
		}
		
		if (!this.type.isResolved())
		{
			markers.add(Markers.semantic(this.position, this.type.isArrayType() ? "resolve.type" : "resolve.any",
			                             this.type.toString()));
		}
		
		return this;
	}
	
	@Override
	public void checkTypes(MarkerList markers, IContext context)
	{
		this.type.checkType(markers, context, TypePosition.TYPE);
	}
	
	@Override
	public void check(MarkerList markers, IContext context)
	{
		this.type.check(markers, context);

		IClass iclass = this.type.getTheClass();
		if (iclass == null)
		{
			// Already reported this in RESOLVE ^
			return;
		}
		
		if (iclass.hasModifier(Modifiers.OBJECT_CLASS))
		{
			// Object type, we can safely use it's instance field.
			return;
		}
		
		markers.add(Markers.semantic(this.position, "type.access.invalid", this.type.toString()));
	}
	
	@Override
	public IValue foldConstants()
	{
		return this;
	}
	
	@Override
	public IValue cleanup(IContext context, IClassCompilableList compilableList)
	{
		return this;
	}
	
	@Override
	public void writeExpression(MethodWriter writer, IType type) throws BytecodeException
	{
		IClass iclass = this.type.getTheClass();
		if (iclass != null)
		{
			IDataMember field = iclass.getMetadata().getInstanceField();
			if (field != null)
			{
				field.writeGet(writer, null, this.getLineNumber());
				return;
			}
		}

		throw new BytecodeException("Object access was not compiled correctly");
	}

	@Override
	public String toString()
	{
		return IASTNode.toString(this);
	}

	@Override
	public void toString(String prefix, StringBuilder buffer)
	{
		this.type.toString(prefix, buffer);
	}
}

@@ -2889,24 +2889,181 @@
 all;%0A%09%09%7D%0A%09%09%0A
+%09%09return this;%0A%09%7D%0A%09%0A%09@Override%0A%09public void checkTypes(MarkerList markers, IContext context)%0A%09%7B%0A%09%09this.type.checkType(markers, context, TypePosition.TYPE);%0A%0A
 %09%09if (!this.
@@ -3250,34 +3250,16 @@
 ));%0A%09%09%7D%0A
-%09%09%0A%09%09return this;%0A
 %09%7D%0A%09%0A%09@O
@@ -3276,37 +3276,32 @@
 ublic void check
-Types
 (MarkerList mark
@@ -3335,36 +3335,32 @@
 %09this.type.check
-Type
 (markers, contex
@@ -3364,146 +3364,114 @@
 text
-, TypePosition.TYPE);%0A%09%7D%0A%09%0A%09@Override%0A%09public void check(MarkerList markers, IContext context
+);%0A%0A%09%09if (!this.type.isResolved()
 )%0A
+%09
 %09%7B%0A%09%09
-this.type.check(markers, context);%0A%0A%09%09
+%09// Already reported this in CHECK_TYPES%0A%09%09%09return;%0A%09%09%7D%0A%0A%09%09final 
 ICla
@@ -3524,86 +3524,19 @@
 ass 
-=
+!
 = null
-)%0A%09%09%7B%0A%09%09%09// Already reported this in RESOLVE %5E%0A%09%09%09return;%0A%09%09%7D%0A%09%09%0A%09%09if (
+ && 
 icla

Fix ClassAccess compiler errors