/*
 *   
 *
 * Copyright  1990-2006 Sun Microsystems, Inc. All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 only, as published by the Free Software Foundation. 
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License version 2 for more details (a copy is
 * included at /legal/license.txt). 
 * 
 * You should have received a copy of the GNU General Public License
 * version 2 along with this work; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA 
 * 
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
 * Clara, CA 95054 or visit www.sun.com if you need additional
 * information or have any questions. 
 */

package javax.microedition.lcdui;

import com.sun.midp.i3test.*;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
import com.sun.midp.chameleon.skins.*;
import com.sun.midp.midlet.MIDletStateHandler;
import com.sun.midp.util.DummyMIDlet;

/**
 * This test case is meant to verify that the <code>sizeChanged</code>
 * method of a Canvas is called correctly in every instance where it
 * is required by the MIDP specification.
 * It is triggered by the addition or removal of a title, ticker,
 * or both.  It may also be triggered in the event that 
 * the full screen and non-full screen display is toggled.
 *
 * <code>sizeChanged</code>  should be called when the canvas is 
 * visible, or in the event the canvas is not shown, <code>sizeChanged</code>
 * should be called before <code>paint</code> is called.  
 */
public class TestSizeChanged extends TestCase {

    /**
     * Name for this test grouping
     */
    static final String testName = "SizeChanged";
    
    // We define a set of constants that are set by pulling values
    // out of the various skin classes (ie. TitleSkin, TickerSkin, etc).

    /**
     * Width of the screen in non fullscreen mode
     */
    protected static int STD_WIDTH;

    /**
     * Height of the screen in non fullscreen mode
     */
    protected static int STD_HEIGHT;
    
    // Note that although for chameleon's standard spec, the fullscreen
    // height and the standard height are the same, this may not be the
    // case for all implementations. Including both here allows the test
    // to be valid in both circumstances.
    
    /**
     * Height of the screen in fulscreen mode
     */
    protected static int FS_HEIGHT;
    
    /**
     * Height of the title bar
     */
    protected static int TITLE_HEIGHT;

    /**
     * Height of the ticker
     */
    protected static int TICKER_HEIGHT;
    
    /**
     * Height of the soft button bar
     */
    protected static int SOFTBTN_HEIGHT;
    
    /**
     * The Display to use to view our test canvases
     */
    protected Display display;
    
    /**
     * Test ticker, used for sizing only
     */
    protected Ticker ticker;
    
    /**
     * Construct a new SizeChanged test. The constructor
     * initializes all sizing constants from the chameleon
     * skin files.
     */
    public TestSizeChanged() {
        // Initialize the constants
        
        STD_WIDTH = ScreenSkin.WIDTH;
        STD_HEIGHT = ScreenSkin.HEIGHT;
        FS_HEIGHT = ScreenSkin.FULLHEIGHT;
        TITLE_HEIGHT = TitleSkin.HEIGHT;
        TICKER_HEIGHT = TickerSkin.HEIGHT;
        SOFTBTN_HEIGHT = SoftButtonSkin.HEIGHT;

        ticker = new Ticker("Canvas sizeChanged() tests...");
    }
    
    /**
     * This is a utility method which checks values passed by 
     * <code>sizeChanged</code> against passed in values. It 
     * ensures that the method should have been called by waiting
     * for the canvas to be visible on the screen.
     *
     * @param canvas the canvas to test
     * @param WIDTH the <b>correct</b> width the canvas should be after the
     *              most recent <code>sizeChanged</code> call
     * @param HEIGHT the <b>correct</b> height the canvas should be after the
     *               most recent <code>sizeChanged</code> call
     */
    protected void checkCanvasSizeChanged(TestCanvas canvas, 
                                          int WIDTH, int HEIGHT) {
        synchronized (canvas) {
            display.setCurrent(canvas);
            try {
                canvas.wait();
            } catch (InterruptedException ie) {
            } catch (IllegalMonitorStateException im) {
                fail("Unexpected monitor exception");
                return;
            }
        }
        
        // Now the canvas should be showing on the screen, we test
        // our assertions about <code>sizeChanged</code> being called
        assertEquals("Checking for Canvas.sizeChanged() width update", 
                     WIDTH, canvas.checkSizeWidth());
        assertEquals("Checking for Canvas.sizeChanged() height update", 
                     HEIGHT, canvas.checkSizeHeight());
    }

