
package jp.riken.brain.ni.samuraigraph.base;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JCheckBoxMenuItem;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;


/**
 *
 */
public class SGUtility implements SGIConstants
{

//	/**
//	 * 
//	 * @param args
//	 */
//	public static void main( String[] args )
//	{
//System.out.println("*** START ***");
//
//		GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
//
//		Font[] fonts = ge.getAllFonts();
//		for( int ii=0; ii<fonts.length; ii++ )
//		{
//			Font f = fonts[ii];
//			System.out.println( "-- "+f+" --");
//			System.out.println( "Name:\t" + f.getName() );
//			System.out.println( "FName:\t" + f.getFontName() );
//			System.out.println( "Family:\t" + f.getFamily() );
//			System.out.println();
//		}
//		System.out.println();
//
//
//		String names[] = ge.getAvailableFontFamilyNames( Locale.getDefault() );
////		for( int ii=0; ii<names.length; ii++ )
////		{
////			System.out.println(names[ii]);
////		}
////		System.out.println();
//
//
////		for( int ii=0; ii<names.length; ii++ )
////		{
////			System.out.println( names[ii] );
////			Font f1 = new Font( names[ii], Font.PLAIN, 1 );
////			Font f2 = new Font( f1.getName(), Font.PLAIN, 1 );
////			Font f3 = new Font( f1.getFamily(), Font.PLAIN, 1 );
////			Font f4 = new Font( f1.getFontName(), Font.PLAIN, 1 );
////			System.out.println(f1);
////			System.out.println(f2);
////			System.out.println(f3);
////			System.out.println(f4);
//////		System.out.println(f.getName());
//////		System.out.println(f.getFontName());
//////		System.out.println(f.getFamily());
//////		System.out.println(f);
////			System.out.println();
////		}
//
//
//System.out.println("*** END ***");
//	}



	/**
	 * Returns an array of available font family names.
	 */
	public static String[] getAvailableFontFamilyNames()
	{
//		Font[] fonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
//		String[] names = new String[fonts.length];
//		for( int ii=0; ii<names.length; ii++ )
//		{
//			names[ii] = fonts[ii].getFontName();
//		}
//		return names;

		return GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
	}


	/**
	 * Returns the canonical path.
	 * @param path - path name
	 */
	public static String getCanonicalPath( final String path )
	{
		File file = new File( path );
		String cPath = null;
		try
		{
			cPath = file.getCanonicalPath();
		}
		catch( IOException ex )
		{
			return null;
		}

		return cPath;
	}



	/**
	 * 
	 */
	public static Rectangle2D createUnion(
		final ArrayList rectList )
	{

		if( rectList==null )
		{
			throw new IllegalArgumentException("rectList==null");
		}

		Rectangle2D rectAll = null;
		for( int ii=0; ii<rectList.size(); ii++ )
		{
			Rectangle2D rect = (Rectangle2D)rectList.get(ii);
			if( rect==null )
			{
				throw new IllegalArgumentException("rect==null");
			}

			if( rectAll == null )
			{
				rectAll = rect;
				continue;
			}

			rectAll = rect.createUnion(rectAll);
		}

		return rectAll;		
	}



	/**
	 * 
	 * @param rect
	 * @param direction
	 * @return
	 */
	public static double getRectStart(
		final Rectangle2D rect,
		final boolean direction )
	{
		if( direction )
		{
			return rect.getX();
		}
		return rect.getY();
	}



	/**
	 * 
	 * @param rect
	 * @param direction
	 * @return
	 */
	public static double getRectSize(
		final Rectangle2D rect,
		final boolean direction )
	{
		if( direction )
		{
			return rect.getWidth();
		}
		return rect.getHeight();
	}



	/**
	 * 
	 * @param rect
	 * @param direction
	 * @return
	 */
	public static double getRectEnd(
		final Rectangle2D rect,
		final boolean direction )
	{
		return getRectStart(rect,direction) + getRectSize(rect,direction);
	}



	/**
	 * 
	 * @param rect
	 * @param value
	 * @param direction
	 * @return
	 */
	public static boolean setRectStart(
		final Rectangle2D rect,
		final double value,
		final boolean direction )
	{

		if( direction )
		{
			rect.setRect(
				value,
				rect.getY(),
				rect.getWidth(),
				rect.getHeight()
			);
		}
		else
		{
			rect.setRect(
				rect.getX(),
				value,
				rect.getWidth(),
				rect.getHeight()
			);
		}

		return true;
	}



