package jp.hasc.hasctool.ui.views;

import java.util.TreeSet;

import jp.hasc.hasctool.core.runtime.RuntimeContext;
import jp.hasc.hasctool.core.runtime.hierarchize.remote.XERequestSender;
import jp.hasc.hasctool.core.runtime.hierarchize.remote.XERequestSender.JobStatus;
import jp.hasc.hasctool.core.util.CoreUtil;

import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.part.ViewPart;

/**
 * @author iwasaki
 */
public class XERequestSenderView extends ViewPart {
	/** logger for this class */
	private final static org.apache.commons.logging.Log LOG = org.apache.commons.logging.LogFactory
			.getLog(XERequestSenderView.class);
	public static final String ID = "jp.hasc.hasctool.ui.views.XERequestSenderView"; //$NON-NLS-1$
	
	private XERequestSender sender_ = null;
	private ToolBar toolBar_;

	private Composite parent_;
	private Canvas canvas_;
	private Image canvasImage_=null;
	
	private TreeSet<Integer> updateHint_=new TreeSet<Integer>();
	
	private int unitWidth_ = 16;
	public static final int INDEX_DRAW_ALL = -1;
	
	@Override
	public void createPartControl(Composite parent) {
		parent_=parent;
		parent.setLayout(new FormLayout());
		//
		toolBar_ = new ToolBar(parent, SWT.VERTICAL);
		{
			FormData fd = new FormData();
			fd.top=new FormAttachment(0, 1);
			fd.right=new FormAttachment(100, -1);
			fd.bottom=new FormAttachment(100, -1);
			toolBar_.setLayoutData(fd);
		}
		{
			ToolItem ti=new ToolItem(toolBar_, SWT.NONE);
			ti.setText("z-"); //$NON-NLS-1$
			ti.setToolTipText(Messages.XERequestSenderView_ZoomOut);
			ti.addSelectionListener(new SelectionAdapter() {
				@Override
				public void widgetSelected(SelectionEvent e) {
					unitWidth_=Math.max(3,unitWidth_-1);
					requestUpdate(INDEX_DRAW_ALL);
				}
			});
		}
		{
			ToolItem ti=new ToolItem(toolBar_, SWT.NONE);
			ti.setText("z+"); //$NON-NLS-1$
			ti.setToolTipText(Messages.XERequestSenderView_ZoomIn);
			ti.addSelectionListener(new SelectionAdapter() {
				@Override
				public void widgetSelected(SelectionEvent e) {
					unitWidth_=unitWidth_+1;
					requestUpdate(INDEX_DRAW_ALL);
				}
			});
		}
		{
			ToolItem ti=new ToolItem(toolBar_, SWT.NONE);
			ti.setText("rs"); //$NON-NLS-1$
			ti.setToolTipText(Messages.XERequestSenderView_EditReceivers);
			ti.addSelectionListener(new SelectionAdapter() {
				@Override
				public void widgetSelected(SelectionEvent e) {
					if (sender_==null) return;
					InputDialog dlg=new InputDialog(toolBar_.getShell(), Messages.XERequestSenderView_EditReceivers, "Receivers:", sender_.getReceivers(), null); //$NON-NLS-1$
					if (dlg.open()==InputDialog.OK) {
						if (sender_==null) return;
						sender_.setReceivers(dlg.getValue());
						sender_.updateReceivers();
					}
				}
			});
		}
		//
		canvas_ = new Canvas(parent, SWT.BORDER | SWT.NO_BACKGROUND);
		{
			FormData fd = new FormData();
			fd.left=new FormAttachment(0, 1);
			fd.top=new FormAttachment(0, 1);
			fd.right=new FormAttachment(toolBar_, -1);
			fd.bottom=new FormAttachment(100, -1);
			canvas_.setLayoutData(fd);
			canvas_.addPaintListener(new PaintListener() {
				@Override
				public void paintControl(PaintEvent e) {
					redrawRequested_=false;
					// uses buffered image
					Point s = canvas_.getSize();
					if (s.x<=0 || s.y<=0) return;
					if (canvasImage_==null || canvasImage_.getBounds().width!=s.x || canvasImage_.getBounds().height!=s.y) {
						synchronized (updateHint_) {
							updateHint_.clear();
							updateHint_.add(INDEX_DRAW_ALL);
						}
						if (canvasImage_!=null) canvasImage_.dispose();
						canvasImage_=new Image(canvas_.getDisplay(), s.x, s.y);
					}
				    GC gc = new GC(canvasImage_);
					onCanvasPaint(gc);
					gc.dispose();
					e.gc.drawImage(canvasImage_, 0, 0);
				}
			});
		}
	}
	