    /**
     * This is a utility method which checks values passed by 
     * <code>sizeChanged</code> against passed in values. It 
     * ensures that the method should have been called by waiting
     * for the canvas to be visible on the screen.
     *
     * @param canvas the canvas to test
     * @param WIDTH the <b>correct</b> width the canvas should be after the
     *              most recent <code>sizeChanged</code> call
     * @param HEIGHT the <b>correct</b> height the canvas should be after the
     *               most recent <code>sizeChanged</code> call
     */
    protected void checkCurrentCanvasSizeChanged(TestCanvas canvas, 
                                                 int WIDTH, int HEIGHT) {
        synchronized (canvas) {
            //display.setCurrent(canvas);
            try {
                canvas.wait();
            } catch (InterruptedException ie) {
            } catch (IllegalMonitorStateException im) {
                fail("Unexpected monitor exception");
                return;
            }
        }
        
        // Now the canvas should be showing on the screen, we test
        // our assertions about <code>sizeChanged</code> being called
        assertEquals("Checking for Canvas.sizeChanged() width update", 
                     WIDTH, canvas.checkSizeWidth());
        assertEquals("Checking for Canvas.sizeChanged() height update", 
                     HEIGHT, canvas.checkSizeHeight());
    }
   
    /*
     * Tests 1-4 involve adding components to a Canvas which is not
     * the current display, and checking to see that the proper
     * <code>sizeChanged()</code> occurs whenthe Canvas is brought to 
     * the foreground with a <code>display.setCurrent()</code> call.
     */

    /**
     * This test is for a canvas with no title and no ticker set in
     * standard screen mode.  The canvas is set to the current display 
     * causing <code>sizeChanged</code> to be called.
     */
    protected void testOne() {
        declare(testName + " 1: bg->fg: std-screen, no title, no ticker");
        
        TestCanvas canvas = new TestCanvas();

        checkCanvasSizeChanged(canvas, STD_WIDTH, STD_HEIGHT - SOFTBTN_HEIGHT); 
    }
    
    /**
     * This test is for a canvas with a title set in standard screen mode.
     * The canvas is set to the current display causing <code>sizeChanged</code> 
     * to be called.
     */
    protected void testTwo() {
        declare(testName + " 2: bg->fg: std-screen, add title");
        
        TestCanvas canvas = new TestCanvas();
        canvas.setTitle("SizeChanged Test 2");
        
        checkCanvasSizeChanged(canvas, STD_WIDTH, 
                               STD_HEIGHT - SOFTBTN_HEIGHT - TITLE_HEIGHT);
    }

    /**
     * This test is for a canvas with a title and ticker set in standard 
     * screen mode.  The canvas is set to the current display causing 
     * <code>sizeChanged</code> to be called.
     */
    protected void testThree() {
        declare(testName + " 3: bg->fg: std-screen, add title, add ticker");
        
        TestCanvas canvas = new TestCanvas();
        canvas.setTitle("sizeChanged Test 3");
        canvas.setTicker(ticker);
        
        checkCanvasSizeChanged(canvas, STD_WIDTH, STD_HEIGHT - SOFTBTN_HEIGHT 
                               - TICKER_HEIGHT - TITLE_HEIGHT);
    }

    /**
     * This test is for a canvas with a ticker set in standard screen mode.
     * The canvas is set to the current display causing <code>sizeChanged</code> 
     * to be called.
     */
    protected void testFour() {
        declare(testName + " 4: bg->fg: std-screen, add ticker");
        
        TestCanvas canvas = new TestCanvas();
        canvas.setTicker(ticker);
        
        checkCanvasSizeChanged(canvas, STD_WIDTH, 
                               STD_HEIGHT - SOFTBTN_HEIGHT - TICKER_HEIGHT);
    }

    /*
     * Tests 5-8 involve removing components from a Canvas which is not
     * the current display, and checking to see that the proper
     * <code>sizeChanged()</code> occurs whenthe Canvas is brought to 
     * the foreground with a <code>display.setCurrent()</code> call.
     */
    
