/*
 * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Oracle nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * (C) Copyright IBM Corp. 2003, All Rights Reserved.
 * This technology is protected by multiple US and International
 * patents. This notice and attribution to IBM may not be removed.
 */

package j2dbench.tests.text;

import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.font.GlyphMetrics;
import java.awt.font.GlyphVector;
import java.awt.font.TextHitInfo;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.text.Bidi;
import java.util.ArrayList;

import j2dbench.Group;
import j2dbench.Result;
import j2dbench.TestEnvironment;

public abstract class TextMeasureTests extends TextTests {
    static Group measureroot;
    static Group measuretestroot;

    public static void init() {
        measureroot = new Group(textroot, "Measuring", "Measuring Benchmarks");
        measuretestroot = new Group(measureroot, "tests", "Measuring Tests");

        new StringWidth();
        new StringBounds();
        new CharsWidth();
        new CharsBounds();
        new FontCanDisplay();

        if (hasGraphics2D) {
            new GVWidth();
            new GVLogicalBounds();
            new GVVisualBounds();
            new GVPixelBounds();
            new GVOutline();
            new GVGlyphLogicalBounds();
            new GVGlyphVisualBounds();
            new GVGlyphPixelBounds();
            new GVGlyphOutline();
            new GVGlyphTransform();
            new GVGlyphMetrics();

            new TLAdvance();
            new TLAscent();
            new TLBounds();
            new TLGetCaretInfo();
            new TLGetNextHit();
            new TLGetCaretShape();
            new TLGetLogicalHighlightShape();
            new TLHitTest();
            new TLOutline();

        /*
            new FontLineMetrics();
            new FontStringBounds();
        */
        }
    }

    public TextMeasureTests(Group parent, String nodeName, String description) {
        super(parent, nodeName, description);
    }

    static class SWContext extends TextContext {
        FontMetrics fm;

        public void init(TestEnvironment env, Result results) {
            super.init(env, results);
            fm = graphics.getFontMetrics(font);
        }
    }

    public Context createContext() {
        return new SWContext();
    }

    public static class StringWidth extends TextMeasureTests {
        public StringWidth() {
            super(measuretestroot, "stringWidth", "Measuring String Width");
        }

        public void runTest(Object ctx, int numReps) {
            SWContext swctx = (SWContext)ctx;
            String text = swctx.text;
            FontMetrics fm = swctx.fm;
            int wid = 0;
            do {
                wid += fm.stringWidth(text);
            } while (--numReps >= 0);
        }
    }

    public static class StringBounds extends TextMeasureTests {
        public StringBounds() {
            super(measuretestroot, "stringBounds", "Measuring String Bounds");
        }

        public void runTest(Object ctx, int numReps) {
            SWContext swctx = (SWContext)ctx;
            String text = swctx.text;
            FontMetrics fm = swctx.fm;
            int wid = 0;
            Rectangle r = null;
            do {
                r = null;
                int dx = fm.stringWidth(text);
                int dy = fm.getAscent() + fm.getDescent() + fm.getLeading();
                int x = 0;
                int y = -fm.getAscent();
                r = new Rectangle(x, y, dx, dy);
            } while (--numReps >= 0);
        }
    }

    public static class CharsWidth extends TextMeasureTests {
        public CharsWidth() {
            super(measuretestroot, "charsWidth", "Measuring Chars Width");
        }

        public void runTest(Object ctx, int numReps) {
            SWContext swctx = (SWContext)ctx;
            FontMetrics fm = swctx.fm;
            char[] chars = swctx.chars;
            int wid = 0;
            do {
                wid += fm.charsWidth(chars, 0, chars.length);
            } while (--numReps >= 0);
        }
    }

    public static class CharsBounds extends TextMeasureTests {
        public CharsBounds() {
            super(measuretestroot, "charsBounds", "Measuring Chars Bounds");
        }

        public void runTest(Object ctx, int numReps) {
            SWContext swctx = (SWContext)ctx;
            FontMetrics fm = swctx.fm;
            char[] chars = swctx.chars;
            int wid = 0;
            Rectangle r = null;
            do {
                r = null;
                int dx = fm.charsWidth(chars, 0, chars.length);
                int dy = fm.getAscent() + fm.getDescent() + fm.getLeading();
                int x = 0;
                int y = -fm.getAscent();
                r = new Rectangle(x, y, dx, dy);
            } while (--numReps >= 0);
        }
    }

    public static class FontCanDisplay extends TextMeasureTests {
        public FontCanDisplay() {
            super(measuretestroot, "fontcandisplay", "Font canDisplay(char)");
        }

        public void runTest(Object ctx, int numReps) {
            Font font = ((TextContext)ctx).font;
            boolean b = false;
            do {
                for (int i = 0; i < 0x10000; i += 0x64) {
                    b ^= font.canDisplay((char)i);
                }
            } while (--numReps >= 0);
        }
    }

