/* DefaultBookWidgetLoader.java

	Purpose:
		
	Description:
		
	History:
		Apr 23, 2008 6:48:10 PM     2008, Created by Dennis.Chen

Copyright (C) 2008 Potix Corporation. All Rights Reserved.
*/
package org.zkoss.zssex.ui.widget;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.zkoss.image.AImage;
import org.zkoss.image.Image;
import org.zkoss.util.logging.Log;
import org.zkoss.zk.ui.UiException;
import org.zkoss.zss.model.SChart;
import org.zkoss.zss.model.SPicture;
import org.zkoss.zss.model.SSheet;
import org.zkoss.zss.model.ViewAnchor;
import org.zkoss.zss.ui.Spreadsheet;
import org.zkoss.zss.ui.Widget;
import org.zkoss.zss.ui.sys.SpreadsheetCtrl;
import org.zkoss.zss.ui.sys.WidgetLoader;

/**
 * @author Dennis.Chen
 *
 */
public class DefaultBookWidgetLoader implements WidgetLoader {
	private static final long serialVersionUID = -4896590549570605193L;

	private static final Log log = Log.lookup(DefaultBookWidgetLoader.class);

	private Spreadsheet _spreadsheet;
//	private HeaderPositionHelper _rowSizeHelper;
//	private HeaderPositionHelper _colSizeHelper;
	
	static final int ZINDEX_START = 200;
	
	Map<String, Map<String, List<Widget>>> _widgetMap = new HashMap<String, Map<String, List<Widget>>>(); //<sheet-id, <relid, widget list>>
	
	public void init(Spreadsheet spreadsheet) {
		_spreadsheet = spreadsheet;
	}

	public void invalidate() {
		//do nothing
	}

	public void onSheetClean(SSheet sheet) {
		String key = sheet.getId();
		Map<String, List<Widget>> list = _widgetMap.get(key);
		if (list != null) {
			Iterator<List<Widget>> iter = list.values().iterator();
			while (iter.hasNext()) {
				for(Widget widget:iter.next()){
					((SpreadsheetCtrl) _spreadsheet.getExtraCtrl()).removeWidget(widget);
				}
			}
			_widgetMap.remove(key);
		}
//		_rowSizeHelper = null;
//		_colSizeHelper = null;
	}

	public void onSheetSelected(SSheet sheet) {
		final String key = sheet.getId();

//		_rowSizeHelper = ((SpreadsheetCtrl) _spreadsheet.getExtraCtrl()).getRowPositionHelper(key);
//		_colSizeHelper = ((SpreadsheetCtrl) _spreadsheet.getExtraCtrl()).getColumnPositionHelper(key);

		final Map<String, List<Widget>> list = new LinkedHashMap<String, List<Widget>>();
		preparePictureWidgets(sheet, list);
		prepareChartWidgets(sheet, list);
		if (list != null && list.size() > 0) {
			_widgetMap.put(key, list);
		}
	}
	
	public void onSheetFreeze(SSheet sheet) {
		List<SPicture> pictures = sheet.getPictures();
		if(pictures!=null){
			for(SPicture picture : pictures) {
				updatePictureWidget(sheet, picture);
			}
		}
		List<SChart> charts = sheet.getCharts();
		if(charts!=null){
			for(SChart chart : charts) {
				updateChartWidget(sheet, chart);
			}
		}
	}
	
	public void onColumnChange(SSheet sheet, int left, int right){
		List<SPicture> pictures = sheet.getPictures();
		if(pictures!=null){
			for(SPicture picture : pictures) {
				ViewAnchor anchor = picture.getAnchor();
				if(anchor.getColumnIndex()>=left){
					updatePictureWidget(sheet, picture);
				}
			}
		}
		List<SChart> charts = sheet.getCharts();
		if(charts!=null){
			for(SChart chartX : charts) {
				ViewAnchor anchor = chartX.getAnchor();
				if(anchor.getColumnIndex()>=left){
					updateChartWidget(sheet, chartX);
				}
				
			}
		}
	}