	/**
	 * 
	 * @param rect
	 * @param value
	 * @param direction
	 * @return
	 */
	public static boolean setRectEnd(
		final Rectangle2D rect,
		final double value,
		final boolean direction )
	{

		if( direction )
		{
			rect.setRect(
				value - rect.getWidth(),
				rect.getY(),
				rect.getWidth(),
				rect.getHeight()
			);
		}
		else
		{
			rect.setRect(
				rect.getX(),
				value - rect.getHeight(),
				rect.getWidth(),
				rect.getHeight()
			);
		}

		return true;
	}



	/**
	 * 
	 * @param rect
	 * @param value
	 * @param direction
	 * @return
	 */
	public static boolean setRectSize(
		final Rectangle2D rect,
		final double value,
		final boolean direction )
	{

		if( direction )
		{
			rect.setRect(
				rect.getX(),
				rect.getY(),
				value,
				rect.getHeight()
			);
		}
		else
		{
			rect.setRect(
				rect.getX(),
				rect.getY(),
				rect.getWidth(),
				value
			);
		}

		return true;
	}



	/**
	 * 
	 * @param rect
	 * @param value
	 * @param direction
	 * @return
	 */
	public static boolean isRectContains(
		final Rectangle2D rect,
		final double value,
		final boolean direction )
	{
		return SGUtilityNumber.contains(
			getRectStart(rect,direction), getRectEnd(rect,direction), value );
	}



	/**
	 * 
	 * @param rect1
	 * @param rect2
	 * @param direction
	 * @return
	 */
	public static boolean isRectContains(
		final Rectangle2D rect1,
		final Rectangle2D rect2,
		final boolean direction )
	{
		return SGUtilityNumber.contains(
			getRectStart(rect1,direction), getRectEnd(rect1,direction),
			getRectStart(rect2,direction), getRectEnd(rect2,direction) );
	}



	/**
	 * 
	 * @param rect1
	 * @param rect2
	 * @param direction
	 * @return
	 */
	public static double getOverlapping(
		final Rectangle2D rect1,
		final Rectangle2D rect2,
		final boolean direction )
	{
		return SGUtilityNumber.getOverlap(
			getRectStart(rect1,direction), getRectEnd(rect1,direction),
			getRectStart(rect2,direction), getRectEnd(rect2,direction) );
	}



	/**
	 * 
	 */
	public static boolean showMessageDialog( Component parentComponent, Object message, String title, int messageType )
	{
		JOptionPane.showMessageDialog(
			parentComponent, message, title, messageType
		);
		return true;
	}


	/**
	 * 
	 * @param parentComponent
	 * @return
	 */
	public static boolean showErrorMessageDialog(
		Component parentComponent, String msg, String title )
	{
		Toolkit.getDefaultToolkit().beep();
		SGUtility.showMessageDialog(
			parentComponent,
			msg,
			title, 
			JOptionPane.ERROR_MESSAGE );
		return true;
	}


	/**
	 * 
	 * @param parentComponent
	 * @return
	 */
	public static boolean showIllegalInputErrorMessageDialog(
		Component parentComponent )
	{
		return showIllegalInputErrorMessageDialog(
			parentComponent,
			"The input value is illegal." );
	}


	/**
	 * 
	 * @param parentComponent
	 * @return
	 */
	public static boolean showIllegalInputErrorMessageDialog(
		Component parentComponent, String msg )
	{
		return showErrorMessageDialog(
			parentComponent,
			msg,
			"Illegal input" );
	}

	/**
	 * 
	 * @param parentComponent
	 * @return
	 */
	public static boolean showFileNotFoundMessageDialog( Component parentComponent )
	{
		SGUtility.showMessageDialog(
			parentComponent,
			"Failed to open the file denoted by a specified pathname.",
			"File not found", 
			JOptionPane.ERROR_MESSAGE );
		return true;
	}



	/**
	 * 
	 * @param wnd
	 * @return
	 */
	public static void setCenter( final Window wnd, final Window owner )
	{
		Dimension dim = owner.getSize();
		final int width = wnd.getWidth();
		final int height = wnd.getHeight();
		final int x = owner.getX() + ( dim.width - width )/2;
		final int y = owner.getY() + ( dim.height - height )/2;
		wnd.setLocation(x,y);
	}



