/*
  Copyright (C) 2007 Helge Hess

  This file is part of JOPE.

  JOPE is free software; you can redistribute it and/or modify it under
  the terms of the GNU Lesser General Public License as published by the
  Free Software Foundation; either version 2, or (at your option) any
  later version.

  JOPE is distributed in the hope that it will be useful, but WITHOUT ANY
  WARRANTY; without even the implied warranty of MERCHANTABILITY or
  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
  License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with JOPE; see the file COPYING.  If not, write to the
  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
  02111-1307, USA.
*/
package org.opengroupware.jope.appserver.publisher;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.opengroupware.jope.appserver.core.WOContext;
import org.opengroupware.jope.appserver.core.WOElement;
import org.opengroupware.jope.appserver.core.WOMessage;
import org.opengroupware.jope.appserver.core.WOResponse;

/*
 * JoResource
 * 
 * TODO: document
 */
public class JoResource extends WOElement implements IJoObject {
  protected static final Log log = LogFactory.getLog("JoResource");
  
  protected URL url;
  
  public JoResource(URL _url) {
    this.url = _url;
  }
  
  /* generate response */

  @Override
  public void appendToResponse(WOResponse _r, WOContext _ctx) {
    URLConnection con = null;
    try {
      con = this.url.openConnection();
    }
    catch (IOException coe) {
      log.warn("could not open connection to url: " + this.url);
      _r.setStatus(WOMessage.HTTP_STATUS_NOT_FOUND);
      return;
    }
    
    /* open stream */
    
    InputStream is  = null;
    try {
      is = con.getInputStream();
    }
    catch (IOException ioe) {
      log.warn("could not open stream to url: " + this.url);
      _r.setStatus(WOMessage.HTTP_STATUS_NOT_FOUND);
      return;
    }
    
    /* transfer */
    
    try {
      String mimeType = con.getContentType();
      if (mimeType == null || "content/unknown".equals(mimeType)) {
        // TODO: fix this crap and use some real MIME/ext object
        mimeType = null;
        
        String p  = this.url.getPath();
        int idx = p.lastIndexOf('.');
        if (idx >= 0) {
          String ext = p.substring(idx + 1);
          if (ext.equals("css"))
            mimeType = "text/css";
          else if (ext.equals("txt"))
            mimeType = "text/plain";
          else if (ext.equals("js"))
            mimeType = "text/javascript";
          else
            log.error("MIME type unknown for extension '"+ext+"': " + this.url);
        }
        else
          log.error("could not detect MIME type for URL: " + this.url);
      }
      
      if (mimeType == null)
        mimeType = "application/octet-stream";
      
      _r.setHeaderForKey(mimeType, "content-type");
      _r.setHeaderForKey("" + con.getContentLength(), "content-length");
      
      /* setup caching headers */
      
      Date              now = new Date();
      GregorianCalendar cal = new GregorianCalendar();
      
      cal.setTime(new Date(con.getLastModified()));
      _r.setHeaderForKey(WOMessage.httpFormatDate(cal), "last-modified");
      
      cal.setTime(now);
      _r.setHeaderForKey(WOMessage.httpFormatDate(cal), "date");
      
      if (mimeType.startsWith("image/"))
        cal.add(Calendar.HOUR, 1);
      else if (mimeType.startsWith("text/css"))
        cal.add(Calendar.MINUTE, 10);
      else
        cal.add(Calendar.SECOND, 5);
      _r.setHeaderForKey(WOMessage.httpFormatDate(cal), "expires");
      
      /* start streaming */
      
      _r.enableStreaming();
      
      byte[] buffer = new byte[0xFFFF];
      for (int len; (len = is.read(buffer)) != -1; )
        _r.appendContentData(buffer, len);
    }
    catch (IOException e) {
      log.error("IO error trying to deliver resource: " + this.url, e);
      _r.setStatus(WOMessage.HTTP_STATUS_INTERNAL_ERROR);
    }
    finally {
      try {
        if (is != null) is.close();
      }
      catch (IOException e) {
        log.warn("could not close URL input stream: " + this.url, e);
      }
    }
  }
  
  /* recursive lookup */
  
  public Object lookupName(String _name, IJoContext _ctx, boolean _acquire) {
    /* only process names, don't try to be smart */
    if ("/".equals(_name) || "..".equals(_name) || ".".equals(_name))
      return null;
    
    URL nurl = null;
    try {
      // ugly hack
      String s = this.url.toExternalForm();
      if (!s.endsWith("/")) s += "/";
      nurl = new URL(s + _name);
    }
    catch (MalformedURLException e) {
      return e;
    }
    if (nurl == null) return null;
    
    return new JoResource(nurl);
  }
  
  /* description */

  @Override
  public void appendAttributesToDescription(StringBuilder _d) {
    super.appendAttributesToDescription(_d);
    
    if (this.url != null)
      _d.append(" url=" + this.url);
  }
}
