/**
 * This package Contains the drawable base class, font and image classes as resources, and UI facilites.
 */

/*
 * 
 * Copyright (c) 2006-2008 MiniMe. Code released under The MIT/X Window System
 * License. Full license text can be found in license.txt
 */
package minime;

import java.util.Vector;
/** 
 * the layout box sizer to manage the elements layout in a rectangle
 * @author chenhongbo
 */

public  class BoxSizer {
     /**
      * used for store the drawable element's flag
      * @author chenghongbo
      *
      */
	public class flagStruct{
		private int borderFlag=0;
		private int alignFlag=0;
		private int borderSize=0;
		public int getAlignFlag() {
			return alignFlag;
		}
		public void setAlignFlag(int alignFlag) {
			this.alignFlag = alignFlag;
		}
		public int getBorderFlag() {
			return borderFlag;
		}
		public void setBorderFlag(int borderFlag) {
			this.borderFlag = borderFlag;
		}
		public int getBorderSize() {
			return borderSize;
		}
		public void setBorderSize(int borderSize) {
			this.borderSize = borderSize;
		}  
		
		/**  @return :
		 * 		  0:all the flags are not set
		 *        1:only border flag
		 * 		  2:only align flag
		 * 		  3:both border flag and align flag
		 */
		public int flagType(){
			if(borderFlag!=0&&alignFlag==0)
				return 1;
			else if(borderFlag==0&&alignFlag!=0)
				return 2;
			else if(borderFlag!=0&&alignFlag!=0)
				return 3;
			else 
				return 0;
		}
	}
	//for the border flags
	public final static int BORDER_ALL=0x0001;
	
	//for align flags
	public final static int ALIGN_LEFT=0x0001<<1;
	public final static int ALIGN_CENTER=0x0001<<2;
	public final static int ALIGN_RIGHT=0x0001<<3;
	public final static int ALIGN_TOP=0x0001<<4;
	public final static int ALIGN_BOTTOM=0x0001<<5;
	//for sizer flags
	public final static int SIZER_BORDER=0x0001<<6;
	public final static int SIZER_LEFT=0x0001<<7;
	public final static int SIZER_CENTER=0x0001<<8;
	public final static int SIZER_RIGHT=0x0001<<9;
	
	//for sizer type flags
	public static int HORIZONTAL=0x0001<<10;
	public static int VERTICAL=0x0001<<11;
	
	
	//indicate the type of this box sizer
	private int orientation;
   //to store the size information for all the drawable elements
	private Vector elements;
	private Vector flags;
	
	//record the sizer height and width 
	//(the maximum  width and height of the element in the sizer) 
	private int width=0;
	private int height=0;
	
	 /**
	 *  the box sizer constructor
	 *  @param 
	 *    type: the sizer orientation type
	 */
	public  BoxSizer(int type){
		this.orientation=type;
		elements=new Vector();
		flags=new Vector();
	}
	
	/**
	 * add drawable element with default setting
	 * @param
	 *   dw: the  element added into the sizer
	 */
	public void add(Drawable dw)
	{   
		if(height<dw.getHeight())
			height=dw.getHeight();
		if(width<dw.getWidth())
			width=dw.getWidth();
		elements.addElement(dw);
		flagStruct fs=new flagStruct();
		flags.addElement(fs);
	}
	
	
	/**
	 * add drawable element attached with align flags
	 * @param
	 *      dw: the  element added into the sizer
	 *      alignFlag: the align flag ,its value should be one of the followings:
	 *                 ALIGN_LEFT
	 *                 ALIGN_RIGHT
	 *                 ALIGN_CENTER
	 *                 ALIGN_TOP
	 *                 ALIGN_BOTTOM
	 */
	public void add(Drawable dw,int alignFlag)
	{    
		if(height<dw.getHeight())
			height=dw.getHeight();
		if(width<dw.getWidth())
			width=dw.getWidth();

		elements.addElement(dw);
		flagStruct fs=new flagStruct();
		fs.setAlignFlag(alignFlag);
		flags.addElement(fs);
	}
    /**
    *  add drawable element attached with  border flags
    *  The border means that the element will be surrounded by the  border area which will 
    *  be filled with blank space
    *  @param
    *     dw: the  element added into the sizer
    *     borderFlag: the border flag BORDER_ALL
    *     borderSize: the border size
    */
	