	/**
	 * Copy the list of copied objects.
	 * @param list1 - a list which contains copiable objects to be copied
	 * @param list2 - a list to set the copied objects
	 * @return
	 */
	public static boolean copyObjects( final ArrayList list1, final ArrayList list2 )
	{
		if( list1==null | list2==null )
		{
			throw new IllegalArgumentException("list1==null | list2==null");
		}
		
		for( int ii=0; ii<list1.size(); ii++ )
		{
			Object obj = list1.get(ii);
			if( ( obj instanceof SGICopiable ) == false )
			{
				throw new IllegalArgumentException("Not copiable objects are included.");
			}
			SGICopiable cp = (SGICopiable)obj;
			list2.add( cp.copy() );
		}
		
		return true;
	}



	/**
	 * Add a menu command to the pop-up menu.
	 * @param p - a pop-up menu
	 * @param l - an action listener of the command
	 * @param cmd - a command to be added
	 * @return added menu item
	 */
	public static JMenuItem addItem(
		JPopupMenu p,
		ActionListener l,
		String cmd )
	{
		final JMenuItem item = new JMenuItem( cmd );
		p.add(item);
		item.addActionListener(l);
		return item;
	}



	/**
	 * Add a menu command with the check box to the pop-up menu.
	 * @param p - a pop-up menu
	 * @param l - an action listener of the command
	 * @param cmd - a command to be added
	 * @return added menu item
	 */
	public static JCheckBoxMenuItem addCheckBoxItem(
		JPopupMenu p,
		ActionListener l,
		String cmd )
	{
		final JCheckBoxMenuItem item = new JCheckBoxMenuItem( cmd );
		p.add(item);
		item.addActionListener(l);
		return item;
	}



	/**
	 * Identify the OS name.
	 * @param prefix - the prefix of OS name
	 * @return whether the OS name starts with the prefix
	 */
	public static boolean identifyOS( final String prefix )
	{
		if( prefix==null )
		{
			throw new IllegalArgumentException("prefix==null");
		}

		return OS_NAME.toLowerCase().startsWith(prefix);
	}



	/**
	 * 
	 * @return
	 */
	public static String getLookAndFeelID()
	{
		LookAndFeel laf = UIManager.getLookAndFeel();
		if(laf!=null)
		{
			return laf.getID();
		}
		return null;
	}



	/**
	 * Copy an array of a String array.
	 * @param src - an array to be copied
	 * @return a copy of an array
	 */
	public static String[] copyStringArray( String[] src )
	{
		final String[] dest = new String[src.length];
		System.arraycopy( src, 0, dest, 0, src.length );
		return dest;
	}


	/**
	 * Copy an array of a primitive integer array.
	 * @param src - an array to be copied
	 * @return a copy of an array
	 */
	public static int[] copyIntegerArray( int[] src )
	{
		final int[] dest = new int[src.length];
		System.arraycopy( src, 0, dest, 0, src.length );
		return dest;
	}


	/**
	 * Copy an array of a primitive double array.
	 * @param src - an array to be copied
	 * @return a copy of an array
	 */
	public static double[] copyDoubleArray( double[] src )
	{
		final double[] dest = new double[src.length];
		System.arraycopy( src, 0, dest, 0, src.length );
		return dest;
	}



	/**
	 * 
	 * @param obj
	 * @return
	 */
	public static boolean setValue(
		final SGTextField tf, final Object obj )
	{
		if( obj==null )
		{
			tf.setText(null);
		}
		else
		{
			String str = obj.toString();
			Double d = SGUtilityText.getDouble(str);
			if( d==null )
			{
				return false;
			}
			tf.setText( str );
		}

		return true;
	}



	/**
	 * 
	 * @param obj
	 * @return
	 */
	public static boolean setIntValue(
		final SGTextField tf, final Object obj )
	{
		if( obj==null )
		{
			tf.setText(null);
		}
		else
		{
			String str = obj.toString();
			Integer n = SGUtilityText.getInteger(str);
			if( n==null )
			{
				return false;
			}
			tf.setText( str );
		}

		return true;
	}



	/**
	 * 
	 * @param obj
	 * @return
	 */
	public static boolean setValue(
		final SGSpinner sp, final Object obj )
	{
		if( obj==null )
		{
			sp.setText("");
			return false;
		}

		String str = obj.toString();
		Double d = SGUtilityText.getDouble(str);
		if( d==null )
		{
			return false;
		}
		sp.setText( str );
		sp.setValue( d );

		try
		{
			sp.commitEditByDefault();
		}
		catch( ParseException e )
		{
		}

		return true;
	}


