/**
 * 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.kernel.portlet;

import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.servlet.BrowserSnifferUtil;
import com.liferay.portal.kernel.servlet.HttpHeaders;
import com.liferay.portal.kernel.util.ArrayUtil;
import com.liferay.portal.kernel.util.FileUtil;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.HttpUtil;
import com.liferay.portal.kernel.util.PropsUtil;
import com.liferay.portal.kernel.util.StreamUtil;
import com.liferay.portal.kernel.util.StringBundler;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.portal.kernel.util.Validator;
import com.liferay.portal.util.PortalUtil;

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

import java.nio.channels.Channels;
import java.nio.channels.FileChannel;

import javax.portlet.MimeResponse;
import javax.portlet.PortletRequest;
import javax.portlet.ResourceResponse;

import javax.servlet.http.HttpServletRequest;

/**
 * @author Brian Wing Shun Chan
 */
public class PortletResponseUtil {

	/**
	 * @deprecated As of 6.1.0
	 */
	public static void sendFile(
			MimeResponse mimeResponse, String fileName, byte[] bytes)
		throws IOException {

		sendFile(null, mimeResponse, fileName, bytes);
	}

	/**
	 * @deprecated As of 6.1.0
	 */
	public static void sendFile(
			MimeResponse mimeResponse, String fileName, byte[] bytes,
			String contentType)
		throws IOException {

		sendFile(null, mimeResponse, fileName, bytes, contentType);
	}

	/**
	 * @deprecated As of 6.1.0
	 */
	public static void sendFile(
			MimeResponse mimeResponse, String fileName, InputStream inputStream)
		throws IOException {

		sendFile(null, mimeResponse, fileName, inputStream);
	}

	/**
	 * @deprecated As of 6.1.0
	 */
	public static void sendFile(
			MimeResponse mimeResponse, String fileName, InputStream inputStream,
			int contentLength, String contentType)
		throws IOException {

		sendFile(
			null, mimeResponse, fileName, inputStream, contentLength,
			contentType);
	}

	/**
	 * @deprecated As of 6.1.0
	 */
	public static void sendFile(
			MimeResponse mimeResponse, String fileName, InputStream inputStream,
			String contentType)
		throws IOException {

		sendFile(null, mimeResponse, fileName, inputStream, contentType);
	}

	public static void sendFile(
			PortletRequest portletRequest, MimeResponse mimeResponse,
			String fileName, byte[] bytes)
		throws IOException {

		sendFile(portletRequest, mimeResponse, fileName, bytes, null);
	}

	public static void sendFile(
			PortletRequest portletRequest, MimeResponse mimeResponse,
			String fileName, byte[] bytes, String contentType)
		throws IOException {

		sendFile(
			portletRequest, mimeResponse, fileName, bytes, contentType, null);
	}

	public static void sendFile(
			PortletRequest portletRequest, MimeResponse mimeResponse,
			String fileName, byte[] bytes, String contentType,
			String contentDispositionType)
		throws IOException {

		setHeaders(
			portletRequest, mimeResponse, fileName, contentType,
			contentDispositionType);

		write(mimeResponse, bytes);
	}

	public static void sendFile(
			PortletRequest portletRequest, MimeResponse mimeResponse,
			String fileName, InputStream inputStream)
		throws IOException {

		sendFile(portletRequest, mimeResponse, fileName, inputStream, null);
	}

	public static void sendFile(
			PortletRequest portletRequest, MimeResponse mimeResponse,
			String fileName, InputStream inputStream, int contentLength,
			String contentType)
		throws IOException {

		sendFile(
			portletRequest, mimeResponse, fileName, inputStream, contentLength,
			contentType, null);
	}

	public static void sendFile(
			PortletRequest portletRequest, MimeResponse mimeResponse,
			String fileName, InputStream inputStream, int contentLength,
			String contentType, String contentDispositionType)
		throws IOException {

		setHeaders(
			portletRequest, mimeResponse, fileName, contentType,
			contentDispositionType);

		write(mimeResponse, inputStream, contentLength);
	}

	public static void sendFile(
			PortletRequest portletRequest, MimeResponse mimeResponse,
			String fileName, InputStream inputStream, String contentType)
		throws IOException {

		sendFile(
			portletRequest, mimeResponse, fileName, inputStream, 0,
			contentType);
	}

	public static void write(MimeResponse mimeResponse, byte[] bytes)
		throws IOException {

		write(mimeResponse, bytes, 0, 0);
	}

	public static void write(
			MimeResponse mimeResponse, byte[] bytes, int offset,
			int contentLength)
		throws IOException {

		// LEP-3122

		if (!mimeResponse.isCommitted()) {

			// LEP-536

			if (contentLength == 0) {
				contentLength = bytes.length;
			}

			if (mimeResponse instanceof ResourceResponse) {
				ResourceResponse resourceResponse =
					(ResourceResponse)mimeResponse;

				resourceResponse.setContentLength(contentLength);
			}

			OutputStream outputStream = mimeResponse.getPortletOutputStream();

			outputStream.write(bytes, offset, contentLength);
		}
	}

