/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  The ASF licenses this file to You
 * under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.  For additional information regarding
 * copyright in this work, please see the NOTICE file in the top level
 * directory of this distribution.
 */

package org.apache.roller.weblogger.ui.rendering.util;

import java.text.SimpleDateFormat;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.roller.util.RollerConstants;
import org.apache.roller.util.DateUtil;
import org.apache.roller.weblogger.ui.rendering.mobile.MobileDeviceRepository;

/**
 * Utility class to localize the modification date header-related logic.
 */
public final class ModDateHeaderUtil {

	private static Log log = LogFactory.getLog(ModDateHeaderUtil.class);

	/**
	 * Instantiates a new mod date header util.
	 */
	private ModDateHeaderUtil() {
	}

	/**
	 * Sets the HTTP response status to 304 (NOT MODIFIED) if the request
	 * contains an If-Modified-Since header that specifies a time that is at or
	 * after the time specified by the value of lastModifiedTimeMillis
	 * <em>truncated to second granularity</em>. Returns true if the response
	 * status was set, false if not.
	 * 
	 * @param request
	 *            the request
	 * @param response
	 *            the response
	 * @param lastModifiedTimeMillis
	 *            the last modified time millis
	 * @param deviceType
	 *            the device type. Null to ignore ie no theme device type
	 *            swithing check.
	 * 
	 * @return true if a response status was sent, false otherwise.
	 */
	public static boolean respondIfNotModified(HttpServletRequest request,
			HttpServletResponse response, long lastModifiedTimeMillis,
			MobileDeviceRepository.DeviceType deviceType) {

		long sinceDate;
		try {
			sinceDate = request.getDateHeader("If-Modified-Since");
		} catch (IllegalArgumentException ex) {
			// this indicates there was some problem parsing the header value as
			// a date
			return false;
		}

		// truncate to seconds
		lastModifiedTimeMillis -= (lastModifiedTimeMillis % RollerConstants.SEC_IN_MS);

		if (log.isDebugEnabled()) {
			SimpleDateFormat dateFormat = new SimpleDateFormat(
					"EEE MMM dd 'at' h:mm:ss a");
			log.debug("since date = "
					+ DateUtil.format(new Date(sinceDate), dateFormat));
			log.debug("last mod date (trucated to seconds) = "
					+ DateUtil.format(new Date(lastModifiedTimeMillis),
							dateFormat));
		}

		// Set device type for device switching
		String eTag = null;
		if (deviceType != null) {
			// int code = new HashCodeBuilder().append(deviceType.name())
			// .hashCode();
			// eTag = String.valueOf(code);
			eTag = deviceType.name();
		}

		String previousToken = request.getHeader("If-None-Match");
		if (eTag != null && previousToken != null && eTag.equals(previousToken)
				&& lastModifiedTimeMillis <= sinceDate
				|| (eTag == null || previousToken == null)
				&& lastModifiedTimeMillis <= sinceDate) {

			if (log.isDebugEnabled()) {
				log.debug("NOT MODIFIED " + request.getRequestURL());
            }

			response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);

			// use the same date we sent when we created the ETag the
			// first time through
			response.setHeader("Last-Modified",
					request.getHeader("If-Modified-Since"));

			return true;
		} else {
			return false;
		}
	}

	/**
	 * Set the Last-Modified header using the given time in milliseconds. Note
	 * that because the header has the granularity of one second, the value will
	 * get truncated to the nearest second that does not exceed the provided
	 * value.
	 * <p/>
	 * This will also set the Expires header to a date in the past. This forces
	 * clients to revalidate the cache each time.
	 * 
	 * @param response
	 *            the response
	 * @param lastModifiedTimeMillis
	 *            the last modified time millis
	 * @param deviceType
	 *            the device type. Null to ignore ie no theme device type
	 *            swithing check.
	 */
	public static void setLastModifiedHeader(HttpServletResponse response,
			long lastModifiedTimeMillis,
			MobileDeviceRepository.DeviceType deviceType) {

		// Save our device type for device switching. Must use chaching on
		// headers for this to work.
		if (deviceType != null) {

			// int code = new HashCodeBuilder().append(deviceType.name())
			// .hashCode();
			// String eTag = String.valueOf(code);

			String eTag = deviceType.name();

			response.setHeader("ETag", eTag);
		}

		response.setDateHeader("Last-Modified", lastModifiedTimeMillis);
		// Force clients to revalidate each time
		// See RFC 2616 (HTTP 1.1 spec) secs 14.21, 13.2.1
		response.setDateHeader("Expires", 0);
		// We may also want this (See 13.2.1 and 14.9.4)
		// response.setHeader("Cache-Control","must-revalidate");

	}

}
