package cn.bran.play;

import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Set;

import play.Play;
import play.data.validation.Validation;
import play.templates.JavaExtensions;
import play.vfs.VirtualFile;
import cn.bran.japid.compiler.JapidCompilationException;
import cn.bran.japid.compiler.JapidTemplateTransformer;
import cn.bran.japid.compiler.TranslateTemplateTask;
import cn.bran.japid.util.DirUtil;
import cn.bran.japid.util.JapidFlags;
import cn.bran.play.util.PlayDirUtil;

public class JapidCommands {
	private static final String APP = "app";

	/**
	 * TODO: perhaps I can parse the depended modules and apply the commands to them as well.
	 * 
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
		String arg0 = args[0];

//		String applicationPath = System.getProperty("user.dir");
		String applicationPath = ".";
		if (args.length > 1) {
			applicationPath = args[1];
		}
		Play.applicationPath = new File(applicationPath);
		String appPath = Play.applicationPath.getAbsolutePath() + File.separator;
		if ("gen".equals(arg0)) {
			gen(appPath + APP);
		} else if ("regen".equals(arg0)) {
			regen(appPath + APP);
		} else if ("clean".equals(arg0)) {
			delAllGeneratedJava(appPath + APP + File.separator + DirUtil.JAPIDVIEWS_ROOT);
		} else if ("mkdir".equals(arg0)) {
			PlayDirUtil.mkdir(appPath + APP);
		} else {
			JapidFlags.log("not known: " + arg0);
		}
	}

	public static void regen() throws IOException {
		regen(APP);
	}

	public static void regen(String root) throws IOException {
		// TODO Auto-generated method stub
		String pathname = root + File.separatorChar + DirUtil.JAPIDVIEWS_ROOT;
		delAllGeneratedJava(pathname);
		gen(root);
	}

	public static void delAllGeneratedJava(String pathname) {
		String[] javas = DirUtil.getAllFileNames(new File(pathname), new String[] { "java" });

		for (String j : javas) {
			if (!j.contains(DirUtil.JAVATAGS)) {
				String filePath = pathname + File.separatorChar + j;
				JapidFlags.log("removed: " + filePath);
				boolean delete = new File(filePath).delete();
				if (!delete)
					throw new RuntimeException("file was not deleted: " + j);
			}
		}
		// JapidFlags.log("removed: all none java tag java files in " +
		// JapidPlugin.JAPIDVIEWS_ROOT);
	}

	/**
	 * update the java files from the html files, for the changed only
	 * @throws IOException 
	 */
	public static void gen(String packageRoot) throws IOException {
		// moved to reloadChanged
		List<File> changedFiles = reloadChanged(packageRoot);
		if (changedFiles.size() > 0) {
			for (File f : changedFiles) {
				String relativePath = JapidTemplateTransformer.getRelativePath(f, Play.applicationPath);
				JapidFlags.log("updated: " + relativePath.replace("html", "java"));
			}
		} else {
			JapidFlags.log("No java files need to be updated.");
		}

		rmOrphanJava();
	}