    /**
     * This test is for a canvas with a title and a ticker set in
     * standard screen mode.  The canvas is set to the current display 
     * causing <code>sizeChanged</code> to be called.
     */
    protected void testFive() {
        declare(testName + " 5: bg->fg: std-screen, with title & ticker");
        
        TestCanvas tmpCanvas = new TestCanvas();
        TestCanvas canvas = new TestCanvas();

        display.setCurrent(canvas);

        canvas.setTitle("SizeChanged test 5");
        canvas.setTicker(ticker);

        display.setCurrent(tmpCanvas);

        checkCanvasSizeChanged(canvas, STD_WIDTH, STD_HEIGHT - SOFTBTN_HEIGHT -
                               TITLE_HEIGHT - TICKER_HEIGHT); 
    }
    
    /**
     * This test is for a canvas with a title and ticker set in standard 
     * screen mode.  The ticker is removed, and the canvas is set to the 
     * current display causing <code>sizeChanged</code> to be called.
     */
    protected void testSix() {
        declare(testName + " 6: bg->fg: std-screen, with title & ticker." + 
                "  remove ticker");
        
        TestCanvas tmpCanvas = new TestCanvas();
        TestCanvas canvas = new TestCanvas();

        display.setCurrent(canvas);

        canvas.setTitle("SizeChanged Test 6");
        canvas.setTicker(ticker);

        display.setCurrent(tmpCanvas);

        canvas.setTicker(null);
        
        checkCanvasSizeChanged(canvas, STD_WIDTH, STD_HEIGHT - SOFTBTN_HEIGHT 
                               - TITLE_HEIGHT);
    }

    /**
     * This test is for a canvas with a title and ticker set in standard 
     * screen mode.  The title is removed, and the canvas is set to the current 
     * display causing <code>sizeChanged</code> to be called.
     */
    protected void testSeven() {
        declare(testName + " 7: bg->fg: std-screen, with title & ticker." + 
                "  remove title");
        
        TestCanvas tmpCanvas = new TestCanvas();
        TestCanvas canvas = new TestCanvas();

        display.setCurrent(canvas);

        canvas.setTitle("SizeChanged Test 7");
        canvas.setTicker(ticker);

        display.setCurrent(tmpCanvas);

        canvas.setTitle(null);
        
        checkCanvasSizeChanged(canvas, STD_WIDTH, STD_HEIGHT - SOFTBTN_HEIGHT 
                               - TICKER_HEIGHT);
    }

    /**
     * This test is for a canvas with a title and ticker set in standard 
     * screen mode.  The ticker and title are removed, and the canvas is 
     * set to the current display causing <code>sizeChanged</code> 
     * to be called.
     */
    protected void testEight() {
        declare(testName + " 8: bg->fg: std-screen, with title & ticker." + 
                "  remove both ticker and title");
        
        TestCanvas tmpCanvas = new TestCanvas();
        TestCanvas canvas = new TestCanvas();
        
        display.setCurrent(canvas);
        
        canvas.setTitle("SizeChanged Test 8");
        canvas.setTicker(ticker);
        
        display.setCurrent(tmpCanvas);
        
        canvas.setTicker(null);
        canvas.setTitle(null);
        
        checkCanvasSizeChanged(canvas, STD_WIDTH, STD_HEIGHT - SOFTBTN_HEIGHT);
    }

    /**
     * This test is for a canvas with a title and ticker set in standard 
     * screen mode.  FullScreen mode is then set on the Canvas and it is
     * set to the current display causing <code>sizeChanged</code> 
     * to be called.
     */
    protected void testNine() {
        declare(testName + " 9: bg->fg: std-screen, with title & ticker." + 
                "  set fullscreen mode");
        
        TestCanvas tmpCanvas = new TestCanvas();
        TestCanvas canvas = new TestCanvas();
        
        display.setCurrent(canvas);
        
        canvas.setTitle("SizeChanged Test 9");
        canvas.setTicker(ticker);
        
        display.setCurrent(tmpCanvas);
        
        canvas.toggleFullscreen(true);
        
        checkCanvasSizeChanged(canvas, STD_WIDTH, FS_HEIGHT - SOFTBTN_HEIGHT);
    }

