package to.etc.fixdom;

import com.github.javaparser.JavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Modifier;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.PackageDeclaration;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.TypeDeclaration;
import com.github.javaparser.ast.expr.CastExpr;
import com.github.javaparser.ast.expr.SimpleName;
import com.github.javaparser.ast.expr.ThisExpr;
import com.github.javaparser.ast.stmt.ReturnStmt;
import com.github.javaparser.ast.stmt.Statement;
import com.github.javaparser.ast.type.ClassOrInterfaceType;
import com.github.javaparser.ast.type.Type;
import com.github.javaparser.ast.type.TypeParameter;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;

import java.io.File;
import java.util.EnumSet;
import java.util.Optional;

/**
 * @author <a href="mailto:jal@etc.to">Frits Jalvingh</a>
 * Created on 7-2-19.
 */
public class FixDom {
	static public void main(String[] args) throws Exception {
		new FixDom().run();


	}

	/**
	 *
	 */
	private void run() throws Exception {
		File root = new File("/home/jal/git/domui/re/to.etc.domui/src/main/java/to/etc/domui/dom");


		File file = new File(root, "css/CssBase.java");
		fixBaseFile(file);





	}

	private void fixBaseFile(File file) throws Exception {
		if(! file.exists())
			throw new IllegalStateException(file + ": does not exist");

		CompilationUnit parse = JavaParser.parse(file);

		Optional<PackageDeclaration> pd = parse.getPackageDeclaration();
		if(! pd.isPresent()) {
			error(file, "No package declaration");
			return;
		}
		PackageDeclaration packageDeclaration = pd.get();
		String id = packageDeclaration.getName().asString();

		//-- Find the root class
		for(TypeDeclaration<?> type : parse.getTypes()) {
			System.out.println("type: " + type.getNameAsString());

			//-- We need to rename the type to AbstractCssBase and make it abstract
			type.setName(new SimpleName("Abstract") + type.getNameAsString());

			//-- It needs to become generic <N extends itself>
			EnumSet<Modifier> modifiers = type.getModifiers();
			modifiers.add(Modifier.ABSTRACT);
			type.setModifiers(modifiers);

			ClassOrInterfaceDeclaration mdi = (ClassOrInterfaceDeclaration) type;
			NodeList<TypeParameter> typeParameters = mdi.getTypeParameters();

			NodeList<ClassOrInterfaceType> nl = new NodeList<>();
			nl.add(new ClassOrInterfaceType(type.getNameAsString()));
			TypeParameter tp = new TypeParameter("N", nl);
			typeParameters.add(tp);
			mdi.setTypeParameters(typeParameters);

			//-- Now: fix all methods
			for(MethodDeclaration method : type.getMethods()) {
				fixMethod(method);
			}

			System.out.println(parse.toString());
		}




	}

	private void fixMethod(MethodDeclaration method) {
		Type type = method.getType();
		if(! type.asString().equals("void"))
			return;

		if(! method.getName().asString().startsWith("set")) {
			return;
		}
		TypeParameter typeN = new TypeParameter("N");
		method.setType(typeN);

		//-- All places where we have a return we need to rewrite


		NodeList<Statement> statements = method.getBody().get().getStatements();
		if(statements.size() != 0) {
			Statement last = statements.get(statements.size() - 1);
			if(last.toString().equals("return")) {
				statements.remove(statements.size()-1);
			}
		}

		//-- Fix any intermediary returns
		CastExpr cx = new CastExpr(typeN, new ThisExpr());
		method.getBody().get().accept(new VoidVisitorAdapter<Void>() {
			@Override public void visit(ReturnStmt n, Void arg) {
				n.setExpression(cx);
				super.visit(n, arg);
			}
		}, null);

		//-- Add the appropriate statement
		ReturnStmt rs = new ReturnStmt(cx);
		statements.add(rs);
	}

	private void error(File file, String err) {
		System.err.println(file + " error: " + err);
	}


}

@@ -911,16 +911,43 @@
 o.File;%0A
+import java.io.FileWriter;%0A
 import j
@@ -1409,21 +1409,16 @@
 (file);%0A
-%0A%0A%0A%0A%0A
 %09%7D%0A%0A%09pri
@@ -2533,15 +2533,40 @@
 %0A%09%09%09
-nl.add(
+ClassOrInterfaceType baseType = 
 new 
@@ -2609,16 +2609,109 @@
 tring())
+;%0A%09%09%09baseType.setTypeArguments(NodeList.nodeList(new TypeParameter(%22N%22)));%0A%09%09%09nl.add(baseType
 );%0A%09%09%09Ty
@@ -2983,15 +2983,171 @@
 ));%0A
+%0A
 %09%09
-%7D%0A%0A%0A%0A
+%09File f = new File(file.getParentFile(), type.getNameAsString() + %22.java%22);%0A%09%09%09try(FileWriter fw = new FileWriter(f)) %7B%0A%09%09%09%09fw.write(parse.toString());%0A%09%09%09%7D%0A%09%09%7D
 %0A%09%7D%0A

Fix extends clause.