    public static class GVContext extends G2DContext {
        GlyphVector gv;

        public void init(TestEnvironment env, Result results) {
            super.init(env, results);

            int flags = Font.LAYOUT_LEFT_TO_RIGHT;
            if (Bidi.requiresBidi(chars, 0, chars.length)) { // assume rtl
                flags = Font.LAYOUT_RIGHT_TO_LEFT;
            }
            gv = font.layoutGlyphVector(frc, chars, 0, chars.length, flags);

            // gv options
        }
    }

    public static abstract class GVMeasureTest extends TextMeasureTests {
        protected GVMeasureTest(Group parent, String nodeName, String description) {
            super(parent, nodeName, description);
        }

        public Context createContext() {
            return new GVContext();
        }
    }

    public static class GVWidth extends GVMeasureTest {
        public GVWidth() {
            super(measuretestroot, "gvWidth", "Measuring GV Width");
        }

        public void runTest(Object ctx, int numReps) {
            GVContext gvctx = (GVContext)ctx;
            GlyphVector gv = gvctx.gv;
            double wid = 0;
            do {
                wid += gv.getGlyphPosition(gv.getNumGlyphs()).getX();
            } while (--numReps >= 0);
        }
    }

    public static class GVLogicalBounds extends GVMeasureTest {
        public GVLogicalBounds() {
            super(measuretestroot, "gvLogicalBounds", "Measuring GV Logical Bounds");
        }

        public void runTest(Object ctx, int numReps) {
            GVContext gvctx = (GVContext)ctx;
            GlyphVector gv = gvctx.gv;
            Rectangle2D r;
            do {
                r = gv.getLogicalBounds();
            } while (--numReps >= 0);
        }
    }

    public static class GVVisualBounds extends GVMeasureTest {
        public GVVisualBounds() {
            super(measuretestroot, "gvVisualBounds", "Measuring GV Visual Bounds");
        }

        public void runTest(Object ctx, int numReps) {
            GVContext gvctx = (GVContext)ctx;
            GlyphVector gv = gvctx.gv;
            Rectangle2D r;
            do {
                r = gv.getVisualBounds();
            } while (--numReps >= 0);
        }
    }

    public static class GVPixelBounds extends GVMeasureTest {
        public GVPixelBounds() {
            super(measuretestroot, "gvPixelBounds", "Measuring GV Pixel Bounds");
        }

        public void runTest(Object ctx, int numReps) {
            GVContext gvctx = (GVContext)ctx;
            GlyphVector gv = gvctx.gv;
            Rectangle2D r;
            do {
                r = gv.getPixelBounds(null, 0, 0); // !!! add opt to provide different frc?
            } while (--numReps >= 0);
        }
    }

    public static class GVOutline extends GVMeasureTest {
        public GVOutline() {
            super(measuretestroot, "gvOutline", "Getting GV Outline");
        }

        public void runTest(Object ctx, int numReps) {
            GVContext gvctx = (GVContext)ctx;
            GlyphVector gv = gvctx.gv;
            Shape s;
            do {
                s = gv.getOutline();
            } while (--numReps >= 0);
        }
    }

    public static class GVGlyphLogicalBounds extends GVMeasureTest {
        public GVGlyphLogicalBounds() {
            super(measuretestroot, "gvGlyphLogicalBounds", "Measuring GV Glyph Logical Bounds");
        }

        public void runTest(Object ctx, int numReps) {
            GVContext gvctx = (GVContext)ctx;
            GlyphVector gv = gvctx.gv;
            Shape s;
            do {
                for (int i = 0, e = gv.getNumGlyphs(); i < e; ++i) {
                    s = gv.getGlyphLogicalBounds(i);
                }
            } while (--numReps >= 0);
        }
    }

    public static class GVGlyphVisualBounds extends GVMeasureTest {
        public GVGlyphVisualBounds() {
            super(measuretestroot, "gvGlyphVisualBounds", "Measuring GV Glyph Visual Bounds");
        }

        public void runTest(Object ctx, int numReps) {
            GVContext gvctx = (GVContext)ctx;
            GlyphVector gv = gvctx.gv;
            Shape s;
            do {
                for (int i = 0, e = gv.getNumGlyphs(); i < e; ++i) {
                    s = gv.getGlyphVisualBounds(i);
                }
            } while (--numReps >= 0);
        }
    }


    public static class GVGlyphPixelBounds extends GVMeasureTest {
        public GVGlyphPixelBounds() {
            super(measuretestroot, "gvGlyphPixelBounds", "Measuring GV Glyph Pixel Bounds");
        }

