/*  
 * Copyright 2004-2009 unitarou <boss@unitarou.org>. 
 * All rights reserved.
 * 
 * This program and the accompanying materials are made available under the terms of 
 * the Common Public License v1.0 which accompanies this distribution, 
 * and is available at http://opensource.org/licenses/cpl.php
 * 
 * Contributors:
 *     unitarou - initial API and implementation
 */
package org.unitarou.yukinoshita.view.jface.gi;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;

import org.eclipse.jface.util.Geometry;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.ScrollBar;

import org.unitarou.lang.ArgumentChecker;
import org.unitarou.lang.Objects;
import org.unitarou.sgf.Node;
import org.unitarou.sgf.PropertyType;
import org.unitarou.sgf.type.GameType;
import org.unitarou.swt.Swts;
import org.unitarou.yukinoshita.events.EventBroker;
import org.unitarou.yukinoshita.events.EventBrokerMonitor;
import org.unitarou.yukinoshita.model.NodeList;
import org.unitarou.yukinoshita.model.NodeView;
import org.unitarou.yukinoshita.model.RgtProxy;
import org.unitarou.yukinoshita.view.jface.YukinoshitaPanel;
import org.unitarou.yukinoshita.view.monitor.GameInfoNodeMonitor;
import org.unitarou.yukinoshita.view.monitor.GameMonitor;
import org.unitarou.yukinoshita.view.monitor.MouseWheelMonitor;

/**
 * ûâléÌGameInfovpeBð\¦·éplÅ·B
 * 
 * @author UNITAROU &lt;boss@unitarou.org&gt;
 */
public class GameInfoSheet implements YukinoshitaPanel, EventBrokerMonitor {
	
    /**
     * ±Ìplð\¦³¹é{@link GameType}Å·B
     * ¢wèÌêÍ{@link GameType#GAME}ÆÝÈµÜ·B
     */
    private GameType gameType_;
    
    /**
     * ±ÌV[gÌ[gwidgetÅA{@link #frame_}ÌeÅ·B 
     */
    private ScrolledComposite scFrame_;
    
    /**
     * ÀÛÉxði[·éíÅ·B
     * {@link #createContents(Composite)}ÅÍScrolledCompositeð
     * Ü¸Í¶ßÉì¬µÄ¢éÌÅA
     * {@link #close()}ÍeÌScrolledComposite©çdispose·éKvª èÜ·B
     */
    private Composite frame_;
    
    
    /** ^Í{@link GiLine} */
    private final List<GiLine> giLines_;
    
    private final Adapter adapter_;
    
    private EventBroker eventBroker_;

    /**
	 * 
	 */
	public GameInfoSheet() {
		super();
		gameType_ = GameType.GAME;
		scFrame_ = null;
		frame_ = null;
		giLines_ = new ArrayList<GiLine>();
		adapter_ = new Adapter();
		eventBroker_ = EventBroker.NULL_BROKER;
	}
	
    /* (non-Javadoc)
     * @see org.unitarou.lang.Adaptable#getAdapter(java.lang.Class)
     */
    public Object getAdapter(Class<?> adapter) {
        if (adapter == null) {
            return null;
        }
        if (adapter.isAssignableFrom(adapter_.getClass())) {
            return adapter_;
        }
        return null;
    }


    /* (non-Javadoc)
     * @see org.unitarou.yukinoshita.view.swt.ControlCreatable#createControl(org.eclipse.swt.widgets.Composite)
     */
    public Control createContents(Composite parent) {
        ArgumentChecker.throwIfNull(parent);
        
        scFrame_ = new ScrolledComposite(parent, SWT.V_SCROLL);
        scFrame_.setExpandHorizontal(true);
        scFrame_.setExpandVertical(true);
        frame_ = new Composite(scFrame_, SWT.NONE);

        GridLayout gridLayout = new GridLayout(1, false);
        gridLayout.horizontalSpacing = 0;
        gridLayout.verticalSpacing = 4;
        gridLayout.marginHeight = 2;
        gridLayout.marginWidth = 2;
        frame_.setLayout(gridLayout);
        for (Iterator<GiLine> ip = giLines_.iterator(); ip.hasNext(); ) {
            GiLine giLine = ip.next();
            Control control = giLine.createContents(frame_);
            GridData gridData = new GridData(SWT.FILL, SWT.FILL, false, false);
            control.setLayoutData(gridData);
        }
		scFrame_.addControlListener(new ControlAdapter() {
			@Override
			public void controlResized(ControlEvent e) {
				resized();
			}
		});
        scFrame_.setContent(frame_);
        return scFrame_;
    }

    private void resized() {
		Point size = Geometry.getSize(scFrame_.getClientArea());
		size = frame_.computeSize(size.x, SWT.DEFAULT);
		scFrame_.setMinSize(size);
		for (GiLine giLine : giLines_) {
			GridData gridData = (GridData)giLine.getControl().getLayoutData();
			gridData.widthHint = size.x;
		}
    }
    