	/**
	 * 
	 * @param obj
	 * @return
	 */
	public static boolean setIntValue(
		final SGSpinner sp, final Object obj )
	{
		if( obj==null )
		{
			sp.setText("");
			return false;
		}

		String str = obj.toString();
		Integer n = SGUtilityText.getInteger(str);
		if( n==null )
		{
			return false;
		}
		sp.setText( str );
		sp.setValue( new Double( n.doubleValue() ) );

		try
		{
			sp.commitEditByDefault();
		}
		catch( ParseException e )
		{
		}

		return true;
	}


	/**
	 * Returns a location on a rectangle.
	 */
	public static int getMouseLocation(
		final Rectangle2D rect,
		final int x, final int y,
		final float radius )
	{
		final boolean bMinX = ( Math.abs(x-rect.getMinX()) < radius );
		final boolean bMidX = ( Math.abs(x-rect.getCenterX()) < radius );
		final boolean bMaxX = ( Math.abs(x-rect.getMaxX()) < radius );
		final boolean bMinY = ( Math.abs(y-rect.getMinY()) < radius );
		final boolean bMidY = ( Math.abs(y-rect.getCenterY()) < radius );
		final boolean bMaxY = ( Math.abs(y-rect.getMaxY()) < radius );


		int location = OTHER;

		if( bMinX )
		{
			// 左上
			if( bMinY )
			{
				location = NORTH_WEST;
			}
			// 左端中央
			else if( bMidY )
			{
				location = WEST;
			}
			// 左下
			else if( bMaxY )
			{
				location = SOUTH_WEST;
			}
		}
		else if( bMaxX )
		{
			// 右上
			if( bMinY )
			{
				location = NORTH_EAST;
			}
			// 右端中央
			else if( bMidY )
			{
				location = EAST;
			}
			// 右下
			else if( bMaxY )
			{
				location = SOUTH_EAST;
			}
		}
		else if( bMidX )
		{
			// 上端中央
			if( bMinY )
			{
				location = NORTH;
			}
			// 下端中央
			else if( bMaxY )
			{
				location = SOUTH;
			}
		}

		return location;
	}



	/**
	 * Resize the rectangle by mouse drag.
	 * @param rect
	 * @param pos
	 * @param e
	 * @param ml
	 */
	public static void resizeRectangle(
		final Rectangle2D rect,	// rectangle
		final Point pos,			// mouse position
		final MouseEvent e,		// mouse event on dragging
		final int ml				// mouse location
		)
	{

		// is Shift-key is pressed		
		final boolean onShift = ( ( e.getModifiers() & InputEvent.SHIFT_MASK )!=0 );


		final int diffX = e.getX() - pos.x;
		final int diffY = e.getY() - pos.y;
		
		final float xOld = (float)rect.getX();
		final float yOld = (float)rect.getY();
		final float wOld = (float)rect.getWidth();
		final float hOld = (float)rect.getHeight();


		final float sizeOldX = wOld;
		final float sizeOldY = hOld;

		float sizeNewX = 0.0f;
		float sizeNewY = 0.0f;


		// variables to be updated
		float x = xOld;
		float y = yOld;
		float w = wOld;
		float h = hOld;


		if( ml == NORTH )
		{
//System.out.println("NORTH");
			pos.setLocation(
				pos.getX(),
				pos.getY() + diffY
			);
			sizeNewY = sizeOldY - diffY;
			y = yOld + sizeOldY - sizeNewY;
			h = sizeNewY;
		}
		else if( ml == SOUTH )
		{
//System.out.println("SOUTH");
			pos.setLocation(
				pos.getX(),
				pos.getY() + diffY
			);
			sizeNewY = sizeOldY + diffY;
			h = sizeNewY;
		}
		else if( ml == WEST )
		{
//System.out.println("WEST");
			pos.setLocation(
				pos.getX() + diffX,
				pos.getY() + diffY
			);
			sizeNewX = sizeOldX - diffX;
			x = xOld + sizeOldX - sizeNewX;
			w = sizeNewX;
		}
		else if( ml == EAST )
		{
//System.out.println("EAST");
			pos.setLocation(
				pos.getX() + diffX,
				pos.getY()
			);
			sizeNewX = sizeOldX + diffX;
			w = sizeNewX;
		}
		else if( ml == NORTH_WEST )
		{
			pos.setLocation(
				pos.getX() + diffX,
				pos.getY() + diffY
			);
			sizeNewX = sizeOldX - diffX;

			if( onShift )
			{
				sizeNewY = sizeNewX*( sizeOldY/sizeOldX );
			}
			else
			{
				sizeNewY = sizeOldY - diffY;
			}
			x = xOld + sizeOldX - sizeNewX;
			y = yOld + sizeOldY - sizeNewY;
			w = sizeNewX;
			h = sizeNewY;
//System.out.println(sizeOldY+"  "+sizeNewY);
		}
		else if( ml == NORTH_EAST )
		{
			if( !onShift )
			{
				pos.setLocation(
					pos.getX() + diffX,
					pos.getY() + diffY
				);

				sizeNewX = sizeOldX + diffX;
				sizeNewY = sizeOldY - diffY;

				y = yOld + sizeOldY - sizeNewY;
				w = sizeNewX;
				h = sizeNewY;
				
			}
			else
			{
				pos.setLocation(
					pos.getX(),
					pos.getY() + diffY
				);

				sizeNewY = sizeOldY - diffY;
				sizeNewX = sizeNewY*( sizeOldX/sizeOldY );

				y = yOld + sizeOldY - sizeNewY;
				w = sizeNewX;
				h = sizeNewY;
			}
		}
		else if( ml == SOUTH_WEST )
		{
			if( !onShift )
			{
				pos.setLocation(
					pos.getX() + diffX,
					pos.getY() + diffY
				);

				sizeNewX = sizeOldX - diffX;
				sizeNewY = sizeOldY + diffY;

				x = xOld + sizeOldX - sizeNewX;
				w = sizeNewX;
				h = sizeNewY;
			}
			else
			{
				pos.setLocation(
					pos.getX() + diffX,
					pos.getY() + diffY
				);

				sizeNewX = sizeOldX - diffX;
				sizeNewY = sizeNewX*( sizeOldY/sizeOldX );

				x = xOld + sizeOldX - sizeNewX;
				w = sizeNewX;
				h = sizeNewY;
			}
		}
		else if( ml == SOUTH_EAST )
		{
			if( !onShift )
			{
				pos.setLocation(
					pos.getX() + diffX,
					pos.getY() + diffY
				);

				sizeNewX = sizeOldX + diffX;
				sizeNewY = sizeOldY + diffY;

				w = sizeNewX;
				h = sizeNewY;
			}
			else
			{
				pos.setLocation(
					pos.getX() + diffX,
					pos.getY()
				);

				sizeNewX = sizeOldX + diffX;
				sizeNewY = sizeNewX*( sizeOldY/sizeOldX );

				w = sizeNewX;
				h = sizeNewY;
			}
		}


		// update the rectangle
		rect.setRect(x,y,w,h);

//System.out.println(rect);
	}