	public void add(Drawable dw,int borderFlag,int borderSize){
		if(height<dw.getHeight()+borderSize*2)
			height=dw.getHeight()+borderSize*2;
		if(width<dw.getWidth()+borderSize*2)
			width=dw.getWidth()+borderSize*2;
		elements.addElement(dw);
		flagStruct fs=new flagStruct();
		fs.setBorderFlag(borderFlag);
		fs.setBorderSize(borderSize);
		flags.addElement(fs);
	}
	
	
	/**
	 * add drawable element attached with align flags and border flags
	 *  @param:
	 *     dw: the  element added into the sizer
	 *     alignFlag: the align flag ,its value should be one of the followings:
	 *                 ALIGN_LEFT
	 *                 ALIGN_RIGHT
	 *                 ALIGN_CENTER
	 *                 ALIGN_TOP
	 *                 ALIGN_BOTTOM
     *     borderFlag: the border flag BORDER_ALL
     *     borderSize: the border size
	*/
	public void add (Drawable dw,int borderFlag,int alignFlag,int borderSize){
		if(height<dw.getHeight()+borderSize*2)
			height=dw.getHeight()+borderSize*2;
		if(width<dw.getWidth()+borderSize*2)
			width=dw.getWidth()+borderSize*2;
		elements.addElement(dw);
		flagStruct fs=new flagStruct();
		fs.setBorderFlag(borderFlag);
		fs.setBorderSize(borderSize);
		fs.setAlignFlag(alignFlag);
		flags.addElement(fs);
		
	}
	
	/**
	 * add  a sizer with default setting
	 * 
	 */
	public void add(BoxSizer bs){
		elements.addElement(bs);
		flagStruct fs=new flagStruct();
		flags.addElement(fs);
	}
	
	/**
	 * add  a sizer attached with align flags 
	 */
	public void add(BoxSizer bs,int sizerAlignFlag){
		elements.addElement(bs);
		flagStruct fs=new flagStruct();
		fs.setAlignFlag(sizerAlignFlag);
		flags.addElement(fs);
	}
	
	/**
	 * add  a sizer attached with  border flags
	 */
	public void add(BoxSizer bs,int sizerBorderFlag,int sizerBorderSize){
		elements.addElement(bs);
		flagStruct fs=new flagStruct();
		fs.setBorderFlag(sizerBorderFlag);
		fs.setBorderSize(sizerBorderSize);
		flags.addElement(fs);
		
	}
	
	/**
	 * add  a sizer attached with align flags and border flags
	 */
	public void add(BoxSizer bs,int sizerBorderFlag,int sizerAlignFlag,int sizerBorderSize){
		elements.addElement(bs);
		flagStruct fs=new flagStruct();
		fs.setBorderFlag(sizerBorderFlag);
		fs.setBorderSize(sizerBorderSize);
		fs.setAlignFlag(sizerAlignFlag);
		flags.addElement(fs);	
		
	}
	
	
	public int getHeight() {
		return height;
	}

	public void setHeight(int height) {
		this.height = height;
	}

	public int getWidth() {
		return width;
	}

	public void setWidth(int width) {
		this.width = width;
	}

