/**
 *  Copyright (c) 2015-2016 Angelo ZERR.
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the Eclipse Public License v1.0
 *  which accompanies this distribution, and is available at
 *  http://www.eclipse.org/legal/epl-v10.html
 *
 *  Contributors:
 *  Angelo Zerr <[email protected]> - initial API and implementation
 */
package ts.eclipse.ide.core.compiler;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;

import ts.client.Location;
import ts.cmd.Severity;
import ts.cmd.tsc.ITypeScriptCompilerMessageHandler;
import ts.eclipse.ide.core.TypeScriptCorePlugin;
import ts.eclipse.ide.core.resources.jsconfig.IDETsconfigJson;
import ts.eclipse.ide.core.utils.TypeScriptResourceUtil;
import ts.eclipse.ide.core.utils.WorkbenchResourceUtil;
import ts.eclipse.ide.internal.core.Trace;
import ts.resources.jsonconfig.TsconfigJson;
import ts.utils.FileUtils;

/**
 * Eclipse IDE implementation of {@link ITypeScriptCompilerMessageHandler} used
 * to track "tsc" message to:
 * 
 * <ul>
 * <li>add error marker to the *.ts files which have error.</li>
 * <li>refresh emitted files *.js and *.js.map files</li>
 * </ul>
 */
public class IDETypeScriptCompilerMessageHandler implements ITypeScriptCompilerMessageHandler {

	private final IContainer container;
	private final boolean listEmittedFiles;
	private final IDETsconfigJson tsconfig;
	private final List<IFile> filesToRefresh;
	private final List<IFile> emittedFiles;

	public IDETypeScriptCompilerMessageHandler(IContainer container, boolean listEmittedFiles, boolean deleteMarkers)
			throws CoreException {
		this.container = container;
		this.listEmittedFiles = listEmittedFiles;
		this.tsconfig = TypeScriptResourceUtil.findTsconfig(container);
		this.filesToRefresh = new ArrayList<IFile>();
		this.emittedFiles = new ArrayList<IFile>();
		if (deleteMarkers) {
			TypeScriptResourceUtil.deleteTscMarker(container);
		}
	}

	@Override
	public void addFile(String filePath, boolean emitted) {
		IFile file = getFile(filePath);
		if (file == null) {
			return;
		}
		List<IFile> files = emitted ? emittedFiles : filesToRefresh;
		if (!files.contains(file)) {
			files.add(file);
		}
	}

	private IFile getFile(String filePath) {
		IPath path = new Path(filePath);
		if (container.exists(path)) {
			return container.getFile(path);
		}
		return WorkbenchResourceUtil.findFileFromWorkspace(filePath);
	}

	public List<IFile> getFilesToRefresh() {
		return filesToRefresh;
	}

	@Override
	public void onCompilationCompleteWatchingForFileChanges() {
		try {
			refreshEmittedFiles();
		} catch (CoreException e) {
			TypeScriptCorePlugin.logError(e);
		}
	}

	/**
	 * Refresh emitted files *.js , *.js.map
	 * 
	 * @throws CoreException
	 */
	public void refreshEmittedFiles() throws CoreException {
		// refresh *.js, *.js.map files
		for (IFile emittedFile : emittedFiles) {
			TypeScriptResourceUtil.refreshFile(emittedFile, true);
		}
		for (IFile tsFile : getFilesToRefresh()) {
			try {
				TypeScriptResourceUtil.refreshAndCollectEmittedFiles(tsFile, tsconfig, true, null);
			} catch (CoreException e) {
				Trace.trace(Trace.SEVERE, "Error while tsc compilation when ts file is refreshed", e);
			}
		}
		// refresh outFile if tsconfig.json defines it.
		if (tsconfig != null) {
			// Refresh *.js outFile
			IFile outFile = tsconfig.getOutFile();
			if (outFile != null) {
				TypeScriptResourceUtil.refreshFile(outFile, true);
				// Refresh *.js.map outFile
				IContainer outDir = outFile.getParent();
				IPath mapFileNamePath = WorkbenchResourceUtil.getRelativePath(outFile, outDir)
						.addFileExtension(FileUtils.MAP_EXTENSION);
				TypeScriptResourceUtil.refreshAndCollectEmittedFile(mapFileNamePath, outDir, true, null);
			}
		}
	}

	public IDETsconfigJson getTsconfig() {
		return tsconfig;
	}

	@Override
	public void addError(String filename, Location startLoc, Location endLoc, Severity severity, String code,
			String message) {
		IFile file = getFile(filename);
		if (file != null) {
			try {
				String error = TypeScriptResourceUtil.formatTscError(code, message);
				TypeScriptResourceUtil.addTscMarker(file, error, getSeverity(severity), startLoc.getLine());
			} catch (CoreException e) {

			}
		}
	}

	private int getSeverity(Severity severity) {
		switch (severity) {
		case error:
			return IMarker.SEVERITY_ERROR;
		case info:
			return IMarker.SEVERITY_INFO;
		default:
			return IMarker.SEVERITY_WARNING;
		}
	}

	public boolean isWatch() {
		TsconfigJson tsconfig = getTsconfig();
		return tsconfig != null && tsconfig.getCompilerOptions() != null
				&& tsconfig.getCompilerOptions().isWatch() != null && tsconfig.getCompilerOptions().isWatch();
	}

	public boolean isListEmittedFiles() {
		return listEmittedFiles;
	}
}