    /**
     * This test is for a canvas with a title and ticker set in full 
     * screen mode.  Standard mode is then set on the Canvas and it is
     * set to the current display causing <code>sizeChanged</code> 
     * to be called.
     */
    protected void testTen() {
        declare(testName + " 10: bg->fg: full-screen, with title & ticker." + 
                "  set std-screen mode");
        
        TestCanvas tmpCanvas = new TestCanvas();
        TestCanvas canvas = new TestCanvas();
        
        display.setCurrent(canvas);

        canvas.toggleFullscreen(true);  
        canvas.setTitle("SizeChanged Test 10");
        canvas.setTicker(ticker);

        
        display.setCurrent(tmpCanvas);
        
        canvas.toggleFullscreen(false);
        
        checkCanvasSizeChanged(canvas, STD_WIDTH, FS_HEIGHT - SOFTBTN_HEIGHT -
                               TICKER_HEIGHT - TITLE_HEIGHT);
    }

    /**
     * Test 11 involves removing components from a Canvas which is
     * the current display, and checking to see that the proper
     * <code>sizeChanged()</code> occurs after each removal
     */
    protected void testEleven() {
        declare(testName + " 11: in fg: std-screen, with title & ticker." +
                " remove title, then ticker.");
        
        TestCanvas tmpCanvas = new TestCanvas();
        TestCanvas canvas = new TestCanvas();

        display.setCurrent(tmpCanvas);

        canvas.setTitle("SizeChanged test 11");
        canvas.setTicker(ticker);

        checkCanvasSizeChanged(canvas, STD_WIDTH, STD_HEIGHT - SOFTBTN_HEIGHT -
                               TITLE_HEIGHT - TICKER_HEIGHT); 

        canvas.setTitle(null);
        checkCurrentCanvasSizeChanged(canvas, STD_WIDTH, STD_HEIGHT - 
                                      SOFTBTN_HEIGHT - TICKER_HEIGHT); 

        canvas.setTicker(null);
        checkCurrentCanvasSizeChanged(canvas, STD_WIDTH, STD_HEIGHT - SOFTBTN_HEIGHT); 
    }
    
    /**
     * Test 12 involves adding components from a canvas which is
     * current display, and checking to see that the proper
     * <code>sizeChanged</code> call occurs after each addition.
     */
    protected void testTwelve() {
        declare(testName + " 12: in fg: std-screen.  add title, then ticker");
        
        TestCanvas canvas = new TestCanvas();
        
        checkCanvasSizeChanged(canvas, STD_WIDTH, STD_HEIGHT - SOFTBTN_HEIGHT); 
        
        canvas.setTitle("SizeChanged Test 12");
        checkCurrentCanvasSizeChanged(canvas, STD_WIDTH, STD_HEIGHT - 
                                      SOFTBTN_HEIGHT - TITLE_HEIGHT); 
        
        canvas.setTicker(ticker);
        checkCurrentCanvasSizeChanged(canvas, STD_WIDTH, STD_HEIGHT - 
                                      SOFTBTN_HEIGHT - TITLE_HEIGHT - 
                                      TICKER_HEIGHT); 
    }

    /**
     * Test 13 involves changing to fullScreen mode and back on
     * a canvas which is the current display, and checking to see that the proper
     * <code>sizeChanged</code> call occurs after each change.
     */
    protected void testThirteen() {
        declare(testName + " 13: in fg: std-screen, with title & ticker." +
                " remove title, then ticker.");
        
        TestCanvas tmpCanvas = new TestCanvas();
        TestCanvas canvas = new TestCanvas();

        display.setCurrent(tmpCanvas);

        canvas.setTitle("SizeChanged test 13");
        canvas.setTicker(ticker);
        
        checkCanvasSizeChanged(canvas, STD_WIDTH, STD_HEIGHT - SOFTBTN_HEIGHT -
                               TITLE_HEIGHT - TICKER_HEIGHT); 

        canvas.toggleFullscreen(true);
        checkCurrentCanvasSizeChanged(canvas, STD_WIDTH, STD_HEIGHT - SOFTBTN_HEIGHT); 

        canvas.toggleFullscreen(false);
        checkCurrentCanvasSizeChanged(canvas, STD_WIDTH, STD_HEIGHT - SOFTBTN_HEIGHT
                                      - TITLE_HEIGHT - TICKER_HEIGHT); 
    }

