/*
 * 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 javax.microedition.lcdui.*;

import minime.core.CanDisable;
import minime.core.ResourceManager;
import minime.core.CanSelect;

/**
 * An object that has the capability of being placed on the display. The
 * contents displayed and their interaction with the user are defined by
 * subclasses. Unless otherwise specified by a subclass, the default state of
 * newly created Displayable objects is as follows: it is visible on the
 * Display; the coordinates is (0,0) and the size is also 0; the background
 * color is transparent; it is not clipped.
 * 
 * @author aaa
 * 
 */
public abstract class Drawable
{
    /** The status is not be selected but it's not disabled */
    public final static int NOT_SELECTED_ABLE = 0;
    /** Selected status */
//    public final static int SELECTED = 1;
    /** Disabled status */
//    public final static int DISABLED = 2;

    /** The x,y coordinates and size of the drawable objecct */
    private int left;
    private int top;
    private int w;
    private int h;
    /** the background color */
    private int bgColor;
    private Rectangle clipRectangle;

    /** associated ressource ID (-1 if any) */
    private int rscId ;
    
    /** set to true when the drawable need to layout by layout methode */
    private boolean needLayout ;
    /** set to true when the layout is going on, set to false when finish */
    private boolean doingLayout ;
    /** The status */
    private int status;

    /** The visible attribute, if true the component will be drawn */
    private boolean visible;

    /** the drawable object clip status */
    public boolean clip;

    static final Logger LOG = Logger.getLogger("minime.Drawable");

    public Drawable()
    {
        this.left = 0;
        this.top = 0;
        this.w = 0;
        this.h = 0 ;

        this.visible=true;

        this.clip = false;
        this.clipRectangle = new Rectangle();
        bgColor = Portability.TRANSPARENT ;
        rscId = ResourceManager.NONE_RESOURCE ;
        needLayout = true ;
        doingLayout = false ;
        
    }

    /**
     * Renders the drawable object. If the visible attribute is false, it
     * doesn't render the drawable. If clip attribute is true, it just renders
     * the drawable area.
     * 
     * @param gc
     *            the Graphics object to be used for rendering the Drawable
     */
    public final void render(Graphics gc)
    {
        if (!isVisible())
        {
            return;
        }

        // layout the component 
        layout() ;

        // translate the coordinate 
        gc.translate(left, top) ;
        
        // clip if needed 
        if (clip)
        {
            GraphicsUtil.getClip(gc, clipRectangle);
            GraphicsUtil.setClip(gc, left, top, w, h);
            LOG.debug("Clip " + this + "(" + getLeft() + ", " + getTop() + ", " + w + ", " + h);
        }

        // rendering         
        LOG.debug("Rendering " + this + "(" + getLeft() + ", " + getTop() + ", " + w + ", " + h);
        if (getBgColor() != Portability.TRANSPARENT)
        	drawBackground(gc) ;

        // check type of rendering 
        if (this instanceof CanSelect && ((CanSelect)this).isSelected())
        {
        	((CanSelect)this).renderImplSelected(gc) ;
        }
        else if (this instanceof CanDisable && !((CanDisable)this).isEnable())
        {
        	((CanDisable)this).renderImplDisable(gc) ;
        }
        else // basic rendering 
        	renderImpl(gc);
        gc.translate(-left, -top) ;

        // restore clip area
        if (clip)
        {
            GraphicsUtil.setClip(gc, clipRectangle);
            LOG.debug("Restore clip " + this + "(" + clipRectangle.x + ", " + clipRectangle.y + ", " + clipRectangle.w + ", " + clipRectangle.h);
        }
    }

    public final void layout()
    {
    	if (!doingLayout && needLayout && isVisible())
    	{
            LOG.debug("perform layout:" + this);
			// set doingLayout in order to avoid recursive call to layout() 
            // through getWidth and getHeight
            doingLayout=true ;
    		layoutImp() ;
    		// layout is done, clear the flags
    		needLayout = false ;
    		doingLayout = false ;
    	}
    }

    protected void layoutImp()
    {
    	needLayout = false ;
    }
    
    public void setNeedLayout()
    {
    	if (doingLayout==false)
    		needLayout = true ;
    }

    public void clrNeedLayout()
    {
    	needLayout = false ;
    }

//    /**
//     * Verify the Drawable property
//     * 
//     * @param property
//     *            the Drawable property
//     * @return true if Drawable status is that property
//     */
//    public boolean is(int property)
//    {
//        return (status & property) > 0;
//    }
//
//    /**
//     * Sets the Drawable property
//     * 
//     * @param property
//     *            the Drawable property
//     */
//    public void set(int property)
//    {
//        status |= property;
//    }
//
//    /**
//     * Unsets the Drawable property
//     * 
//     * @param property
//     *            the sepcify property
//     */
//    public void unset(int property)
//    {
//        status &= ~property;
//    }

    protected void drawBackground(Graphics gc)
    {
	    int savedColor = gc.getColor();
        gc.setColor(getBgColor());
        gc.fillRect(0, 0, w, h);  // fill from 0,0 because Drawable might have already been translated ...
	    gc.setColor(savedColor);
    }
    
    /**
     * Renders the Drawable implementation.The operation will be defined by
     * subclasses.
     * 
     * @param gc
     *            the Graphics object to be used for rendering the Drawable
     */
    public abstract void renderImpl(Graphics gc);

//    /**
//     * Sets Drawable selected status.
//     * 
//     * @param s
//     *            the selected status, true is selected, false is not selected.
//     */
//    public void setSelected(boolean s)
//    {
//        if (s)
//            set(SELECTED);
//        else
//            unset(SELECTED);
//    }

	public void setWidth(int w)
	{
		// update the width if necessary
		if (w!=this.w)
		{
			setNeedLayout();
			this.w = w;
		}
	}

	public void setHeight(int h)
	{
		// update the height if necessary 
		if (h!=this.h)
		{
			setNeedLayout() ;
			this.h = h ;
		}
	}

	// do auto layout to make sure Width & Height are set 
	public int getWidth()
	{
		layout() ;
		return w;
	}

	public int getHeight()
	{
		layout() ;
		return h;
	}

	public int getRscId()
	{
		return rscId;
	}

	public void setRscId(int rscId) {
		this.rscId = rscId;
	}

	public void setBgColor(int bgColor) {
		this.bgColor = bgColor;
	}

	public int getBgColor() {
		return bgColor;
	}
	public void activate()
	{
		return ;
	}
	public void deactivate()
	{
		return ;
	}
    /**
     * Sets the Drawable position
     * 
     * @param left
     *            the x coordinate of Drawable
     * @param top
     *            the x coordinate of Drawable
     */
    public void setPosition(int left, int top)
    {
        this.setLeft(left);
        this.setTop(top);
    }
	
	public void setLeft(int left) {
		this.left = left;
	}

	public int getLeft() {
		return left;
	}

	public void setTop(int top) {
		this.top = top;
	}

	public int getTop() {
		return top;
	}

	public void setVisible(boolean visible) {
		this.visible = visible;
	}

	public boolean isVisible() {
		return visible;
	}

//	public boolean isEnable() {
//		return enable;
//	}
//
//	public void setEnable(boolean enable) {
//		this.enable = enable;
//	}
//
//	public boolean isSelected() {
//		return selected;
//	}
//
//	public void setSelected(boolean selected) {
//		this.selected = selected;
//	}
}