	public static XERequestSenderView openView(RuntimeContext context, XERequestSender sender) {
		try {
			IWorkbenchWindow window=(IWorkbenchWindow) context.getObjectFromRepogitory(IWorkbenchWindow.class);
			XERequestSenderView view = (XERequestSenderView) window.getActivePage().showView(XERequestSenderView.ID
					,null, IWorkbenchPage.VIEW_VISIBLE);
			view.setSender(sender);
			return view;
		} catch (Exception ex) {
			CoreUtil.throwAsRuntimeException(ex);
			return null;
		}
	}

	private int getCanvasWidth() {
		return canvas_.getSize().x-canvas_.getBorderWidth()*2;
	}
	private int getCanvasHeight() {
		return canvas_.getSize().y-canvas_.getBorderWidth()*2;
	}
	
	private Color[] boxColors_=new Color[]{new Color(null,255,255,128), new Color(null,200,255,255), new Color(null,255,200,255),
			new Color(null,255,200,128), new Color(null,200,200,255), new Color(null,200,255,128), new Color(null,200,200,200) };
	
	private Color[] statusColors_=new Color[]{new Color(null,128,128,128), new Color(null,255,255,255), new Color(null,0,0,0) };

	public final static Color COLOR_WHITE = new Color(null,255,255,255);
	
	private void onCanvasPaint(GC gc) {
		int cw=getCanvasWidth(), ch=getCanvasHeight();
		int uw=Math.max(1,(cw-2)/unitWidth_);
		
		if (sender_==null) return;
		JobStatus[] jobStatuses = sender_.getJobStatuses();
		if (jobStatuses!=null) {
			synchronized (jobStatuses) {
				boolean drawAll=false;
				synchronized (updateHint_) {
					if (/*updateHint_.isEmpty() || */updateHint_.contains(INDEX_DRAW_ALL)){
						drawAll=true;
					}else{
						// paint partially
						for(int i : updateHint_) {
							paintSub(jobStatuses, gc, uw, i);
						}
					}
					updateHint_.clear();
				}
				if (drawAll) {
					// bg
					gc.setBackground(COLOR_WHITE);
					gc.fillRectangle(0,0,cw,ch);
					// paint All
					for(int i=0,inum=jobStatuses.length;i<inum;++i) {
						paintSub(jobStatuses, gc, uw, i);
					}
					LOG.debug("drawAll"); //$NON-NLS-1$
				}
			}
		}
	}

	private void paintSub(JobStatus[] jobStatuses, GC gc, int uw, int i) {
		JobStatus js = jobStatuses[i];
		if (js.nodeIndex>=0) {
			gc.setBackground(boxColors_[js.nodeIndex%boxColors_.length]);
		}else{
			gc.setBackground(COLOR_WHITE);
		}
		int x=unitWidth_*(i%uw),y=unitWidth_*(i/uw);
		gc.fillRectangle(x+1, y+1, unitWidth_-2, unitWidth_-2);
		gc.setForeground(statusColors_[js.status]);
		gc.drawRectangle(x+1, y+1, unitWidth_-2, unitWidth_-2);
	}
	
	@Override
	public void setFocus() {
	}

	public XERequestSender getSender() {
		return sender_;
	}

	public void setSender(XERequestSender sender) {
		sender_ = sender;
	}
	
	public void onUpdate(final int index) {
		requestUpdate(index);
	}
	
	private boolean redrawRequested_=false;

	protected void requestUpdate(int index) {
		synchronized (updateHint_) {
			if (index==INDEX_DRAW_ALL) updateHint_.clear();
			updateHint_.add(index);
		}
		if (!redrawRequested_) {
			redrawRequested_=true;
			parent_.getDisplay().asyncExec(new Runnable() {
				@Override
				public void run() {
					canvas_.redraw();
				}
			});
		}
	}
}