    /**
     * Overridden from TestCase parent. This method will kick off each
     * individual test
     */
    public void runTests() {
        
        MIDletStateHandler msHandler = MIDletStateHandler.getMidletStateHandler();
        // Start a new instance of DummyMIDlet
        try {
            msHandler.startMIDlet("com.sun.midp.util.DummyMIDlet",
                                  "DummyMIDlet");
        } catch (Exception cnf) {
            return;
        }
        
        // Wait for async request to be processed 
        while (DummyMIDlet.midlet == null) {
            try { Thread.sleep(100); } catch (InterruptedException ie) {;}
        }

        MIDlet midlet = DummyMIDlet.midlet;
        display = Display.getDisplay(midlet);
        
        testOne();   
        testTwo();
        testThree();
        testFour();
        testFive();
        testSix();
        testSeven();
        testEight();
        testNine();
        testTen();
        testEleven();
        testTwelve();
        testThirteen();
        
        // Notify the system that our midlet is done
        midlet.notifyDestroyed();
        try {
            ((DummyMIDlet)midlet).destroyApp(true);
        } catch (MIDletStateChangeException ignore) {
        }
    }
    
    /**
     * Inner canvas class which notifies the main TestCase when
     * a particular canvas has become visible. This allows the
     * main TestCase know that <code>sizeChanged</code> should have 
     * been called by a specific time.
     */
    class TestCanvas extends Canvas {

        /**
         * Most recent width value in a <code>sizeChanged</code> call.
         * Reset to -1 when <code>checkSizeWidth</code> is called.
         */
        private int curW = -1;

        /**
         * Most recent height value in a <code>sizeChanged</code> call.
         * Reset to -1 when <code>checkSizeHeight</code> is called.
         */
        private int curH = -1;

        /**
         * Construct a new TestCanvas given the parent test handler.
         */
        public TestCanvas() {
            super();
        }

        /**
         * Returns the most recent width from a <code>sizeChanged</code>
         * call and resets <code>curW</code> to -1
	 * @return width from the most recent sizeChanged call
         */
        public int checkSizeWidth() {       
            int tmp;
            tmp = curW;
            curW = -1;
            return tmp;
        }

        /**
         * Returns the most recent height from a <code>sizeChanged</code>
         * call and resets <code>curH</code> to -1
	 * @return heightfrom the most recent sizeChanged call
         */
        public int checkSizeHeight() {      
            int tmp;
            tmp = curH;
            curH = -1;
            return tmp;
        }
        
        /**
         * Used to remotely toggle this canvas's fullscreen mode
         *
         * @param onOff true if this canvas should go to fullscreen mode
         */
        public void toggleFullscreen(boolean onOff) {
            super.setFullScreenMode(onOff);
        }
        
        /**
         * This notifies the canvas that it's available size for drawing
         * has changed (due to a title, ticker, or fullscreen mode change)
         *
         * @param w the new width in pixels of the drawable area of the
         * <code>Canvas</code>
         * @param h the new height in pixels of the drawable area of
         * the <code>Canvas</code>
         */
        protected void sizeChanged(int w, int h) {
            curW = w;
            curH = h;
        }
        
        /** 
         * Just a default white fill. The test programmatically changes
         * screens so it would be too fast to write anything useful anyway,
         * as the user wouldn't have time to read it.
         *
         * @param g the Graphics to paint with
         */
        protected void paint(Graphics g) {
            int c = g.getColor();

            // IMPL_NOTE: 
	    // remove this once chameleon sizeChanged() code is working.
            // This line is used to fake sizeChanged() being called before paint().
            // It appears sizeChanged is not being called in the chameleon code...
	    // ...but we don't know why.
            this.sizeChanged(this.getWidth(), this.getHeight());

            g.setColor(0xFFFFFF);
            g.fillRect(g.getClipX(), g.getClipY(), 
                       g.getClipWidth(), g.getClipHeight());
            g.setColor(c);

            // The spec guarantees us that paint can only be called when
            // the Canvas is visible, that is, isShown() returns true.
            synchronized (this) {
                try {
                    this.notify();
                } catch (IllegalMonitorStateException e) {
                    // If the other wait()ing thread doesn't get
                    // interrupted, the whole test will block anyway, so
                    // we rely on wait/notify to be working correctly
                }
            }
        }
    }    
}
