/* SpreadsheetTag.java

	Purpose:
		
	Description:
		
	History:
		Tue Dec 26 08:48:59 TST 2010, Created by Ashish Dasnurkar

Copyright (C) 2010 Potix Corporation. All Rights Reserved.

*/
package org.zkoss.zss.jsp.ui;

import java.io.IOException;
import java.io.Serializable;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.DynamicAttributes;
import javax.servlet.jsp.tagext.SimpleTagSupport;

import org.zkoss.lang.reflect.Fields;
import org.zkoss.util.logging.Log;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.GenericRichlet;
import org.zkoss.zk.ui.Page;
import org.zkoss.zk.ui.ext.AfterCompose;
import org.zkoss.zk.ui.metainfo.ComponentDefinition;
import org.zkoss.zkplus.embed.Renders;
import org.zkoss.zss.api.model.Book;
import org.zkoss.zss.jsp.BookProvider;
import org.zkoss.zss.ui.Spreadsheet;

/**
 * ZK Spreadsheet Tag implementation.
 * @author ashish
 *
 */
public class SpreadsheetTag extends SimpleTagSupport implements DynamicAttributes, Serializable {
	private static final long serialVersionUID = 6306465247069736585L;
	
	private static final Log log = Log.lookup(SpreadsheetTag.class);
	private Spreadsheet _spreadsheet;
	private Map _attrMap = new LinkedHashMap();
	private String _use;
	private ComposerHandler _composehandler;
	
	private Object _bookProvider;
	
	/**
	 * Reders ZK Spreadsheet JSP tag
	 * @throws JspException
	 * @throws IOException
	 */
	public void doTag() throws JspException, IOException {
		PageContext pgCtx = (PageContext) getJspContext();
		final ServletContext svlctx = pgCtx.getServletContext();
		final HttpServletRequest request = (HttpServletRequest) pgCtx.getRequest();
		final HttpServletResponse response = (HttpServletResponse) pgCtx.getResponse();
		StringWriter writer = new StringWriter();
		final HashMap arg = new HashMap();
		try {
			Renders.render(svlctx, request, response, 
					new GenericRichlet() {
						public void service(Page page) throws Exception {
							initComponent(page);
							
							BookProvider bp = getRealBookProvider();
							if(bp!=null){
								Book book = bp.loadBook(svlctx, request, response);
								_spreadsheet.setBook(book);
							}
							arg.put("zssId", _spreadsheet.getId());
							arg.put("zssUuid", _spreadsheet.getUuid());
							arg.put("desktopId", page.getDesktop().getId());
							
							doAfterComposeComponent();
						}
					}, null, writer);
			
			writer.append("<script type=\"text/javascript\">");
			String identify = "zssjsp['"+arg.get("zssId")+"']";
			writer.append("zk.afterLoad('zssjsp',function(){");
			writer.append(identify+"={};");
			writer.append(identify+".uuid='"+arg.get("zssUuid")+"';");
			writer.append(identify+".desktopId='"+arg.get("desktopId")+"';");
			writer.append("});");
			writer.append("</script>\n");
			
		} catch (ServletException e) {
			throw new JspException(e.getMessage(),e);
		}
		getJspContext().getOut().write(writer.toString());
	}
	
	/**
	 * Inits Spreadsheet component
	 * @param page
	 * @throws Exception
	 */
	protected void initComponent(Page page) throws Exception {
		String tagName = getComponentName();
		ComponentDefinition compdef = page.getComponentDefinition(tagName, true);
		if(compdef==null)
			throw new JspException("can't find this Component's definition:"+tagName);
		_spreadsheet = (Spreadsheet) compdef.resolveImplementationClass(page, getUse()).newInstance();
		_spreadsheet.setPage(page);
		_composehandler = new ComposerHandler(_attrMap.remove("apply"));
		evaluateDynaAttributes(_spreadsheet, _attrMap);
	}

	/**
	 * The name of the component from the ZK Component Definition
	 * @return name of the component from the ZK Component Definition
	 */
	protected String getComponentName() {
		return "spreadsheet";
	}

	/**
	 * Invokes composer for further processing after composing component 
	 * @throws JspException
	 */
	protected void doAfterComposeComponent() throws JspException {
		//call aftercompose for some sparedhseet initialization since 3.0.0
		if (_spreadsheet instanceof AfterCompose)
			((AfterCompose)_spreadsheet).afterCompose();
		_composehandler.doAfterCompose(_spreadsheet);// Composer fire...
	}

	
	/**
	 * 
	 * Evaluate all attributes.
	 * @param target the target component
	 * @param attrs
	 */



	protected void evaluateDynaAttributes(Component target, Map attrs) throws NoSuchMethodException {
		
		for(Iterator itor = attrs.entrySet().iterator();itor.hasNext();){
			Map.Entry entry= (Entry) itor.next();
			String attnm = (String)entry.getKey();
			Object value = entry.getValue();
			evaluateDynaAttribute(target, attnm, value);
		}
	}

	/**
	 * Test if the attribute are annotation or component attribute.<br>
	 * If(is Component attribute)Invokes setter method to update all 
	 * assigned attribute.
	 * @param target
	 * @param attnm
	 * @param value
	 */


	public static void evaluateDynaAttribute(Component target, String attnm, Object value) throws NoSuchMethodException {
		Fields.setByCompound(target, attnm, value, true);
	}

	/**
	 * Stores attribute name-value pairs for later processing
	 * @throws JspException
	 */
	public void setDynamicAttribute(String uri, String localName, Object value)
			throws JspException {
		_attrMap.put(localName, value);
	}

    /**
     * Returns the class name that is used to implement the component
     * associated with this tag.
     *
     * <p>Default: null
     *
     * @return the class name used to implement the component, or null
     * to use the default
     */
	public String getUse() {
		return _use;
	}

    /**
     * Sets the class name that is used to implement the component
     * associated with this tag.
     *
     * @param use the class name used to implement the component, or null
     * to use the default
     */
	public void setUse(String use) {
		this._use = use;
	}

	public Object getBookProvider() {
		return _bookProvider;
	}

	/**
	 * Sets the book provider, it could either a instance or a class name of {@link BookProvider} 
	 * @param bookProvider
	 */
	public void setBookProvider(Object bookProvider) {
		this._bookProvider = bookProvider;
	}
	
	private BookProvider getRealBookProvider()throws JspException {
		if(_bookProvider == null || _bookProvider instanceof BookProvider){
			return (BookProvider)_bookProvider;
		}
		BookProvider p = null;
		if(_bookProvider instanceof Class){
			try {
				return (BookProvider)((Class)_bookProvider).newInstance();
			} catch (Exception e) {
				throw new JspException(e.getMessage(),e);
			}
		}
		if(_bookProvider instanceof String){
			try{
				return (BookProvider) this.getClass().forName((String)_bookProvider).newInstance();
			} catch (Exception e) {
				throw new JspException(e.getMessage(),e);
			}
		}
		throw new JspException("can't initialize book provider by "+_bookProvider);
	}
	
}
