/**
 * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Liferay Enterprise
 * Subscription License ("License"). You may not use this file except in
 * compliance with the License. You can obtain a copy of the License by
 * contacting Liferay, Inc. See the License for the specific language governing
 * permissions and limitations under the License, including but not limited to
 * distribution rights of the Software.
 *
 *
 *
 */

package com.liferay.portal.upload;

import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayInputStream;
import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayOutputStream;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.servlet.HttpHeaders;
import com.liferay.portal.kernel.servlet.ServletInputStreamAdapter;
import com.liferay.portal.kernel.util.FileUtil;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.ProgressTracker;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.portal.servlet.filters.uploadservletrequest.UploadServletRequestFilter;
import com.liferay.portal.util.PropsUtil;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

/**
 * @author Brian Myunghun Kim
 * @author Brian Wing Shun Chan
 * @author Harry Mark
 */
public class LiferayInputStream extends ServletInputStreamAdapter {

	public static final long THRESHOLD_SIZE = GetterUtil.getLong(
		PropsUtil.get(LiferayInputStream.class.getName() + ".threshold.size"));

	public LiferayInputStream(HttpServletRequest request) throws IOException {
		super(request.getInputStream());

		_session = request.getSession();
		_totalSize = request.getContentLength();

		if (_totalSize < 0) {
			_totalSize = GetterUtil.getLong(
				request.getHeader(HttpHeaders.CONTENT_LENGTH), _totalSize);
		}

		boolean createTempFile = GetterUtil.getBoolean(
			request.getAttribute(
				UploadServletRequestFilter.COPY_MULTIPART_STREAM_TO_FILE),
				Boolean.TRUE);

		if ((_totalSize >= THRESHOLD_SIZE) && createTempFile) {
			_tempFile = FileUtil.createTempFile();
		}
		else {
			_tempFile = null;

			request.removeAttribute(
				UploadServletRequestFilter.COPY_MULTIPART_STREAM_TO_FILE);
		}
	}

	public void cleanUp() {
		if (_tempFile != null) {
			if (_tempFileOutputStream != null) {
				try {
					_tempFileOutputStream.close();
				}
				catch (IOException ioe) {
					if (_log.isWarnEnabled()) {
						_log.warn(ioe, ioe);
					}
				}
			}

			_tempFile.delete();
		}
	}

	@Override
	public void close() throws IOException {
		super.close();

		if (_tempFileOutputStream != null) {
			_tempFileOutputStream.close();
		}
	}

	public ServletInputStream getCachedInputStream() throws IOException {
		if (_totalSize < THRESHOLD_SIZE) {
			return new ServletInputStreamAdapter(
				new UnsyncByteArrayInputStream(
					_cachedBytes.unsafeGetByteArray(), 0, _cachedBytes.size()));
		}
		else if (_tempFile != null) {
			return new ServletInputStreamAdapter(
				new FileInputStream(_tempFile));
		}
		else {
			return this;
		}
	}

	@Override
	public int read(byte[] b, int off, int len) throws IOException {
		int bytesRead = super.read(b, off, len);

		if (bytesRead > 0) {
			_totalRead += bytesRead;
		}
		else {
			return bytesRead;
		}

		int percent = (int)((_totalRead * 100L) / _totalSize);

		if (_log.isDebugEnabled()) {
			_log.debug(bytesRead + "/" + _totalRead + "=" + percent);
		}

		if (_totalSize > 0) {
			if (_totalSize < THRESHOLD_SIZE) {
				_cachedBytes.write(b, off, bytesRead);
			}
			else {
				_writeToTempFile(b, off, bytesRead);
			}
		}

		ProgressTracker progressTracker =
			(ProgressTracker)_session.getAttribute(LiferayFileUpload.PERCENT);

		Integer curPercent = null;

		if (progressTracker != null) {
			curPercent = progressTracker.getPercent();
		}

		if ((curPercent == null) || ((percent - curPercent.intValue()) >= 1)) {
			if (progressTracker == null) {
				progressTracker = new ProgressTracker(StringPool.BLANK);

				progressTracker.initialize(_session);
			}

			progressTracker.setPercent(percent);
		}

		return bytesRead;
	}

	private void _writeToTempFile(byte[] b, int off, int bytesRead)
		throws IOException {

		if ((_tempFile != null) && (bytesRead > 0)) {
			if (_tempFileOutputStream == null) {
				_tempFileOutputStream = new FileOutputStream(_tempFile, true);
			}

			_tempFileOutputStream.write(b, off, bytesRead);
		}
	}

	private static Log _log = LogFactoryUtil.getLog(LiferayInputStream.class);

	private UnsyncByteArrayOutputStream _cachedBytes =
		new UnsyncByteArrayOutputStream();
	private final HttpSession _session;
	private final File _tempFile;
	private OutputStream _tempFileOutputStream;
	private long _totalRead;
	private long _totalSize;

}