package dyvilx.tools.compiler.ast.method;

import dyvil.collection.iterator.ArrayIterator;
import dyvilx.tools.compiler.ast.context.IImplicitContext;
import dyvilx.tools.compiler.ast.expression.IValue;
import dyvilx.tools.compiler.ast.type.IType;

import java.util.Arrays;
import java.util.Iterator;

public class MatchList<T extends IOverloadable> implements IImplicitContext, Iterable<Candidate<T>>
{
	private static final byte SORTED    = 1;
	private static final byte SKIP_SORT = 2;

	private Candidate<T>[] candidates = (Candidate<T>[]) new Candidate[4];
	private int size;

	private byte sorted = SORTED;

	private final IImplicitContext implicitContext;

	public MatchList(IImplicitContext implicitContext)
	{
		this.implicitContext = implicitContext;
	}

	public MatchList(IImplicitContext implicitContext, boolean skipSort)
	{
		this.implicitContext = implicitContext;

		if (skipSort)
		{
			this.sorted |= SKIP_SORT;
		}
	}

	public boolean isSkipSort()
	{
		return (this.sorted & SKIP_SORT) != 0;
	}

	public int size()
	{
		return this.size;
	}

	public boolean isEmpty()
	{
		return this.size <= 0;
	}

	public boolean hasCandidate()
	{
		return !this.isSkipSort() && !this.isEmpty() && !this.isAmbigous() && !this.getBestCandidate().invalid;
	}

	public void ensureCapacity(int capacity)
	{
		if (capacity <= this.candidates.length)
		{
			return;
		}

		final int newCapacity = capacity << 1;

		final Candidate<T>[] tempMethods = (Candidate<T>[]) new Candidate[newCapacity];
		System.arraycopy(this.candidates, 0, tempMethods, 0, this.size);
		this.candidates = tempMethods;
	}

	public void add(Candidate<T> candidate)
	{
		this.sorted &= ~SORTED;

		this.ensureCapacity(this.size + 1);
		this.candidates[this.size++] = candidate;
	}

	public void addAll(MatchList<T> list)
	{
		final int otherSize = list.size;
		if (otherSize <= 0)
		{
			return;
		}

		this.sorted &= ~SORTED;
		this.ensureCapacity(this.size + otherSize);
		System.arraycopy(list.candidates, 0, this.candidates, this.size, otherSize);
		this.size += otherSize;
	}

	public Candidate<T> getCandidate(int index)
	{
		return this.candidates[index];
	}

	public boolean isAmbigous()
	{
		if (this.size <= 1 || (this.sorted & SKIP_SORT) != 0)
		{
			return false;
		}

		this.sort();
		return this.candidates[0].compareTo(this.candidates[1]) == 0;
	}

	public Candidate<T> getBestCandidate()
	{
		switch (this.size)
		{
		case 0:
			return null;
		case 1:
			return this.candidates[0];
		case 2:
			final Candidate<T> c1 = this.candidates[0];
			final Candidate<T> c2 = this.candidates[1];
			final int compareTo = c1.compareTo(c2);
			if (compareTo < 0)
			{
				return c1;
			}
			if (compareTo > 0)
			{
				return c2;
			}
			return null;
		}

		// this.sort(); // calling isAmbiguous() sorts this list
		if (this.isAmbigous())
		{
			return null;
		}
		return this.candidates[0];
	}

	public T getBestMember()
	{
		final Candidate<T> bestCandidate = this.getBestCandidate();
		return bestCandidate == null ? null : bestCandidate.member;
	}

	private void sort()
	{
		if (this.sorted != 0 || this.size <= 1)
		{
			return;
		}

		Arrays.sort(this.candidates, 0, this.size);
		this.sorted |= SORTED;
	}

	@Override
	public void getImplicitMatches(MatchList<IMethod> list, IValue value, IType targetType)
	{
		this.implicitContext.getImplicitMatches(list, value, targetType);
	}

	@Override
	public Iterator<Candidate<T>> iterator()
	{
		this.sort();
		return new ArrayIterator<>(this.candidates, 0, this.size);
	}

	public MatchList<T> emptyCopy()
	{
		return new MatchList<>(this.implicitContext, this.isSkipSort());
	}
}

@@ -2264,35 +2264,202 @@
 ();%0A
+%0A
 %09%09
-return this.candidates%5B0%5D
+final Candidate%3CT%3E first = this.candidates%5B0%5D;%0A%09%09for (int i = 1; i %3C this.size; i++)%0A%09%09%7B%0A%09%09%09final Candidate%3CT%3E candidate = this.candidates%5Bi%5D;%0A%09%09%09if (candidate.member != first.member && first
 .com
@@ -2465,21 +2465,16 @@
 mpareTo(
-this.
 candidat
@@ -2478,18 +2478,230 @@
 date
-s%5B1%5D
 ) == 0
+)%0A%09%09%09%7B%0A%09%09%09%09// if two candidates have the same rank but different members, its ambiguous%0A%09%09%09%09// sometimes the same member is added twice, but we don't count that as ambiguous.%0A%09%09%09%09return true;%0A%09%09%09%7D%0A%09%09%7D%0A%0A%09%09return false
 ;%0A%09%7D

Improve Match List Ambiguity Check