/*******************************************************************************
 * Copyright (c) 2007, 2018 BEA Systems, Inc.
 * 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:
 *    wharley@bea.com - initial API and implementation
 *******************************************************************************/

package org.eclipse.jdt.internal.apt.pluggable.core.filer;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.apt.core.internal.env.BinaryFileOutputStream;
import org.eclipse.jdt.apt.core.internal.util.FileSystemUtil;
import org.eclipse.jdt.internal.apt.pluggable.core.Apt6Plugin;
import org.eclipse.jdt.internal.apt.pluggable.core.dispatch.IdeProcessingEnvImpl;

/**
 * @see BinaryFileOutputStream
 */
public class IdeNonSourceOutputStream  extends ByteArrayOutputStream
{
	private final IdeProcessingEnvImpl _env;
	private final IFile _file;
	private final Collection<IFile> _parentFiles;
	
	public IdeNonSourceOutputStream(IdeProcessingEnvImpl env, IFile file, Collection<IFile> parentFiles) {
		_env = env;
		_file = file;
		_parentFiles = parentFiles;
	}

	@Override
	public void close() throws IOException {
		super.close();
		
		InputStream contents = new ByteArrayInputStream(toByteArray());
		try {
			
			boolean contentsChanged = true;
			if (!_file.exists()) {
				saveToDisk(contents, true);
			}
			else {
				InputStream in = null;
				InputStream oldData = null;
				try {
					// Only write the contents if the data is different
					in = new ByteArrayInputStream(toByteArray());
					oldData = new BufferedInputStream(_file.getContents());
					if (FileSystemUtil.compareStreams(in, oldData)) {
						contentsChanged = false;
					}
				}
				catch (CoreException ce) {
					// Ignore -- couldn't read the old data, so assume it's different
					contentsChanged = true;
				}
				finally {
					closeInputStream(in);
					closeInputStream(oldData);
				}
				if (contentsChanged) {
					contents.reset();
					saveToDisk(contents, false);
				}
			}
		}
		finally {
			closeInputStream(contents);
		}
		
		// If there are no parents, we don't need to track dependencies
		if (_parentFiles != null && !_parentFiles.isEmpty()) {
			_env.getAptProject().getGeneratedFileManager(_env.isTestCode()).addGeneratedFileDependency(_parentFiles, _file);
			_env.addNewResource(_file);
		}
	}
	
	private void closeInputStream(InputStream stream) {
		if (stream != null) {
			try {
				stream.close();
			}
			catch (IOException ioe) {}
		}
	}
	
	private void saveToDisk(InputStream toSave, boolean create) throws IOException{
		try {
			FileSystemUtil.makeDerivedParentFolders(_file.getParent());
			if (create) {
				_file.create(toSave, IResource.FORCE | IResource.DERIVED, null);
			}
			else {
				_file.setContents(toSave, true, false, null);
			}
		}
		catch (CoreException ce) {
			if (_file.exists()) {
				// Do nothing. This is a case-insensitive file system mismatch,
				// and the underlying platform has saved the contents already.
			}
			else {
				Apt6Plugin.log(ce, "Could not create generated non-Java file " + _file.getName()); //$NON-NLS-1$
				throw new IOException(ce);
			}
		}
	}

}