        public void runTest(Object ctx, int numReps) {
            GVContext gvctx = (GVContext)ctx;
            GlyphVector gv = gvctx.gv;
            Rectangle2D r;
            do {
                for (int i = 0, e = gv.getNumGlyphs(); i < e; ++i) {
                    r = gv.getGlyphPixelBounds(i, null, 0, 0); // !!! add opt to provide different frc?
                }
            } while (--numReps >= 0);
        }
    }

    public static class GVGlyphOutline extends GVMeasureTest {
        public GVGlyphOutline() {
            super(measuretestroot, "gvGlyphOutline", "Getting GV Glyph Outline");
        }

        public void runTest(Object ctx, int numReps) {
            GVContext gvctx = (GVContext)ctx;
            GlyphVector gv = gvctx.gv;
            Shape s;
            do {
                for (int i = 0, e = gv.getNumGlyphs(); i < e; ++i) {
                    s = gv.getGlyphOutline(i);
                }
            } while (--numReps >= 0);
        }
    }

    public static class GVGlyphTransform extends GVMeasureTest {
        public GVGlyphTransform() {
            super(measuretestroot, "gvGlyphTransform", "Getting GV Glyph Transform");
        }

        public void runTest(Object ctx, int numReps) {
            GVContext gvctx = (GVContext)ctx;
            GlyphVector gv = gvctx.gv;
            AffineTransform tx;
            do {
                for (int i = 0, e = gv.getNumGlyphs(); i < e; ++i) {
                    tx = gv.getGlyphTransform(i);
                }
            } while (--numReps >= 0);
        }
    }

    public static class GVGlyphMetrics extends GVMeasureTest {
        public GVGlyphMetrics() {
            super(measuretestroot, "gvGlyphMetrics", "Getting GV Glyph Metrics");
        }

        public void runTest(Object ctx, int numReps) {
            GVContext gvctx = (GVContext)ctx;
            GlyphVector gv = gvctx.gv;
            GlyphMetrics gm;
            do {
                for (int i = 0, e = gv.getNumGlyphs(); i < e; ++i) {
                    gm = gv.getGlyphMetrics(i);
                }
            } while (--numReps >= 0);
        }
    }

    public static class TLContext extends G2DContext {
        TextLayout tl;

        public void init(TestEnvironment env, Result results) {
            super.init(env, results);

            // need more tl options here
            tl = new TextLayout(text, font, frc);
        }
    }

    public static abstract class TLMeasureTest extends TextMeasureTests {
        protected TLMeasureTest(Group parent, String nodeName, String description) {
            super(parent, nodeName, description);
        }

        public Context createContext() {
            return new TLContext();
        }
    }

    public static class TLAdvance extends TLMeasureTest {
        public TLAdvance() {
            super(measuretestroot, "tlAdvance", "Measuring TL advance");
        }

        public void runTest(Object ctx, int numReps) {
            TLContext tlctx = (TLContext)ctx;
            TextLayout tl = tlctx.tl;
            double wid = 0;
            do {
                wid += tl.getAdvance();
            } while (--numReps >= 0);
        }
    }

    public static class TLAscent extends TLMeasureTest {
        public TLAscent() {
            super(measuretestroot, "tlAscent", "Measuring TL ascent");
        }

        public void runTest(Object ctx, int numReps) {
            TLContext tlctx = (TLContext)ctx;
            TextLayout tl = tlctx.tl;
            float ht = 0;
            do {
                ht += tl.getAscent();
            } while (--numReps >= 0);
        }
    }

    public static class TLBounds extends TLMeasureTest {
        public TLBounds() {
            super(measuretestroot, "tlBounds", "Measuring TL advance");
        }

        public void runTest(Object ctx, int numReps) {
            TLContext tlctx = (TLContext)ctx;
            TextLayout tl = tlctx.tl;
            Rectangle2D r;
            do {
                r = tl.getBounds();
            } while (--numReps >= 0);
        }
    }

    static class TLExContext extends TLContext {
        TextHitInfo[] hits;
        Rectangle2D lb;

        public void init(TestEnvironment env, Result results) {
            super.init(env, results);

            ArrayList list = new ArrayList(text.length() * 2 + 2);
            TextHitInfo hit = TextHitInfo.trailing(-1);
            do {
                list.add(hit);
                hit = tl.getNextRightHit(hit);
            } while (hit != null);
            hits = (TextHitInfo[])list.toArray(new TextHitInfo[list.size()]);

            lb = tl.getBounds();
            lb.setRect(lb.getMinX() - 10, lb.getMinY(), lb.getWidth() + 20, lb.getHeight());
        }
    }

    public static abstract class TLExtendedMeasureTest extends TLMeasureTest {
        protected TLExtendedMeasureTest(Group parent, String nodeName, String description) {
            super(parent, nodeName, description);
        }

        public Context createContext() {
            return new TLExContext();
        }
    }