	/**
	 * Move a object to the head of the list.
	 * @param obj - an objct to be moved
	 * @param list - the list which contains the object
	 * @return
	 */
	public static boolean moveObjectToFront(
		final Object obj, final ArrayList list )
	{
		return moveObject( obj, list, list.size()-1 );
	}



	/**
	 * Move a object to the tail of the list.
	 * @param obj - an objct to be moved
	 * @param list - the list which contains the object
	 * @return
	 */
	public static boolean moveObjectToBack(
		final Object obj, final ArrayList list )
	{
		return moveObject( obj, list, 0 );
	}


	/**
	 * Move the object to the location of given index.
	 * @param obj - object to be moved
	 * @param list - the list which contains the object
	 * @param index - array index
	 * @return
	 */
	public static boolean moveObject(
		final Object obj, final List list, final int index )
	{
		// check
		if( list==null )
		{
			throw new IllegalArgumentException("list==null");
		}

		if( index < 0 | index >= list.size() )
		{
			throw new IllegalArgumentException("index < 0 | index >= list.size()");
		}

		if( list.contains(obj)==false )
		{
			return false;
		}

		for( int ii=0; ii<list.size(); ii++ )
		{
			if( obj.equals( list.get(ii) ) )
			{
				list.remove(ii);
				break;
			}
		}
		list.add( index, obj );
		
		return true;
	}



	/**
	 * 
	 * @param list - a list to be modified
	 * @param listVisible - a list of visible objects
	 * @return
	 */
	public static boolean setVisibleList(
		final List list, final List listVisible )
	{
		ArrayList listInvisible = new ArrayList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			SGIVisible el
				= (SGIVisible)list.get(ii);
			final boolean b = listVisible.contains(el);
			el.setVisible(b);
			if(!b)
			{
				listInvisible.add( el );
			}
		}

		list.clear();
		for( int ii=0; ii<listVisible.size(); ii++ )
		{
			list.add( listVisible.get(ii) );
		}
		for( int ii=0; ii<listInvisible.size(); ii++ )
		{
			list.add( listInvisible.get(ii) );
		}

		return true;
	}

}


