/*******************************************************************************
 * Copyright (c) 2000, 2016 QNX Software Systems and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     QNX Software Systems - Initial API and implementation
 *******************************************************************************/
package org.eclipse.cdt.make.internal.core;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;

import org.eclipse.cdt.utils.spawner.ProcessFactory;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;

public class MakeRecon extends OutputStream {

	IPath make;
	String[] args;
	String[] environ;
	IPath directory;
	IProgressMonitor monitor;
	OutputStream console;
	MyList log;
	StringBuilder currentLine;

	class MyList extends ArrayList<String> {
		private static final long serialVersionUID = 1L;

		public void removeInterval(int start, int len) {
			removeRange(start, len);
		}
	}

	public MakeRecon(IPath buildCommand, String[] buildArguments, String[] env, IPath workingDirectory,
			IProgressMonitor mon, OutputStream cos) {
		this(buildCommand, new String[] { "-n" }, buildArguments, env, workingDirectory, mon, cos); //$NON-NLS-1$
	}

	public MakeRecon(IPath buildCommand, String[] options, String[] buildArguments, String[] env,
			IPath workingDirectory, IProgressMonitor mon, OutputStream cos) {

		make = buildCommand;

		args = new String[0];
		if (options != null) {
			String[] array = new String[args.length + options.length];
			System.arraycopy(args, 0, array, 0, args.length);
			System.arraycopy(options, 0, array, args.length, options.length);
			args = array;
		}
		if (buildArguments != null) {
			String[] array = new String[args.length + buildArguments.length];
			System.arraycopy(args, 0, array, 0, args.length);
			System.arraycopy(buildArguments, 0, array, args.length, buildArguments.length);
			args = array;
		}

		environ = env;
		directory = workingDirectory;
		monitor = mon;
		console = cos;
		currentLine = new StringBuilder();
		log = new MyList();

		// Get the buffer log.
		invokeMakeRecon();

	}

	private void invokeMakeRecon() {
		int i = 0;
		String[] array = new String[args.length + 1];
		array[0] = make.toOSString();
		System.arraycopy(args, 0, array, 1, args.length);
		Process p;
		try {
			p = ProcessFactory.getFactory().exec(array, environ, directory.toFile());
			InputStream in = p.getInputStream();
			BufferedReader reader = new BufferedReader(new InputStreamReader(in));
			// Swallow the output
			String line;
			try {
				while ((line = reader.readLine()) != null) {
					log.add(line);
					i++;
				}
			} catch (IOException e) {
			}
			try {
				in.close();
			} catch (IOException e) {
			}
			p.destroy();
			log.trimToSize();
		} catch (IOException e1) {
			i = IProgressMonitor.UNKNOWN;
		}
		monitor.beginTask("", i); //$NON-NLS-1$
	}

	/**
	 * @see java.io.OutputStream#close()
	 */
	@Override
	public void close() throws IOException {
		if (console != null) {
			console.close();
		}
		monitor.done();
	}

	/**
	 * @see java.io.OutputStream#flush()
	 */
	@Override
	public void flush() throws IOException {
		if (console != null) {
			console.flush();
		}
	}

	/**
	 * @see java.io.OutputStream#write(int)
	 */
	@Override
	public synchronized void write(int b) throws IOException {
		currentLine.append((char) b);
		checkProgress(false);
		if (console != null) {
			console.write(b);
		}
	}

	/**
	 * @see java.io.OutputStream#write(byte[], int, int)
	 */
	@Override
	public synchronized void write(byte[] b, int off, int len) throws IOException {
		if (b == null) {
			throw new NullPointerException();
		} else if (off != 0 || (len < 0) || (len > b.length)) {
			throw new IndexOutOfBoundsException();
		} else if (len == 0) {
			return;
		}
		currentLine.append(new String(b, 0, len));
		checkProgress(false);
		if (console != null) {
			console.write(b, off, len);
		}
	}

	private void checkProgress(boolean flush) {
		String buffer = currentLine.toString();
		int i = 0;
		while ((i = buffer.indexOf("\n")) != -1) { //$NON-NLS-1$
			String line = buffer.substring(0, i).trim(); // get rid of any trailing \r
			processLine(line);
			buffer = buffer.substring(i + 1); // skip the \n and advance
		}
		currentLine.setLength(0);
		if (flush) {
			if (buffer.length() > 0) {
				processLine(buffer);
			}
		} else {
			currentLine.append(buffer);
		}
	}

	private void processLine(String line) {
		int found = -1;
		for (int i = 0; i < log.size(); i++) {
			String s = log.get(i);
			if (s.startsWith(line)) {
				found = i;
				break;
			}
		}

		if (found != -1) {
			String show = log.get(found);
			if (show.length() > 50) {
				show = show.substring(0, 50);
			}
			monitor.subTask(show);
			monitor.worked(found + 1);
			log.removeInterval(0, found + 1);
		}
	}
}
