// HTMLParser Library $Name: v1_6 $ - A java-based parser for HTML
// http://sourceforge.org/projects/htmlparser
// Copyright (C) 2003 Derrick Oswald
//
// Revision Control Information
//
// $Source: /cvsroot/htmlparser/htmlparser/src/org/htmlparser/lexerapplications/thumbelina/TileSet.java,v $
// $Author: derrickoswald $
// $Date: 2004/07/31 16:42:30 $
// $Revision: 1.2 $
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//

package org.htmlparser.lexerapplications.thumbelina;

import java.awt.Rectangle;
import java.util.Enumeration;
import java.util.Vector;

/**
 * Class to track picture regions.
 */
public class TileSet
/*
    extends
        java.awt.Canvas
    implements
        java.awt.event.ActionListener,
        java.awt.event.MouseListener,
        java.awt.event.WindowListener
*/
{
    /**
     * The list of Pictures.
     */
    protected Vector mRegions;

    /**
     * Construct a tile set.
     */
    public TileSet ()
    {
        mRegions = new Vector ();
    }

    /**
     * Get the number of tiles in this collection.
     * @return The number of pictures showing.
     * Note that the same image and URL may be showing
     * (different pieces) in several locations.
     */
    public int getSize ()
    {
        return (mRegions.size ());
    }

    /**
     * Get the list of pictures.
     * @return An enumeration over the picture objects in this set.
     */
    public Enumeration getPictures ()
    {
        return (mRegions.elements ());
    }

    /**
     * Add a single picture to the list.
     * @param r The picture to add.
     */
    public void add (final Picture r)
    {
        Vector regions; // this will be the new set
        Enumeration e;
        Picture rover;
        Rectangle intersection;
        Vector splits;
        Enumeration frags;

        regions = new Vector ();
        for (e = getPictures (); e.hasMoreElements (); )
        {
            rover = (Picture)e.nextElement ();
            if (rover.intersects (r))
            {
                intersection = rover.intersection (r);
                if (!intersection.equals (rover))
                {
                    // incoming lies completely within the existing picture
                    // or touches the existing picture somehow
                    splits = split (r, rover, false);
                    for (frags = splits.elements (); frags.hasMoreElements (); )
                        regions.addElement (frags.nextElement ());
                }
                else
                    // incoming covers existing... drop the existing picture
                    // but be sure to release the image memory
                    rover.setImage (null);
            }
            else
                // no conflict, keep the existing
                regions.addElement (rover);
        }
        regions.addElement (r);
        mRegions = regions;
    }

    /**
     * Split the large picture.
     * Strategy: split horizontally (full width strips top and bottom).
     * NOTE: top and bottom make sense only in terms of AWT coordinates.
     * @param small The incoming picture.
     * @param large The encompassing picture. The attributes of this one
     * are propagated to the fragments.
     * @param keep If <code>true</code>, the center area is kept,
     * otherwise discarded.
     * @return The fragments from the large picture.
     */
    private Vector split (
        final Picture small,
        final Picture large,
        final boolean keep)
    {
        Picture m;
        Vector ret;

        ret = new Vector ();

        if (large.intersects (small))
        {
            Rectangle intersection = large.intersection (small);

            // if tops don't match split off the top
            if ((intersection.y + intersection.height)
                != (large.y + large.height))
            {
                m = new Picture (large);
                m.y = (intersection.y + intersection.height);
                m.height = (large.y + large.height) - m.y;
                ret.addElement (m);
            }

            // if left sides don't match make a left fragment
            if (intersection.x != large.x)
            {
                m = new Picture (large);
                m.y = intersection.y;
                m.width = intersection.x - large.x;
                m.height = intersection.height;
                ret.addElement (m);
            }

            // the center bit
            if (keep)
            {
                m = new Picture (large);
                m.x = intersection.x;
                m.y = intersection.y;
                m.width = intersection.width;
                m.height = intersection.height;
                ret.addElement (m);
            }

            // if right sides don't match make a right fragment
            if ((intersection.x + intersection.width)
                != (large.x + large.width))
            {
                m = new Picture (large);
                m.x = intersection.x + intersection.width;
                m.y = intersection.y;
                m.width = (large.x + large.width) - m.x;
                m.height = intersection.height;
                ret.addElement (m);
            }

            // if bottoms don't match split off the bottom
            if (intersection.y != large.y)
            {
                m = new Picture (large);
                m.height = (intersection.y - large.y);
                ret.addElement (m);
            }
        }

        return (ret);
    }

    /**
     * Find the Picture at position x,y
     * @param x The x coordinate of the point to examine.
     * @param y The y coordinate of the point to examine.
     * @return The picture at that point, or <code>null</code>
     * if there are none.
     */
    public Picture pictureAt (final int x, final int y)
    {
        Picture m;
        Picture ret;

        ret = null;

        for (int i = 0; (null == ret) && (i < mRegions.size ()); i++)
        {
            m = (Picture)mRegions.elementAt (i);
            if (m.contains (x, y))
                ret = m;
        }

        return (ret);
    }

    /**
     * Move the given picture to the top of the Z order.
     * @param picture The picture to add.
     */
    public void bringToTop (final Picture picture)
    {
        Picture m;
        Picture ret;

        ret = null;

        for (int i = 0; (null == ret) && (i < mRegions.size ()); )
        {
            m = (Picture)mRegions.elementAt (i);
            if (picture.same (m))
                mRegions.removeElementAt (i);
            else
                i++;
        }
        add (picture);

    }

//    //
//    // Unit test.
//    //
//
//    // and need to add:
//    extends
//        java.awt.Canvas
//    implements
//        java.awt.event.ActionListener,
//        java.awt.event.MouseListener,
//        java.awt.event.WindowListener
//    // to the class definition
//
//    boolean mVerbose;
//    int mCounter;
//    java.awt.Point origin;
//    Rectangle last;
//    int type;
//
//  static java.awt.MenuBar menuMain;
//  static java.awt.Menu Options;
//  static java.awt.MenuItem repeat;
//  static java.awt.MenuItem clear;
//    static java.awt.TextField status;
//
//    // checks if adding the rectangle causes an overlap
//    boolean checkAdd (Rectangle r, Vector v)
//    {
//        Enumeration e;
//        boolean ret;
//        ret = false;
//
//        for (e = v.elements (); !ret && e.hasMoreElements (); )
//            ret = r.intersects ((Rectangle)e.nextElement ());
//
//        return (ret);
//    }
//
//    void paintwait ()
//    {
//        java.awt.Graphics g = getGraphics ();
//        if (null != g)
//            paint (g);
//        Thread.yield ();
//        try
//        {
//            Thread.sleep (1000);
//        }
//        catch (Exception exception)
//        {
//        }
//    }
//
//    void add ()
//    {
//        if (null != last)
//        {
//            Picture m = new Picture (last);
//            try
//            {
//                m.setURL (new URL ("http://localhost/image#" + mCounter++));
//            }
//            catch (java.net.MalformedURLException murle)
//            {
//                murle.printStackTrace ();
//            }
//            this.add (m);
//            repaint ();
//        }
//    }
//
//    //
//    // WindowListener interface
//    //
//    public void windowOpened (java.awt.event.WindowEvent e) {}
//    public void windowClosing (java.awt.event.WindowEvent e)
//    {
//        System.exit (0);
//    }
//    public void windowClosed (java.awt.event.WindowEvent e) {}
//    public void windowIconified (java.awt.event.WindowEvent e) {}
//    public void windowDeiconified (java.awt.event.WindowEvent e) {}
//    public void windowActivated (java.awt.event.WindowEvent e) {}
//    public void windowDeactivated (java.awt.event.WindowEvent e) {}
//
//    //
//  // ActionListener interface
//  //
//  public void actionPerformed (java.awt.event.ActionEvent event)
//  {
//      Object object = event.getSource();
//        if (object == repeat)
//            add ();
//        else if (object == clear)
//        {
//            mRegions = new Vector ();
//            repaint ();
//        }
//    }
//
//    //
//    // MouseListener Interface
//    //
//
//    public void mouseClicked (java.awt.event.MouseEvent event)
//    {
//        if (mVerbose)
//            System.out.println ("DrawTarget.mouseClicked " + event);
//    }
//
//  public void mouseReleased (java.awt.event.MouseEvent event)
//    {
//        if (mVerbose)
//            System.out.println ("DrawTarget.mouseReleased " + event);
//        if (null != origin)
//        {
//            last = new Rectangle (
//                Math.min (origin.x, event.getX ()),
//                Math.min (origin.y, event.getY ()),
//                Math.abs (event.getX () - origin.x),
//                Math.abs (event.getY () - origin.y));
//            add ();
//            origin = null;
//        }
//    }
//
//  public void mouseEntered (java.awt.event.MouseEvent event)
//    {
//        if (mVerbose)
//            System.out.println ("DrawTarget.mouseEntered " + event);
//    }
//
//  public void mouseExited (java.awt.event.MouseEvent event)
//    {
//        if (mVerbose)
//            System.out.println ("DrawTarget.mouseExited " + event);
//    }
//
//    public void mousePressed (java.awt.event.MouseEvent event)
//    {
//        if (mVerbose)
//            System.out.println ("DrawTarget.mousePressed " + event);
//        if (event.isMetaDown ())
//        {
//            status.setText (getDetails (event.getX (), event.getY ()));
//        }
//        else
//            origin = new java.awt.Point (event.getX (), event.getY ());
//    }
//
//    public void update (java.awt.Graphics graphics)
//    {
//        paint (graphics);
//    }
//
//    static final java.awt.Color[] mColours =
//    {
//        java.awt.Color.blue,
//        java.awt.Color.cyan,
//        java.awt.Color.gray,
//        java.awt.Color.green,
//        java.awt.Color.orange,
//        java.awt.Color.pink,
//        java.awt.Color.red,
//        java.awt.Color.yellow,
//        java.awt.Color.lightGray,
//        java.awt.Color.darkGray,
//    };
//
//    public void paint (java.awt.Graphics graphics)
//    {
//        java.awt.Dimension size = getSize ();
//        graphics.setColor (getBackground ());
//        graphics.fillRect (0, 0, size.width + 1, size.height + 1);
//
//        if (0 == mRegions.size ())
//        {
//            graphics.setColor (getForeground ());
//            graphics.drawString (
//                "Click and drag to create a picture.", 10, 20);
//            graphics.drawString (
//                "Right click a picture for details.", 10, 40);
//        }
//        else
//        {
//            Enumeration e = getPictures ();
//            while (e.hasMoreElements ())
//            {
//                Picture m = (Picture)e.nextElement ();
//                String url = m.getURL ().toExternalForm ();
//                int n = url.indexOf ('#');
//                n = Integer.parseInt (url.substring (n + 1))
//                java.awt.Color colour = mColours[n % mColours.length];
//                graphics.setColor (colour);
//                graphics.fillRect (m.x, m.y, m.width + 1, m.height + 1);
//                graphics.setColor (java.awt.Color.black);
//                graphics.drawRect (m.x, m.y, m.width, m.height);
//            }
//            checkOverlap (graphics);
//        }
//    }
//
//    void checkOverlap (java.awt.Graphics graphics)
//    {
//        Picture m;
//        Picture _m;
//        Rectangle r;
//
//        graphics.setColor (java.awt.Color.magenta);
//        for (int i = 0; i < mRegions.size (); i++)
//        {
//            m = (Picture)mRegions.elementAt (i);
//            for (int j = i + 1; j < mRegions.size (); j++)
//            {
//                _m = (Picture)mRegions.elementAt (j);
//                if (m.intersects (_m))
//                {
//                    r = m.intersection (_m);
//                    System.out.println (
//                        "overlap ("
//                        + r.x
//                        + ","
//                        + r.y
//                        + ") ("
//                        + (r.x + r.width)
//                        + ","
//                        + (r.y + r.height)
//                        + ")");
//                    graphics.fillRect (r.x, r.y, r.width + 1, r.height + 1);
//                }
//            }
//        }
//    }
//
//    String getDetails (int x, int y)
//    {
//        Picture m;
//        String ret;
//
//        ret = null;
//
//        // find the Picture
//        for (int i = 0; (null == ret) && (i < mRegions.size ()); i++)
//        {
//            m = (Picture)mRegions.elementAt (i);
//            if (m.contains (x, y))
//                ret = m.toString ();
//        }
//        if (null == ret)
//            ret = "";
//
//        return (ret);
//    }
//
//    public static void main (String[] args)
//    {
//        java.awt.Frame frame;
//
//        frame = new java.awt.Frame ();
//        frame.setSize (400,400);
//      menuMain = new java.awt.MenuBar();
//      Options = new java.awt.Menu ("Options");
//        repeat = new java.awt.MenuItem("Repeat");
//      Options.add (repeat);
//      clear = new java.awt.MenuItem("Clear");
//      Options.add (clear);
//
//      menuMain.add (Options);
//      frame.setMenuBar (menuMain);
//
//        java.awt.Insets insets = frame.getInsets ();
//
//        TileSet buffy = new TileSet ();
//        buffy.setLocation (insets.left + 10, insets.top + 10);
//        buffy.setBackground (java.awt.Color.lightGray.brighter ());
//        buffy.setVisible (true);
//
//        frame.add (buffy, "Center");
//        status = new java.awt.TextField ();
//        frame.add (status, "South");
//
//        frame.addWindowListener (buffy);
//        buffy.addMouseListener (buffy);
//      repeat.addActionListener (buffy);
//      clear.addActionListener (buffy);
//
//        frame.setVisible (true);
//
//    }
}

/*
 * Revision Control Modification History
 *
 * $Log: TileSet.java,v $
 * Revision 1.2  2004/07/31 16:42:30  derrickoswald
 * Remove unused variables and other fixes exposed by turning on compiler warnings.
 *
 * Revision 1.1  2003/09/21 18:20:56  derrickoswald
 * Thumbelina
 * Created a lexer GUI application to extract images behind thumbnails.
 * Added a task in the ant build script - thumbelina - to create the jar file.
 * You need JDK 1.4.x to build it.  It can be run on JDK 1.3.x in crippled mode.
 * Usage: java -Xmx256M thumbelina.jar [URL]
 *
 *
 */