	public static void write(MimeResponse mimeResponse, byte[][] bytesArray)
		throws IOException {

		// LEP-3122

		if (mimeResponse.isCommitted()) {
			return;
		}

		// LEP-536

		long contentLength = 0;

		for (byte[] bytes : bytesArray) {
			contentLength += bytes.length;
		}

		if (mimeResponse instanceof ResourceResponse) {
			ResourceResponse resourceResponse = (ResourceResponse)mimeResponse;

			setContentLength(resourceResponse, contentLength);
		}

		OutputStream outputStream = mimeResponse.getPortletOutputStream();

		for (byte[] bytes : bytesArray) {
			outputStream.write(bytes);
		}
	}

	public static void write(MimeResponse mimeResponse, File file)
		throws IOException {

		FileInputStream fileInputStream = new FileInputStream(file);

		FileChannel fileChannel = fileInputStream.getChannel();

		try {
			long contentLength = fileChannel.size();

			if (mimeResponse instanceof ResourceResponse) {
				ResourceResponse resourceResponse =
					(ResourceResponse)mimeResponse;

				setContentLength(resourceResponse, contentLength);
			}

			fileChannel.transferTo(
				0, contentLength,
				Channels.newChannel(mimeResponse.getPortletOutputStream()));
		}
		finally {
			fileChannel.close();
		}
	}

	public static void write(MimeResponse mimeResponse, InputStream inputStream)
		throws IOException {

		write(mimeResponse, inputStream, 0);
	}

	public static void write(
			MimeResponse mimeResponse, InputStream inputStream,
			int contentLength)
		throws IOException {

		OutputStream outputStream = null;

		try {
			if (mimeResponse.isCommitted()) {
				return;
			}

			if (contentLength > 0) {
				if (mimeResponse instanceof ResourceResponse) {
					ResourceResponse resourceResponse =
						(ResourceResponse)mimeResponse;

					resourceResponse.setContentLength(contentLength);
				}
			}

			StreamUtil.transfer(
				inputStream, mimeResponse.getPortletOutputStream(), false);
		}
		finally {
			StreamUtil.cleanUp(inputStream, outputStream);
		}
	}

	public static void write(MimeResponse mimeResponse, String s)
		throws IOException {

		write(mimeResponse, s.getBytes(StringPool.UTF8));
	}

	protected static void setContentLength(
		ResourceResponse response, long contentLength) {

		response.setProperty(
			HttpHeaders.CONTENT_LENGTH, String.valueOf(contentLength));
	}

	protected static void setHeaders(
		PortletRequest portletRequest, MimeResponse mimeResponse,
		String fileName, String contentType, String contentDispositionType) {

		if (_log.isDebugEnabled()) {
			_log.debug("Sending file of type " + contentType);
		}

		// LEP-2201

		if (Validator.isNotNull(contentType)) {
			mimeResponse.setContentType(contentType);
		}

		mimeResponse.setProperty(
			HttpHeaders.CACHE_CONTROL, HttpHeaders.CACHE_CONTROL_PRIVATE_VALUE);

		if (Validator.isNull(fileName)) {
			return;
		}

		String contentDispositionFileName = "filename=\"" + fileName + "\"";

		// If necessary for non-ASCII characters, encode based on RFC 2184.
		// However, not all browsers support RFC 2184. See LEP-3127.

		boolean ascii = true;

		for (int i = 0; i < fileName.length(); i++) {
			if (!Validator.isAscii(fileName.charAt(i))) {
				ascii = false;

				break;
			}
		}

		try {
			if (!ascii) {
				String encodedFileName = HttpUtil.encodeURL(fileName, true);

				HttpServletRequest request = PortalUtil.getHttpServletRequest(
					portletRequest);

				if (BrowserSnifferUtil.isIe(request)) {
					contentDispositionFileName =
						"filename=\"" + encodedFileName + "\"";
				}
				else {
					contentDispositionFileName =
						"filename*=UTF-8''" + encodedFileName;
				}
			}
		}
		catch (Exception e) {
			if (_log.isWarnEnabled()) {
				_log.warn(e);
			}
		}

		if (Validator.isNull(contentDispositionType)) {
			String extension = GetterUtil.getString(
				FileUtil.getExtension(fileName));

			extension = StringUtil.toLowerCase(extension);

			String[] mimeTypesContentDispositionInline = null;

			try {
				mimeTypesContentDispositionInline = PropsUtil.getArray(
					"mime.types.content.disposition.inline");
			}
			catch (Exception e) {
				mimeTypesContentDispositionInline = new String[0];
			}

			if (ArrayUtil.contains(
					mimeTypesContentDispositionInline, extension)) {

				contentDispositionType = HttpHeaders.CONTENT_DISPOSITION_INLINE;
			}
			else {
				contentDispositionType =
					HttpHeaders.CONTENT_DISPOSITION_ATTACHMENT;
			}
		}

		StringBundler sb = new StringBundler(4);

		sb.append(contentDispositionType);
		sb.append(StringPool.SEMICOLON);
		sb.append(StringPool.SPACE);
		sb.append(contentDispositionFileName);

		mimeResponse.setProperty(
			HttpHeaders.CONTENT_DISPOSITION, sb.toString());
	}

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

}