	public void onRowChange(SSheet sheet, int top, int bottom){
		List<SPicture> pictures = sheet.getPictures();
		if(pictures!=null){
			for(SPicture picture : pictures) {
				ViewAnchor anchor = picture.getAnchor();
				if(anchor.getRowIndex()>=top){
					updatePictureWidget(sheet, picture);
				}
			}
		}
		List<SChart> charts = sheet.getCharts();
		if(charts!=null){
			for(SChart chartX : charts) {
				ViewAnchor anchor = chartX.getAnchor();
				if(anchor.getRowIndex()>=top){
					updateChartWidget(sheet, chartX);
				}
			}
		}
	}
	
	
	private void preparePictureWidgets(SSheet sheet, Map<String, List<Widget>> widgets) {
		//show
		List<SPicture> pictures = sheet.getPictures();
		if (pictures == null || pictures.size() == 0)
			return;
		int zindex = ZINDEX_START + widgets.size();
		for(SPicture picture : pictures) {
			final List<Widget> imagewgts = newImageWidgetGroup(sheet, picture, zindex++);
			if (imagewgts != null && imagewgts.size()>0) {
				widgets.put(picture.getId(), imagewgts);
				for(Widget w:imagewgts){
					((SpreadsheetCtrl) _spreadsheet.getExtraCtrl()).addWidget(w);
				}
			}
		}
	}
	
	private void prepareChartWidgets(SSheet sheet, Map<String, List<Widget>> widgets) {
		//show
		List<SChart> charts = sheet.getCharts();
		if (charts == null || charts.size() == 0)
			return;
		int zindex = ZINDEX_START + widgets.size();
		for(SChart chartX : charts) {
			try {
				final List<Widget> chartwgts = newChartWidgetGroup(sheet, chartX, zindex++);
				if (chartwgts != null && chartwgts.size()>0) {
					widgets.put(chartX.getId(), chartwgts);
					for(Widget w:chartwgts){
						((SpreadsheetCtrl) _spreadsheet.getExtraCtrl()).addWidget(w);
					}
				}
			} catch (IOException e) {
				throw UiException.Aide.wrap(e);
			}
		}
	}
	
	public void addChartWidget(SSheet sheet, SChart chart) {
		final String key = sheet.getId();
		Map<String, List<Widget>> widgets = _widgetMap.get(key);
		if (widgets == null) {
			widgets = new LinkedHashMap<String, List<Widget>>();
		}
		int zindex = ZINDEX_START + widgets.size();
		
		try {
			final List<Widget> chartwgts = newChartWidgetGroup(sheet, chart, zindex);
			if (chartwgts != null && chartwgts.size()>0) {
				widgets.put(chart.getId(), chartwgts);
				for(Widget w:chartwgts){
					((SpreadsheetCtrl) _spreadsheet.getExtraCtrl()).addWidget(w);
				}
			}
		} catch (IOException e) {
			throw UiException.Aide.wrap(e);
		}
		if (widgets != null && widgets.size() > 0) {
			_widgetMap.put(key, widgets);
		}
	}
	
	public void addPictureWidget(SSheet sheet, SPicture picture) {
		final String key = sheet.getId();
		Map<String, List<Widget>> widgets = _widgetMap.get(key);
		if (widgets == null) {
			widgets = new LinkedHashMap<String, List<Widget>>();
		}
		int zindex = ZINDEX_START + widgets.size();
		
		
		final List<Widget> imagewgts = newImageWidgetGroup(sheet, picture, zindex);
		if (imagewgts != null && imagewgts.size()>0) {
			widgets.put(picture.getId(), imagewgts);
			for(Widget w:imagewgts){
				((SpreadsheetCtrl) _spreadsheet.getExtraCtrl()).addWidget(w);
			}
		}
		
		if (widgets != null && widgets.size() > 0) {
			_widgetMap.put(key, widgets);
		}
	}
	
	public void deletePictureWidget(SSheet sheet, String pictureId) {
		final String key = sheet.getId();
		Map<String, List<Widget>> widgets = _widgetMap.get(key);
		if (widgets == null) {
			return;
		}
		final List<Widget> imagewgts = widgets.remove(pictureId);
		if (imagewgts!=null) {
			for(Widget w:imagewgts){
				((SpreadsheetCtrl) _spreadsheet.getExtraCtrl()).removeWidget(w);
			}
		}
	}
	