    public static class TLGetCaretInfo extends TLExtendedMeasureTest {
        public TLGetCaretInfo() {
            super(measuretestroot, "tlGetCaretInfo", "Measuring TL caret info");
        }

        public void runTest(Object ctx, int numReps) {
            TLExContext tlctx = (TLExContext)ctx;
            TextLayout tl = tlctx.tl;
            TextHitInfo[] hits = tlctx.hits;
            do {
                for (int i = 0; i < hits.length; ++i) {
                    tl.getCaretInfo(hits[i]);
                }
            } while (--numReps >= 0);
        }
    }

    public static class TLGetNextHit extends TLExtendedMeasureTest {
        public TLGetNextHit() {
            super(measuretestroot, "tlGetNextHit", "Measuring TL getNextRight/LeftHit");
        }

        public void runTest(Object ctx, int numReps) {
            TLExContext tlctx = (TLExContext)ctx;
            TextLayout tl = tlctx.tl;
            TextHitInfo[] hits = tlctx.hits;
            TextHitInfo hit;
            do {
                for (int i = 0; i < hits.length; ++i) {
                    hit = tl.getNextLeftHit(hits[i]);
                }
            } while (--numReps >= 0);
        }
    }

    public static class TLGetCaretShape extends TLExtendedMeasureTest {
        public TLGetCaretShape() {
            super(measuretestroot, "tlGetCaretShape", "Measuring TL getCaretShape");
        }

        public void runTest(Object ctx, int numReps) {
            TLExContext tlctx = (TLExContext)ctx;
            TextLayout tl = tlctx.tl;
            TextHitInfo[] hits = tlctx.hits;
            Shape s;
            do {
                for (int i = 0; i < hits.length; ++i) {
                    s = tl.getCaretShape(hits[i]);
                }
            } while (--numReps >= 0);
        }
    }

    public static class TLGetLogicalHighlightShape extends TLExtendedMeasureTest {
        public TLGetLogicalHighlightShape() {
            super(measuretestroot, "tlGetLogicalHighlightShape", "Measuring TL getLogicalHighlightShape");
        }

        public void runTest(Object ctx, int numReps) {
            TLExContext tlctx = (TLExContext)ctx;
            TextLayout tl = tlctx.tl;
            int len = tlctx.text.length();
            Rectangle2D lb = tlctx.lb;
            Shape s;
            if (len < 3) {
                do {
                    s = tl.getLogicalHighlightShape(0, len, lb);
                } while (--numReps >= 0);
            } else {
                do {
                    for (int i = 3; i < len; ++i) {
                        s = tl.getLogicalHighlightShape(i-3, i, lb);
                    }
                } while (--numReps >= 0);
            }
        }
    }

    public static class TLGetVisualHighlightShape extends TLExtendedMeasureTest {
        public TLGetVisualHighlightShape() {
            super(measuretestroot, "tlGetVisualHighlightShape", "Measuring TL getVisualHighlightShape");
        }

        public void runTest(Object ctx, int numReps) {
            TLExContext tlctx = (TLExContext)ctx;
            TextLayout tl = tlctx.tl;
            TextHitInfo[] hits = tlctx.hits;
            Rectangle2D lb = tlctx.lb;
            Shape s;
            if (hits.length < 3) {
                do {
                    s = tl.getVisualHighlightShape(hits[0], hits[hits.length - 1], lb);
                } while (--numReps >= 0);
            } else {
                do {
                    for (int i = 3; i < hits.length; ++i) {
                        s = tl.getVisualHighlightShape(hits[i-3], hits[i], lb);
                    }
                } while (--numReps >= 0);
            }
        }
    }

    public static class TLHitTest extends TLExtendedMeasureTest {
        public TLHitTest() {
            super(measuretestroot, "tlHitTest", "Measuring TL hitTest");
        }

        public void runTest(Object ctx, int numReps) {
            TLExContext tlctx = (TLExContext)ctx;
            TextLayout tl = tlctx.tl;
            int numhits = tlctx.hits.length;
            Rectangle2D lb = tlctx.lb;
            TextHitInfo hit;
            for (int i = 0; i <= numhits; ++i) {
                float x = (float)(lb.getMinX() + lb.getWidth() * i / numhits);
                float y = (float)(lb.getMinY() + lb.getHeight() * i / numhits);
                hit = tl.hitTestChar(x, y, lb);
            }
        }
    }

    public static class TLOutline extends TLMeasureTest {
        public TLOutline() {
            super(measuretestroot, "tlOutline", "Measuring TL outline");
        }

        public void runTest(Object ctx, int numReps) {
            TLContext tlctx = (TLContext)ctx;
            TextLayout tl = tlctx.tl;
            Shape s;
            do {
                s = tl.getOutline(null);
            } while (--numReps >= 0);
        }
    }
}
