content: /**
   *  Copyright (c) 2015-2017 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 <angelo.zerr@gmail.com> - initial API and implementation
   *  
   */
  package ts.eclipse.ide.terminal.interpreter.internal;
  
  import java.io.File;
  import java.util.ArrayList;
  import java.util.List;
  
  import ts.eclipse.ide.terminal.interpreter.CommandTerminalService;
  import ts.eclipse.ide.terminal.interpreter.ICommandInterpreterListener;
  import ts.eclipse.ide.terminal.interpreter.ITerminalCommandListener;
  import ts.eclipse.ide.terminal.interpreter.LineCommand;
  
  /**
   * Command terminal tracker.
   *
   */
  public abstract class CommandTerminalTracker extends AnsiHandler {
  
  	private static final String TILD = "~";
  
  	private final List<ITerminalCommandListener> listeners;
  
  	private int columns;
  
  	private LineCommand lineCommand;
  
  	private String workingDir;
  
  	public CommandTerminalTracker() {
  		this.columns = 80;
  		this.listeners = new ArrayList<>();
  		onOpenTerminal(getUserHome());
  	}
  
  	@Override
  	public synchronized void parse(byte[] byteBuffer, int bytesRead, String encoding) {
  		onContentReadFromStream(byteBuffer, bytesRead, encoding);
  		super.parse(byteBuffer, bytesRead, encoding);
  	}
  
  	@Override
  	protected void processText(String text) {
  		processText(text, columns);
  	}
  
  	protected void processText(String text, int columns) {
  		onProcessText(text, columns);
  		processLine(text);
  	}
  
  	@Override
  	protected void processCarriageReturnLineFeed() {
  		// CRLF is thrown when:
  		// - User types 'Enter' to submit a command.
  		// - OR in Windows OS, when a new line is displayed in the DOS Command.
  		onCarriageReturnLineFeed();
  	}
  
  	private void processLine(String line) {
  		if (line == null) {
  			return;
  		}
  		if (!tryTerminateCommand(line)) {
  			tryExecutingCommand(line);
  		}
  
  		// else if (lineCommand != null) {
  		// executingCommand(line, lineCommand);
  		// }
  	}
  
  	private void tryExecutingCommand(String line) {
  		if (lineCommand == null) {
  			return;
  		}
  		executingCommand(line, lineCommand);
  	}
  
  	public boolean tryTerminateCommand(String line) {
  		String workingDir = null;
  		int length = line.length();
  		char last = line.charAt(length - 1);
  		if (last == '>' || last == '$') {
  			workingDir = line.substring(0, length - 1);
  		} else if (length >= 2 && last == ' ' && line.charAt(length - 2) == '$') {
  			workingDir = line.substring(0, length - 2);
  		}
  		if (workingDir == null) {
  			return false;
  		}
  		workingDir = resolveTild(workingDir);
  		if (isDirectory(workingDir)) {
  			terminateCommand(workingDir);
  			return true;
  		}
  		return false;
  	}
  
  	protected boolean isDirectory(String dir) {
  		return new File(dir).exists();
  	}
  
  	private String resolveTild(String line) {
  		if (line.contains(TILD)) {
  			String home = getUserHome();
  			return line.replaceFirst("^~", home);
  		}
  		return line;
  	}
  
  	protected String getUserHome() {
  		String home = System.getProperty("user.home");
  		if (File.separatorChar == '\\') {
  			// Windows OS with win-bash
  			home = home.replaceAll("[\\\\]", "/");
  		}
  		return home;
  	}
  
  	// ---------------------- Trace to generate JUnit
  
  	private void onOpenTerminal(String userHome) {
  		for (ICommandInterpreterListener listener : CommandTerminalService.getInstance().getInterpreterListeners()) {
  			listener.onOpenTerminal(userHome);
  		}
  	}
  
  	private void onContentReadFromStream(byte[] byteBuffer, int bytesRead, String encoding) {
  		for (ICommandInterpreterListener listener : CommandTerminalService.getInstance().getInterpreterListeners()) {
  			listener.onContentReadFromStream(byteBuffer, bytesRead, encoding);
  		}
  	}
  
  	private void onProcessText(String text, int columns) {
  		for (ICommandInterpreterListener listener : CommandTerminalService.getInstance().getInterpreterListeners()) {
  			listener.onProcessText(text, columns);
  		}
  	}
  
  	private void onCarriageReturnLineFeed() {
  		for (ICommandInterpreterListener listener : CommandTerminalService.getInstance().getInterpreterListeners()) {
  			listener.onCarriageReturnLineFeed();
  		}
  	}
  
  	public void setColumns(int columns) {
  		this.columns = columns;
  	}
  
  	public void addTerminalCommandListener(ITerminalCommandListener listener) {
  		synchronized (listeners) {
  			if (!listeners.contains(listener)) {
  				listeners.add(listener);
  			}
  		}
  	}
  
  	public void removeTerminalCommandListener(ITerminalCommandListener listener) {
  		synchronized (listeners) {
  			listeners.remove(listener);
  		}
  	}
  
  	private void terminateCommand(String workingDir) {
  		this.workingDir = workingDir;
  		if (lineCommand == null) {
  			submitCommand(new LineCommand(null));
  		} else {
  			lineCommand.setNewWorkingDir(workingDir);
  		}
  		LineCommand oldlineCommand = lineCommand;
  		lineCommand = null;
  		terminateCommand(oldlineCommand);
  	}
  
  	protected void terminateCommand(LineCommand lineCommand) {
  		ITerminalCommandListener l = lineCommand.getListener();
  		if (l != null) {
  			l.onTerminateCommand(lineCommand);
  		}
  		synchronized (listeners) {
  			for (ITerminalCommandListener listener : listeners) {
  				listener.onTerminateCommand(lineCommand);
  			}
  		}
  		for (ICommandInterpreterListener listener : CommandTerminalService.getInstance().getInterpreterListeners()) {
  			listener.onTerminateCommand(lineCommand);
  		}
  	}
  
  	protected void executingCommand(String line, LineCommand lineCommand) {
  		ITerminalCommandListener l = lineCommand.getListener();
  		if (l != null) {
  			l.onExecutingCommand(line, lineCommand);
  		}
  		synchronized (listeners) {
  			for (ITerminalCommandListener listener : listeners) {
  				listener.onExecutingCommand(line, lineCommand);
  			}
  		}
  		for (ICommandInterpreterListener listener : CommandTerminalService.getInstance().getInterpreterListeners()) {
  			listener.onExecutingCommand(line, lineCommand);
  		}
  	}
  
  	protected void submitCommand(LineCommand lineCommand) {
  		this.lineCommand = lineCommand;
  		lineCommand.setWorkingDir(workingDir);
  
  		ITerminalCommandListener l = lineCommand.getListener();
  		if (l != null) {
  			l.onSubmitCommand(lineCommand);
  		}
  		synchronized (listeners) {
  			for (ITerminalCommandListener listener : listeners) {
  				listener.onSubmitCommand(lineCommand);
  			}
  		}
  		for (ICommandInterpreterListener listener : CommandTerminalService.getInstance().getInterpreterListeners()) {
  			listener.onSubmitCommand(lineCommand);
  		}
  	}
  }
  