	private List<String> getEffectedPanels(SSheet sheet,ViewAnchor anchor){
		int fr = sheet.getViewInfo().getNumOfRowFreeze();
		int fc = sheet.getViewInfo().getNumOfColumnFreeze();
		List<String> panels = new ArrayList<String>();
		panels.add("default");
		if(fr>0 && anchor.getRowIndex() < fr){
			panels.add("top");
		}
		if(fc>0 && anchor.getColumnIndex() < fc){
			panels.add("left");
		}
		if(fc>0 && fc>0 && anchor.getRowIndex() < fr && anchor.getColumnIndex() < fc){
			panels.add("corner");
		}
		return panels;
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public void updatePictureWidget(SSheet sheet,SPicture picture) {
		final String key = sheet.getId();
		Map<String, List<Widget>> widgets = _widgetMap.get(key);
		if (widgets == null) {
			return;
		}
		
		final List<Widget> imagewgts = widgets.get(picture.getId());
		if (imagewgts == null) {
			return;
		}
		
		final ViewAnchor anchor = picture.getAnchor();
		
		Image pimage = null;
		int zindex = ZINDEX_START + widgets.size();
		List<String> panels = getEffectedPanels(sheet, anchor);
		List<String> newadd = new ArrayList<String>(panels);
		for(Widget w: new ArrayList<Widget>(imagewgts)){//avoid concurrent modif
			if(panels.contains(w.getPanel())){
				pimage = ((ImageWidget)w).getContent();
				setupImageWigetAnchor(sheet, (ImageWidget)w, anchor, zindex = w.getZindex());
				newadd.remove(w.getPanel());
			}else{
				imagewgts.remove(w);
				((SpreadsheetCtrl) _spreadsheet.getExtraCtrl()).removeWidget(w);
			}
		}
		
		//add new widget after picture update
		for(String p:newadd){
			Widget w = newImageWidget(sheet, picture, pimage, p, zindex);
			imagewgts.add(w);
			((SpreadsheetCtrl) _spreadsheet.getExtraCtrl()).addWidget(w);
		}
	}

	private void setupImageWigetAnchor(SSheet sheet, ImageWidget imagewgt, ViewAnchor anchor, int zindex) {
		if (anchor != null) {
			int row = anchor.getRowIndex();
			int col = anchor.getColumnIndex();
			//TODO zss 3.5 check
//			int row2 = anchor.getRow2();
//			int col2 = anchor.getCol2();
			int height = anchor.getHeight();
			int width = anchor.getWidth();
			int left = anchor.getXOffset();
			int top = anchor.getYOffset();
			imagewgt.setRow(row);
			imagewgt.setColumn(col);
			//TODO zss 3.5 check
//			imagewgt.setRow2(row2);
//			imagewgt.setColumn2(col2);
			imagewgt.setZindex(zindex);
			imagewgt.setWidth(width + "px");
			imagewgt.setHeight(height + "px");
			imagewgt.setLeft(left - 1);
			imagewgt.setTop(top - 1);
			//ZSS-455 for adjust client location
			imagewgt.adjustLocation();
		}
	}
	
	private void setupChartWigetAnchor(SSheet sheet, ChartsWidget chartwgt, ViewAnchor anchor, int zindex) {
		if (anchor != null) {
			int row = anchor.getRowIndex();
			int col = anchor.getColumnIndex();
			//TODO zss 3.5 check client
//			int row2 = anchor.getRow2();
//			int col2 = anchor.getCol2();
			int height = anchor.getHeight();
			int width = anchor.getWidth();
			int left = anchor.getXOffset();
			int top = anchor.getYOffset();
			
			chartwgt.setRow(row);
			chartwgt.setColumn(col);
			//TODO zss 3.5 check client
//			chartwgt.setRow2(row2);
//			chartwgt.setColumn2(col2);
			chartwgt.setZindex(zindex);
			chartwgt.setHeight(height);
			chartwgt.setWidth(width);
			chartwgt.setLeft(left - 1);
			chartwgt.setTop(top - 1);
			//ZSS-455 for adjust client location
			chartwgt.adjustLocation();
		}
	}
	
	protected List<Widget> newImageWidgetGroup(SSheet sheet, SPicture picture,int zindex){
		final byte[] picdata = picture.getData();
		if (picdata == null) {
			return null;
		}
		ViewAnchor anchor = picture.getAnchor();
		List<String> panels = getEffectedPanels(sheet,anchor);
		
		Image pimage = null;
		List<Widget> widgets = new ArrayList<Widget>(panels.size());
		for(String p:panels){
			final Widget imagewgt = newImageWidget(sheet, picture, pimage, p, zindex);
			widgets.add(imagewgt);
			pimage = ((ImageWidget)imagewgt).getContent();//reuse image
		}
		return widgets;
	}
	
	protected Widget newImageWidget(SSheet sheet, SPicture picture, Image image,String panel,int zindex){
		final byte[] picdata = picture.getData();
		if (picdata == null && image==null) {
			return null;
		}
		ViewAnchor anchor = picture.getAnchor();
		final ImageWidget imagewgt = new ImageWidget(sheet, picture, panel);
		final Image img = image!=null?image:getAImage(sheet,picture);
		imagewgt.setContent(img);
			
		setupImageWigetAnchor(sheet, imagewgt, anchor, zindex);
			
		return imagewgt;
	}

	private AImage getAImage(SSheet sheet, SPicture picture) {
		try {
			String name = picture.getId() + '.' + picture.getFormat().getFileExtension();
			return new AImage(name, picture.getData());
		} catch (IOException e) {
			log.warning(e);			
			// ignore
		}
		return null;
	}

	protected List<Widget> newChartWidgetGroup(SSheet sheet, SChart chart, int zindex) throws IOException {
		
		List<String> panels = getEffectedPanels(sheet,chart.getAnchor());

		List<Widget> widgets = new ArrayList<Widget>(panels.size());
		for(String p:panels){
			final Widget chartwgt = newChartWidget(sheet, chart, p, zindex);
			widgets.add(chartwgt);
		}
		return widgets;
	}
	
	protected Widget newChartWidget(SSheet sheet, SChart chart, String panel,int zindex){
		ChartsWidget widget = new ChartsWidget(sheet, chart, panel, zindex);
		
		ViewAnchor anchor = chart.getAnchor();
			
		setupChartWigetAnchor(sheet, widget, anchor, zindex);
		return widget;
	}	

	@Override
	public void updateChartWidget(SSheet sheet, SChart chart) {
		final String key = sheet.getId();
		Map<String, List<Widget>> widgets = _widgetMap.get(key);
		if (widgets == null) {
			return;
		}
		
		final List<Widget> chartwgts = widgets.get(chart.getId());
		if (chartwgts == null) {
			return;
		}
		
		final ViewAnchor anchor = chart.getAnchor();
		
		int zindex = ZINDEX_START + widgets.size();
		List<String> panels = getEffectedPanels(sheet, anchor);
		List<String> newadd = new ArrayList<String>(panels);
		for(Widget w: new ArrayList<Widget>(chartwgts)){//avoid concurrent modif
			if(panels.contains(w.getPanel())){
				setupChartWigetAnchor(sheet, (ChartsWidget)w, anchor, w.getZindex());
				newadd.remove(w.getPanel());
			}else{
				chartwgts.remove(w);
				((SpreadsheetCtrl) _spreadsheet.getExtraCtrl()).removeWidget(w);
			}
		}
		
		//add new widget after picture update
		for(String p:newadd){
			Widget w = newChartWidget(sheet, chart, p, zindex);
			chartwgts.add(w);
			((SpreadsheetCtrl) _spreadsheet.getExtraCtrl()).addWidget(w);
		}
	}

	@Override
	public void deleteChartWidget(SSheet sheet, String chartId) {
		final String key = sheet.getId();
		Map<String, List<Widget>> widgets = _widgetMap.get(key);
		if (widgets == null) {
			return;
		}
		final List<Widget> chartwgts = widgets.remove(chartId);
		if (chartwgts == null) {
			return;
		}
		for(Widget w:chartwgts){
			((SpreadsheetCtrl) _spreadsheet.getExtraCtrl()).removeWidget(w);
		}
	}
}