	/** calculate the drawable elements' sizes and positions 
	 * @param re: 
	 *      before return: the initial drawable area
	 *      after  return: the new drawable area 
	 * 
	 */
	public void  fit(Rectangle re){
		
		for(int i=0;i<elements.size();i++)
		{   
			//the element is a drawable element
			if(elements.elementAt(i) instanceof Drawable)
			{    
				Drawable dw=(Drawable)elements.elementAt(i);
//				System.out.println("height:"+dw.getHeight());
//				System.out.println("width:"+dw.getWidth());
				//the orientation is horizontal
				if(orientation==HORIZONTAL)
				{   
					flagStruct fs=(flagStruct)flags.elementAt(i);
					switch(fs.flagType()){
					   case 0:
						   //the element is aligned center vertically and aligned left horizontally by default 
						   dw.setPosition(re.x,re.y+(re.h-dw.getHeight())/2);
						   re.x+=dw.getWidth();
						   re.w-=dw.getWidth();
						   break;
					 //only border flag
					   case 1:
						    //only BORDER_ALL flag
						    dw.setPosition(re.x+fs.getBorderSize(),re.y+(re.h-dw.getHeight())/2);
						    re.x+=fs.getBorderSize()*2+dw.getWidth();
						    re.w-=fs.getBorderSize()*2+dw.getWidth();
						    break;
					   //only align flag
					   case 2:
					       switch(fs.getAlignFlag()){
					          case ALIGN_LEFT:
					        	  dw.setPosition(re.x, re.y+(re.h-dw.getHeight())/2);
					        	  re.x+=dw.getWidth();
					        	  re.w-=dw.getWidth();
					        	  break;
					          case ALIGN_CENTER:
					        	  dw.setPosition(re.x+(re.w-dw.getWidth())/2, re.y+(re.h-dw.getHeight())/2);
					        	  re.w-=dw.getWidth();
					        	  break;
					          case ALIGN_RIGHT:
					        	  dw.setPosition(re.x+(re.w-dw.getWidth()),re.y+(re.h-dw.getHeight())/2);
					        	  re.w-=dw.getWidth();
					        	  break;
					          case ALIGN_TOP:
					        	  dw.setPosition(re.x,re.y);
					        	  re.x+=dw.getWidth();
					        	  re.w-=dw.getWidth();
					        	  break;
					          case ALIGN_BOTTOM:
					        	  dw.setPosition(re.x,re.y+(re.h-dw.getHeight()));
					        	  re.x+=dw.getWidth();
					        	  re.w-=dw.getWidth();
					        	  break;
					       }
						   break;
					   //both have align flag and border flag
					   case 3:
						   switch(fs.getAlignFlag()){
					         case ALIGN_LEFT:
					        	  dw.setPosition(re.x+fs.getBorderSize(),re.y+(re.h-dw.getHeight())/2);
					        	  re.x+=fs.getBorderSize()*2+dw.getWidth();
					        	  re.w-=fs.getBorderSize()*2+dw.getWidth();
					        	  break;
					          case ALIGN_CENTER:
					        	  dw.setPosition(re.x+(re.w-dw.getWidth())/2, re.y+(re.h-dw.getHeight())/2);
					        	  re.w-=fs.getBorderSize()*2+dw.getWidth();
					        	  break;
					          case ALIGN_RIGHT:
					        	  dw.setPosition(re.x+(re.w-dw.getWidth()-fs.getBorderSize()),re.y+(re.h-dw.getHeight())/2);
					        	  re.w-=fs.getBorderSize()*2+dw.getWidth();
					        	  break;
					          case ALIGN_TOP:
					        	  dw.setPosition(re.x+fs.getBorderSize(),re.y+fs.getBorderSize());
					        	  re.x+=fs.getBorderSize()*2+dw.getWidth();
					        	  re.w-=fs.getBorderSize()*2+dw.getWidth();
					        	  break;
					          case ALIGN_BOTTOM:
					        	  dw.setPosition(re.x+fs.getBorderSize(),re.y+(re.h-dw.getHeight()-fs.getBorderSize()));
					        	  re.x+=fs.getBorderSize()*2+dw.getWidth();
					        	  re.w-=fs.getBorderSize()*2+dw.getWidth();
					        	  break;
						   }
						   break;
					}
				}
				//the orientation is vertical
				else
				{  
					flagStruct fs=(flagStruct)flags.elementAt(i);
					switch(fs.flagType()){
					   case 0:
						   //the element is aligned center vertically and aligned left horizontally by default 
						   dw.setPosition(re.x+(re.w-dw.getWidth())/2,re.y);
						   re.y+=dw.getHeight();
						   re.h-=dw.getHeight();
						   break;
					   case 1:
						    //only BORDER_ALL flag
						    dw.setPosition(re.x+(re.w-dw.getWidth())/2,re.y+fs.getBorderSize());
						    re.y+=fs.getBorderSize()*2+dw.getHeight();
						    re.h-=fs.getBorderSize()*2+dw.getHeight();
						    break;
//						 // only align flag
					   case 2:
						   switch(fs.getAlignFlag()){
					          case ALIGN_LEFT:
					        	  dw.setPosition(re.x,re.y);
					        	  re.y+=dw.getHeight();
					        	  re.h-=dw.getHeight();
					        	  break;
					          case ALIGN_CENTER:
					        	  dw.setPosition(re.x+(re.w-dw.getWidth())/2, re.y+(re.h-dw.getHeight())/2);
					        	  re.h-=dw.getHeight();
					        	  break;
					          case ALIGN_RIGHT:
					        	  dw.setPosition(re.x+(re.w-dw.getWidth()),re.y);
					        	  re.y+=dw.getHeight();
					        	  re.h-=dw.getHeight();
					        	  break;
					          case ALIGN_TOP:
					        	  dw.setPosition(re.x+(re.w-dw.getWidth())/2,re.y);
					        	  re.y+=dw.getHeight();
					        	  re.h-=dw.getHeight();
					        	  break;
					          case ALIGN_BOTTOM:
					        	  dw.setPosition(re.x+(re.w-dw.getWidth())/2,re.y+(re.h-dw.getHeight()));
					        	  re.h-=dw.getHeight();
					        	  break;
					       }
						   break;
					   //both have align flag and border flag
					   case 3:  
						   switch(fs.getAlignFlag()){
					         case ALIGN_LEFT:
					        	  dw.setPosition(re.x+fs.getBorderSize(),re.y+fs.getBorderSize());
					        	  re.y+=fs.getBorderSize()*2+dw.getHeight();
					        	  re.h-=fs.getBorderSize()*2+dw.getHeight();
					        	  break;
					          case ALIGN_CENTER:
					        	  dw.setPosition(re.x+(re.w-dw.getWidth())/2, re.y+(re.h-dw.getHeight())/2);
					        	  re.h-=fs.getBorderSize()*2+dw.getHeight();
					        	  break;
					          case ALIGN_RIGHT:
					        	  dw.setPosition(re.x+(re.w-dw.getWidth()-fs.getBorderSize()),re.y+fs.getBorderSize());
					        	  re.y+=fs.getBorderSize()*2+dw.getHeight();
					        	  re.h-=fs.getBorderSize()*2+dw.getHeight();
					        	  break;
					          case ALIGN_TOP:
					        	  dw.setPosition(re.x+(re.w-dw.getWidth())/2,re.y+fs.getBorderSize());
					        	  re.y+=fs.getBorderSize()*2+dw.getHeight();
					        	  re.h-=fs.getBorderSize()*2+dw.getHeight();
					        	  break;
					          case ALIGN_BOTTOM:
					        	  dw.setPosition(re.x+(re.w-dw.getWidth())/2,re.y+(re.h-dw.getHeight()-fs.getBorderSize()));
					        	  re.h-=fs.getBorderSize()*2+dw.getHeight();
					        	  break;
						   }
						   break;
					}
				}
			}
			//the element is a sizer
			else if(elements.elementAt(i) instanceof BoxSizer)
			 {  
				BoxSizer sizer=(BoxSizer)elements.elementAt(i);
				//the orientation is horizontal
				if(orientation==HORIZONTAL)
				{   
					//the nested sizer must be a vertical sizer
					flagStruct fs=(flagStruct)flags.elementAt(i);
					Rectangle nestRec;
					switch(fs.flagType()){
						case 0:
							nestRec=new Rectangle(re.x,re.y,sizer.getWidth(),re.h);
							sizer.fit(nestRec);
							re.x+= nestRec.w;
							re.w-=nestRec.w;
							break;
						case 1:
						    nestRec=new Rectangle(re.x+fs.getBorderSize(),re.y,sizer.getWidth(),re.h);
							sizer.fit(nestRec);
							re.x+= nestRec.w+fs.getBorderSize()*2;
							re.w-=nestRec.w+fs.getBorderSize()*2;
							break;
						case 2:
							switch(fs.getAlignFlag()){
						          case ALIGN_LEFT:
						          case ALIGN_TOP:
						          case ALIGN_BOTTOM:  
							      	  nestRec=new Rectangle(re.x,re.y,sizer.getWidth(),re.h);
									  sizer.fit(nestRec);
									  re.x+= nestRec.w;
									  re.w-=nestRec.w;
						        	  break;
						          case ALIGN_CENTER:
						        	  nestRec=new Rectangle(re.x+(re.w-sizer.getWidth())/2,re.y,sizer.getWidth(),re.h);
						        	  sizer.fit(nestRec);
						        	  re.w-=nestRec.w;
						        	  break;
						          case ALIGN_RIGHT:
						        	  nestRec=new Rectangle(re.x+(re.w-sizer.getWidth()),re.y,sizer.getWidth(),re.h);
						        	  sizer.fit(nestRec);
						        	  re.w-=nestRec.w;
						        	  break;
							   }
							break;
						case 3:
							   switch(fs.getAlignFlag()){
						         case ALIGN_LEFT:
						         case ALIGN_TOP:
						         case ALIGN_BOTTOM:
						        	  nestRec=new Rectangle(re.x+fs.getBorderSize(),re.y,sizer.getWidth(),re.h);
									  sizer.fit(nestRec);
									  re.x+= nestRec.w+fs.getBorderSize()*2;
									  re.w-=nestRec.w+fs.getBorderSize()*2;
						        	  break;
						          case ALIGN_CENTER:
						        	  nestRec=new Rectangle(re.x+(re.w-sizer.getWidth())/2,re.y,sizer.getWidth(),re.h);
						        	  sizer.fit(nestRec);
						        	  re.w-=nestRec.w+fs.getBorderSize()*2;
						        	  break;
						          case ALIGN_RIGHT:
						        	  nestRec=new Rectangle(re.x+(re.w-sizer.getWidth()-fs.getBorderSize()),re.y,sizer.getWidth(),re.h);
						        	  sizer.fit(nestRec);
						        	  re.w-=nestRec.w+fs.getBorderSize()*2;
						        	  break;
							   }
							break;
					}
					
				}
				//the orientation is vertical
				else 
				{
					//the nested sizer must be a horizontal sizer
					flagStruct fs=(flagStruct)flags.elementAt(i);
					Rectangle nestRec;
					switch(fs.flagType()){
					case 0:
						nestRec=new Rectangle(re.x,re.y,re.w,sizer.getHeight());
						sizer.fit(nestRec);
						re.y+= nestRec.h;
						re.h-=nestRec.h;
						break;
					case 1:
					    nestRec=new Rectangle(re.x,re.y+fs.getBorderSize(),re.w,re.h);
						sizer.fit(nestRec);
						re.y+= nestRec.h+fs.getBorderSize()*2;
						re.h-=nestRec.h+fs.getBorderSize()*2;
						break;
					case 2:
						switch(fs.getAlignFlag()){
					          case ALIGN_LEFT:
					          case ALIGN_RIGHT:
					          case ALIGN_TOP:  
						      	  nestRec=new Rectangle(re.x,re.y,re.w,sizer.getHeight());
								  sizer.fit(nestRec);
								  re.y+= nestRec.h;
								  re.h-=nestRec.h;
					        	  break;
					          case ALIGN_CENTER:
					        	  nestRec=new Rectangle(re.x ,re.y+(re.h-sizer.getHeight())/2,re.w,sizer.getHeight());
					        	  sizer.fit(nestRec);
					        	  re.h-=nestRec.h;
					        	  break;
					          case ALIGN_BOTTOM:
					        	  nestRec=new Rectangle(re.x,re.y+(re.h-sizer.getHeight()),re.w,sizer.getHeight());
					        	  sizer.fit(nestRec);
					        	  re.h-=nestRec.h;
					        	  break;
						   }
						break;
					case 3:
						   switch(fs.getAlignFlag()){
					         case ALIGN_LEFT:
					         case ALIGN_RIGHT:
					         case ALIGN_TOP:
					        	  nestRec=new Rectangle(re.x,re.y+fs.getBorderSize(),re.w,sizer.getHeight());
								  sizer.fit(nestRec);
								  re.y+= nestRec.h+fs.getBorderSize()*2;
								  re.h-=nestRec.h+fs.getBorderSize()*2;
					        	  break;
					          case ALIGN_CENTER:
					        	  nestRec=new Rectangle(re.x,re.y+(re.h-sizer.getHeight())/2,re.w,sizer.getHeight());
					        	  sizer.fit(nestRec);
					        	  re.h-=nestRec.h+fs.getBorderSize()*2;
					        	  break;
					          case ALIGN_BOTTOM:
					        	  nestRec=new Rectangle(re.x,re.y+(re.h-sizer.getHeight()-fs.getBorderSize()),re.w,sizer.getHeight());
					        	  sizer.fit(nestRec);
					        	  re.h-=nestRec.h+fs.getBorderSize()*2;
					        	  break;
						   }
						break;
				     }
					
				 }
			 }
	    }
    }
}