	/**
	 * @param root
	 *            the package root "/"
	 * @return
	 */
	public static List<File> reloadChanged(String root) {
		try {
			DirUtil.mkdir(root);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		
		TranslateTemplateTask t = new TranslateTemplateTask();

		File rootDir = new File(root);
		t.setPackageRoot(rootDir);
		t.setInclude(new File(rootDir, DirUtil.JAPIDVIEWS_ROOT));
		t.clearImports();
		t.importStatic(JapidPlayAdapter.class);
		t.importStatic(Validation.class);
		t.importStatic(JavaExtensions.class);
		t.addAnnotation(NoEnhance.class);
		if (DirUtil.hasLayouts(root))
			t.addImport(DirUtil.JAPIDVIEWS_ROOT + "._layouts.*");
		if (DirUtil.hasJavaTags(root))
			t.addImport(DirUtil.JAPIDVIEWS_ROOT + "._javatags.*");
		if (DirUtil.hasTags(root))
			t.addImport(DirUtil.JAPIDVIEWS_ROOT + "._tags.*");
		t.addImport("models.*");
		t.addImport("controllers.*");
		t.addImport(play.mvc.Scope.class.getName() + ".*");
		t.addImport(play.i18n.Messages.class);
		t.addImport(play.i18n.Lang.class);
		t.addImport(play.mvc.Http.class.getName() + ".*");
		t.addImport(Validation.class.getName());
		t.addImport(play.data.validation.Error.class.getName());
//		t.addImport("static  japidviews._javatags.JapidWebUtil.*");
		List<String> javatags = DirUtil.scanJavaTags(root);
		for (String f : javatags) {
			t.addImport("static " + f + ".*");
		}
		try {
			t.execute();
		} catch (JapidCompilationException e) {
//			 remove the .class file from previous successful compilation if any
//			String templateName = e.getTemplateName();
//			String javaSrc = DirUtil.mapSrcToJava(templateName);
//			// remove the java file
//			String javaSrcPath = APP + File.separator + javaSrc;
//			if (new File(javaSrcPath).delete()){
//				System.out.println("[Japid] deleted: " + javaSrcPath);
//			}
//			String className = javaSrc.substring(0, javaSrc.length() - 5).replace('/', '.').replace('\\', '.');
//			// remove the class file
//			Play.classes.remove(className);
			throw e;
		}
		List<File> changedFiles = t.getChangedFiles();
		return changedFiles;
	}

	/**
	 * get all the java files in a dir with the "java" removed
	 * 
	 * @return
	 */
	public static String[] getAllJavaFilesInDir(File root) {
		// from source files only
		String[] allFiles = DirUtil.getAllFileNames(root, new String[] { ".java" });
		for (int i=0; i< allFiles.length; i++) {
			allFiles[i] = allFiles[i].replace(".java", "");
		}
		return allFiles;
	}

	/**
	 * delete orphaned java artifacts from the japidviews directory of the current app and all the depended modules
	 * 
	 * @return
	 */
	public static boolean rmOrphanJava() {
		boolean hasOrphan = false;
		String appAbs = Play.applicationPath.getAbsolutePath() + File.separator + APP;
		hasOrphan = removeOrphanedJavaFrom(appAbs);
		
		Collection<VirtualFile> modules = Play.modules.values();
		for (VirtualFile module: modules) {
			try {
				VirtualFile root = module.child(APP);
				VirtualFile japidViewDir = root.child(DirUtil.JAPIDVIEWS_ROOT);
				File japidFile = japidViewDir.getRealFile();
				if (japidFile.exists()) {
					String absoluteRootPath = root.getRealFile().getAbsolutePath();
					if( removeOrphanedJavaFrom(absoluteRootPath))
						hasOrphan = true;
				}
			}
			catch(Throwable t) {
				
			}
			
		}
		return hasOrphan;
	}

	private static boolean removeOrphanedJavaFrom(String root) {
		boolean hasRealOrphan = false;
		try {
			String pathname = root + File.separator + DirUtil.JAPIDVIEWS_ROOT;
			File src = new File(pathname);
			if (!src.exists()) {
				JapidFlags.log("Could not find required Japid package structure: " + pathname);
				JapidFlags.log("Please use \"play japid:mkdir\" command to create the Japid view structure.");
				return hasRealOrphan;
			}

			Set<File> oj = DirUtil.findOrphanJava(src, null);
			for (File j : oj) {
				String path = j.getPath();
				// JapidFlags.log("found: " + path);
				if (path.contains(DirUtil.JAVATAGS)) {

					// java tags, don't touch
				} else {
					hasRealOrphan = true;
					String realfile = pathname + File.separator + path;
					File file = new File(realfile);
					boolean r = file.delete();
					if (r)
						JapidFlags.log("deleted orphan " + realfile);
					else
						JapidFlags.log("failed to delete: " + realfile);
				}
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
		return hasRealOrphan;
	}

	/**
	 * check the current app and dependencies for changed templates
	 * @return
	 */
	public static List<File> reloadChanged() {
		List<File> reloadChanged = reloadChanged(new File(Play.applicationPath, APP).getAbsolutePath());
		Collection<VirtualFile> modules = Play.modules.values();
		for (VirtualFile module: modules) {
			try {
				VirtualFile root = module.child(APP);
				VirtualFile japidViewDir = root.child(DirUtil.JAPIDVIEWS_ROOT);
				File japidFile = japidViewDir.getRealFile();
				if (japidFile.exists()) {
					String absoluteRootPath = root.getRealFile().getAbsolutePath();
					reloadChanged.addAll(reloadChanged(absoluteRootPath));
				}
			}
			catch(Throwable t) {
				
			}
			
		}
		return reloadChanged;
	}
	
}
