package jadx.core.utils;

import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.IAttributeNode;
import jadx.core.dex.attributes.nodes.JadxErrorAttr;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.utils.exceptions.JadxOverflowException;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ErrorsCounter {
	private static final Logger LOG = LoggerFactory.getLogger(ErrorsCounter.class);

	private final Set<Object> errorNodes = new HashSet<>();
	private int errorsCount;

	public int getErrorCount() {
		return errorsCount;
	}

	private void addError(IAttributeNode node, String msg, Throwable e) {
		errorNodes.add(node);
		errorsCount++;

		if (e != null) {
			if (e.getClass() == JadxOverflowException.class) {
				// don't print full stack trace
				e = new JadxOverflowException(e.getMessage());
				LOG.error("{}, message: {}", msg, e.getMessage());
			} else {
				LOG.error(msg, e);
			}
			node.addAttr(new JadxErrorAttr(e));
		} else {
			node.add(AFlag.INCONSISTENT_CODE);
			LOG.error(msg);
		}
	}

	public static String classError(ClassNode cls, String errorMsg, Throwable e) {
		String msg = formatErrorMsg(cls, errorMsg);
		cls.dex().root().getErrorsCounter().addError(cls, msg, e);
		return msg;
	}

	public static String classError(ClassNode cls, String errorMsg) {
		return classError(cls, errorMsg, null);
	}

	public static String methodError(MethodNode mth, String errorMsg, Throwable e) {
		String msg = formatErrorMsg(mth, errorMsg);
		mth.dex().root().getErrorsCounter().addError(mth, msg, e);
		return msg;
	}

	public static String methodError(MethodNode mth, String errorMsg) {
		return methodError(mth, errorMsg, null);
	}

	public void printReport() {
		if (getErrorCount() > 0) {
			LOG.error("{} errors occurred in following nodes:", getErrorCount());
			List<Object> nodes = new ArrayList<>(errorNodes);
			Collections.sort(nodes, new Comparator<Object>() {
				@Override
				public int compare(Object o1, Object o2) {
					return String.valueOf(o1).compareTo(String.valueOf(o2));
				}
			});
			for (Object node : nodes) {
				String nodeName = node.getClass().getSimpleName().replace("Node", "");
				LOG.error("  {}: {}", nodeName, node);
			}
		}
	}

	public static String formatErrorMsg(ClassNode cls, String msg) {
		return msg + " in class: " + cls;
	}

	public static String formatErrorMsg(MethodNode mth, String msg) {
		return msg + " in method: " + mth;
	}

	private String formatException(Throwable e) {
		if (e == null || e.getMessage() == null) {
			return "";
		} else {
			return "\n error: " + e.getMessage();
		}
	}

	public String formatErrorMsg(ClassNode cls, String msg, Throwable e) {
		return formatErrorMsg(cls, msg) + formatException(e);
	}

	public String formatErrorMsg(MethodNode mth, String msg, Throwable e) {
		return formatErrorMsg(mth, msg) + formatException(e);
	}
}

@@ -641,22 +641,30 @@
 nal Set%3C
-Object
+IAttributeNode
 %3E errorN
@@ -2037,20 +2037,21 @@
 ist%3C
-Object%3E node
+String%3E error
 s = 
@@ -2080,223 +2080,48 @@
 odes
-);%0A%09%09%09Collections.sort(nodes, new Comparator%3CObject%3E() %7B%0A%09%09%09%09@Override%0A%09%09%09%09public int compare(Object o1, Object o2) %7B%0A%09%09%09%09%09return String.valueOf(o1).compareTo(String.valueOf(o2));%0A%09%09%09%09%7D%0A%09%09%09%7D);%0A%09%09%09for (Object
+.size());%0A%09%09%09for (IAttributeNode
  node : 
 node
@@ -2116,17 +2116,22 @@
  node : 
-n
+errorN
 odes) %7B%0A
@@ -2213,44 +2213,134 @@
 %09%09%09%09
-LOG.
 error
-(%22  %7B%7D: %7B%7D%22, nodeName, node
+s.add(nodeName + %22: %22 + node);%0A%09%09%09%7D%0A%09%09%09Collections.sort(errors);%0A%09%09%09for (String err : errors) %7B%0A%09%09%09%09LOG.error(%22  %7B%7D%22, err
 );%0A%09

fix errors list sorting