    /* 
     * @see java.io.Closeable#close()
     */
    public void close() {
    	// »ósv
    }

    
    public Control getControl() {
        return scFrame_;
    }
    
    /**
     * @return íÊðÔµÜ·B
     */
    public GameType getGameType() {
        return gameType_;
    }

    /**
     * @param type
     */
    public void setGameTypeId(String type) {
        ArgumentChecker.throwIfNull(type);
        gameType_ = GameType.parseQuietly(type, GameType.GAME);
    }

    /**
     * @param giLine
     */
    public void addLine(GiLine giLine) {
        ArgumentChecker.throwIfNull(giLine);
        giLines_.add(giLine);
    }


    /**
     * àÌxðpropertiesÅXVµÜ·B
     * @param properties
     * @throws org.unitarou.lang.NullArgumentException øªnullÌê
     */
    public void setLabel(Properties properties) {
        ArgumentChecker.throwIfNull(properties);
        for (GiLine giLine : giLines_) {
            giLine.setLabel(properties);
        }
    }


    /* (non-Javadoc)
     * @see org.unitarou.yukinoshita.events.EventBrokerMonitor#setEventBroker(org.unitarou.yukinoshita.events.EventBroker)
     */
    public void setEventBroker(EventBroker eventBroker) {
        ArgumentChecker.throwIfNull(eventBroker);
        eventBroker_.removeView(this);
        eventBroker_ = eventBroker;
        eventBroker_.addView(this);
    }
    
    /**
     * øÅV[gÌ\¦ðXVµÜ·B
     * @param node
     * @throws org.unitarou.lang.NullArgumentException 
     *          ø(node)É<code>null</code>ª éêB
     */
    public void update(Node node) {
    	ArgumentChecker.throwIfNull(node);
        for (Iterator<GiLine> ip = giLines_.iterator(); ip.hasNext(); ) {
            GiLine giLine = ip.next();
            giLine.currentChanged(node);
        }
        frame_.pack();
		resized();
    }
    
    private class Adapter 	implements 
    		MouseWheelMonitor, GameMonitor, GameInfoNodeMonitor 
    {
		/* 
		 * @see org.unitarou.yukinoshita.view.monitor.MouseWheelMonitor#mouseWheelRolled(org.eclipse.swt.graphics.Point, int)
		 */
		public boolean mouseWheelRolled(Point pointInDisplay, int delta) {
			// ©ªÌq(GiLines)ÌãÉ}EXJ[\ª é©²×éB
			boolean isMouseOn = Swts.isMouseOn(frame_);
			for (GiLine giLine : giLines_) {
				isMouseOn |= Swts.isMouseOn(giLine.getControl());
			}
			if (!isMouseOn) {
				return false;
			}
			ScrollBar bar = scFrame_.getVerticalBar();
			if (bar == null) {
				return true;
			}
			int index = bar.getSelection();
			index = Objects.preventOverRange(
					index + (delta * 10), bar.getMinimum(), bar.getMaximum());
			if (bar.getSelection() != index) {
				bar.setSelection(index);
				scFrame_.setOrigin(0, index);
			}
			return true;
		}

		/* (non-Javadoc)
		 * @see org.unitarou.yukinoshita.view.monitor.GameMonitor#update(org.unitarou.yukinoshita.model.RgtProxy)
		 */
		public void update(RgtProxy rgtProxy) {
            ArgumentChecker.throwIfNull(rgtProxy);
            
            if (scFrame_ == null) {
            	return;
            }
        	NodeList nodeList = rgtProxy.getNodeList();
            for(int index = rgtProxy.getCurrentNodeIndex(); 0 <= index; --index) { 
            	NodeView nodeView = nodeList.getNodeView(index);
            	if (nodeView.containsPropertyType(PropertyType.GAME_INFO)) {
            		gameInfoChanged(nodeView);
            		Point point = scFrame_.getOrigin();
            		if (point.x != 0 || point.y != 0) {
                		scFrame_.setOrigin(0, 0);
            		}
            		return;
            	}
            }
            // GAME_INFOª³¢êÍAæªÌNodeðnµÄ
            // ¡ÜÅÌóÔðSÄNA[·éB
            gameInfoChanged(nodeList.getNodeView(0));
		}
		
        /* (non-Javadoc)
         * @see org.unitarou.yukinoshita.view.NodeMonitor#update(org.unitarou.yukinoshita.model.NodeView)
         */
        public void gameInfoChanged(NodeView nodeView) {
            ArgumentChecker.throwIfNull(nodeView);
            if (scFrame_ == null) {
            	return;
            }
            GameInfoSheet.this.update(nodeView.copyNode());
        }
    }
}
