/*
 * @(#)Mutator.java    1.0  15 December 2003
 *
 * Copyright 2004
 * College of Computer and Information Science
 * Northeastern University
 * Boston, MA  02115
 *
 * The Java Power Tools software may be used for educational
 * purposes as long as this copyright notice is retained intact
 * at the top of all source files.
 *
 * To discuss possible commercial use of this software, 
 * contact Richard Rasala at Northeastern University, 
 * College of Computer and Information Science,
 * 617-373-2462 or rasala@ccs.neu.edu.
 *
 * The Java Power Tools software has been designed and built
 * in collaboration with Viera Proulx and Jeff Raab.
 *
 * Should this software be modified, the words "Modified from 
 * Original" must be included as a comment below this notice.
 *
 * All publication rights are retained.  This software or its 
 * documentation may not be published in any media either
 * in whole or in part without explicit permission.
 *
 * This software was created with support from Northeastern 
 * University and from NSF grant DUE-9950829.
 */

package edu.neu.ccs.gui;

import edu.neu.ccs.util.*;

import java.awt.geom.*;

/**
 * <P>Class <CODE>Mutator</CODE> encapsulates
 * an abstract class <CODE>Strategy</CODE> that defines a mutator strategy
 * for general <CODE>MutatablePaintable</CODE> objects
 * and
 * an abstract class <CODE>StrategyUsage</CODE> that decribes how to use a
 * mutator strategy for a <CODE>PaintableSequence</CODE>.</P>
 *
 * <P>This class also provides:</P>
 *
 * <UL>
 *   <LI>several static methods that construct <CODE>Strategy</CODE> objects,</LI>
 *   <LI>two constants that control the usage of <CODE>Strategy</CODE> objects.</LI>
 * </UL>
 *
 * <P>Class <CODE>Mutator</CODE> cannot itself be instantiated.</P>
 *
 * @see java.awt.geom.AffineTransform
 * @see TransformFactory
 * @author  Richard Rasala
 * @version 2.3
 * @since   2.3
 */
public class Mutator {
    
    /** Private constructor to prevent instantiation. */
    private Mutator() {}
    
    
    /**
     * Class <CODE>Strategy</CODE> encapsulates an algorithm that will produce
     * a mutator <CODE>AffineTransform</CODE> given the center of an associated
     * object.
     */
    public static abstract class Strategy {

        /**
         * <P>Returns a non-<CODE>null</CODE> mutator <CODE>AffineTransform</CODE>
         * given the center of an associated object.</P>
         *
         * <P>If the given center is <CODE>null</CODE> then the mutator returned
         * will be the identity transform.</P>
         *
         * @param  center the center for the mutator operation
         * @return a mutator transform
         */
        public final AffineTransform mutator(Point2D center) {
            if (center == null)
                return new AffineTransform();
            
            return mutator(center.getX(), center.getY());
        }
        
        
        /**
         * <P>Returns a non-<CODE>null</CODE> mutator <CODE>AffineTransform</CODE>
         * given the center (x, y) of an associated object.</P>
         *
         * @param  x the x-coordinate of the center for the mutator operation
         * @param  y the y-coordinate of the center for the mutator operation
         * @return a mutator transform
         */
        public abstract AffineTransform mutator(double x, double y);
    }    
    
    
    /**
     * Returns the Mutator.Strategy encapsulation of translate
     * in TransformFactory.
     *
     * @param  tx the x-coordinate of the translate
     * @param  ty the y-coordinate of the translate
     * @return the encapsulation of translate
     * @see    TransformFactory#translate(double, double)
     */
    public static Mutator.Strategy
        translate(final double tx, final double ty)
    {
        return new Mutator.Strategy() {
            public AffineTransform mutator(double x, double y) {
                return TransformFactory.translate(tx, ty);
            }
        };
    }
    
    
    /**
     * Returns the Mutator.Strategy encapsulation of rotate
     * in TransformFactory.
     *
     * @param  degrees the rotation angle in degrees
     * @return the encapsulation of rotate
     * @see    TransformFactory#rotate(double, double, double)
     */
    public static Mutator.Strategy
        rotate(final double degrees)
    {
        return new Mutator.Strategy() {
            public AffineTransform mutator(double x, double y) {
                return TransformFactory.rotate(x, y, degrees);
            }
        };
    }
    
    
    /**
     * Returns the Mutator.Strategy encapsulation of scale
     * in TransformFactory.
     *
     * @param  degrees the angle in degrees of the main scaling axis
     * @param  s the scale factor along the axis at angle degrees
     * @param  t the scale factor along the axis at angle degrees+90
     * @return the encapsulation of scale
     * @see    TransformFactory#scale(double, double, double, double, double)
     */
    public static Mutator.Strategy
        scale(final double degrees, final double s, final double t)
    {
        return new Mutator.Strategy() {
            public AffineTransform mutator(double x, double y) {
                return TransformFactory.scale(x, y, degrees, s, t);
            }
        };
    }
    
    
    /**
     * Returns the Mutator.Strategy encapsulation of shear
     * in TransformFactory.
     *
     * @param  degrees the angle in degrees of the fixed axis for shear
     * @param  s determines the amount of shear along lines parallel to
     *         the fixed axis for shear
     * @return the encapsulation of shear
     * @see    TransformFactory#shear(double, double, double, double)
     */
    public static Mutator.Strategy
        shear(final double degrees, final double s)
    {
        return new Mutator.Strategy() {
            public AffineTransform mutator(double x, double y) {
                return TransformFactory.shear(x, y, degrees, s);
            }
        };
    }
    
    
    /**
     * Returns the Mutator.Strategy encapsulation of reflect
     * in TransformFactory.
     *
     * @param  degrees the angle in degrees of the line of reflection
     * @return the encapsulation of reflect
     * @see    TransformFactory#reflect(double, double, double)
     */
    public static Mutator.Strategy
        reflect(final double degrees)
    {
        return new Mutator.Strategy() {
            public AffineTransform mutator(double x, double y) {
                return TransformFactory.reflect(x, y, degrees);
            }
        };
    }
    
    
    /**
     * Returns the Mutator.Strategy encapsulation of glidereflect
     * in TransformFactory.
     *
     * @param  degrees the angle in degrees of the line of glide reflection
     * @param  distance the translation distance along the line of glide reflection
     * @return the encapsulation of glidereflect
     * @see    TransformFactory#glidereflect(double, double, double, double)
     */
    public static Mutator.Strategy
        glidereflect(final double degrees, final double distance)
    {
        return new Mutator.Strategy() {
            public AffineTransform mutator(double x, double y) {
                return TransformFactory.glidereflect(x, y, degrees, distance);
            }
        };
    }
    
    
    /**
     * Returns the Mutator.Strategy encapsulation of centeredTransform
     * in TransformFactory.
     *
     * @param  m00 matrix coefficient for row 0 and column 0
     * @param  m10 matrix coefficient for row 1 and column 0
     * @param  m01 matrix coefficient for row 0 and column 1
     * @param  m11 matrix coefficient for row 1 and column 1
     * @param  m02 matrix coefficient for row 0 and column 2
     * @param  m12 matrix coefficient for row 1 and column 2
     * @return the encapsulation of centeredTransform
     * @see    TransformFactory#centeredTransform
     *         (double, double, double, double, double, double, double, double)
     */
    public static Mutator.Strategy
        centeredTransform
            (final double m00,
             final double m10,
             final double m01,
             final double m11,
             final double m02,
             final double m12)
    {
        return new Mutator.Strategy() {
            public AffineTransform mutator(double x, double y) {
                return TransformFactory.
                    centeredTransform(x, y, m00, m10, m01, m11, m02, m12);
            }
        };
    }
    
    
    /**
     * Returns the Mutator.Strategy encapsulation of a specific transform
     * that may be used independent of a center.
     *
     * @param  T the specific transform to encapsulate
     * @return the encapsulation of T
     */
    public static Mutator.Strategy
        transform(final AffineTransform T)
    {
        return new Mutator.Strategy() {
            public AffineTransform mutator(double x, double y) {
                return T;
            }
        };
    }
    
    
    /**
     * Returns the Mutator.Strategy encapsulation of randomTranslate
     * in TransformFactory.
     *
     * @param  maxshift the bound for the translation along each axis
     * @return the encapsulation of randomTranslate
     * @see    TransformFactory#randomTranslate(double)
     */
    public static Mutator.Strategy
        randomTranslate(final double maxshift)
    {
        return new Mutator.Strategy() {
            public AffineTransform mutator(double x, double y) {
                return TransformFactory.randomTranslate(maxshift);
            }
        };
    }
    
    
    /**
     * Returns the Mutator.Strategy encapsulation of randomRotate
     * in TransformFactory.
     *
     * @param  maxangle the bound for the rotation angle in degrees
     * @return the encapsulation of randomRotate
     * @see    TransformFactory#randomRotate(double, double, double)
     */
    public static Mutator.Strategy
        randomRotate(final double maxangle)
    {
        return new Mutator.Strategy() {
            public AffineTransform mutator(double x, double y) {
                return TransformFactory.randomRotate(x, y, maxangle);
            }
        };
    }
    
    
    /**
     * Returns the Mutator.Strategy encapsulation of randomTranslateRotate
     * in TransformFactory.
     *
     * @param  maxshift the bound for the translation along each axis
     * @param  maxangle the bound for the rotation angle in degrees
     * @return the encapsulation of randomTranslateRotate
     * @see    TransformFactory#randomTranslateRotate(double, double, double, double)
     */
    public static Mutator.Strategy
        randomTranslateRotate(final double maxshift, final double maxangle)
    {
        return new Mutator.Strategy() {
            public AffineTransform mutator(double x, double y) {
                return TransformFactory.
                    randomTranslateRotate(x, y, maxshift, maxangle);
            }
        };
    }
    
    
    /**
     * Returns the Mutator.Strategy encapsulation of randomScale
     * in TransformFactory.
     *
     * @param  degrees the angle in degrees of the main scaling axis
     * @param  maxdelta the bound relative to 1 of the scale factors
     * @return the encapsulation of randomScale
     * @see    TransformFactory#randomScale(double, double, double, double)
     */
    public static Mutator.Strategy
        randomScale(final double degrees, final double maxdelta)
    {
        return new Mutator.Strategy() {
            public AffineTransform mutator(double x, double y) {
                return TransformFactory.randomScale(x, y, degrees, maxdelta);
            }
        };
    }
    
    
    /**
     * Returns the Mutator.Strategy encapsulation of randomCenteredTransform
     * in TransformFactory.
     *
     * @param  maxshift the bound on the perturbation coefficients
     * @return the encapsulation of randomCenteredTransform
     * @see    TransformFactory#randomCenteredTransform(double, double, double)
     */
    public static Mutator.Strategy
        randomCenteredTransform(final double maxshift)
    {
        return new Mutator.Strategy() {
            public AffineTransform mutator(double x, double y) {
                return TransformFactory.randomCenteredTransform(x, y, maxshift);
            }
        };
    }
    
    
    /**
     * <P>Returns the composition of an array of Mutator.Strategy
     * objects into a Mutator.Strategy object.</P>
     *
     * <P>Given a center (x, y), the composite strategy computes
     * an <CODE>AffineTransform</CODE> as follows.</P>
     *
     * <P>For each strategy in the given list compute a transform
     * corresponding to the center (x, y).  Then compose these
     * transforms to produce the resulting transform.</P>
     *
     * @param  strategylist the strategy objects to compose
     * @return the composition of the strategy objects
     * @see    TransformFactory#compose(AffineTransform[])
     */
    public static Mutator.Strategy
        compose(Mutator.Strategy[] strategylist)
    {
        final Mutator.Strategy[] list
            = (strategylist == null)
            ? new Mutator.Strategy[0]
            : strategylist;
        
        return new Mutator.Strategy() {
            public AffineTransform mutator(double x, double y) {
                int N = list.length;
                
                AffineTransform[] transforms = new AffineTransform[N];
                
                for (int i = 0; i < N; i++)
                    transforms[i] =
                        (list[i] == null)
                        ? null
                        : list[i].mutator(x, y);
                
                return TransformFactory.compose(transforms);
            }
        };
    }
    
    
    /**
     * <P>Class <CODE>StrategyUsage</CODE> is used to define constants to be
     * used to control the usage of <CODE>Strategy</CODE> objects.</P>
     *
     * <P>Class <CODE>StrategyUsage</CODE> cannot be instantiated outside of
     * its package.</P>
     */
    public static class StrategyUsage {
        /** Package private constructor to prevent outside instantiation. */
        StrategyUsage() {}
    }
    
    
    /**
     * The constant <CODE>MUTATE_AS_GROUP</CODE> signifies that a
     * <CODE>Strategy</CODE> should mutate a collection as a group.
     *
     * @see #MUTATE_AS_ITEMS
     */
    public static final Mutator.StrategyUsage MUTATE_AS_GROUP = new StrategyUsage();
    
    
    /**
     * The constant <CODE>MUTATE_AS_ITEMS</CODE> signifies that a
     * <CODE>Strategy</CODE> should mutate a collection by acting
     * on the individual items of the collection.
     *
     * @see #MUTATE_AS_GROUP
     */
    public static final Mutator.StrategyUsage MUTATE_AS_ITEMS = new StrategyUsage();
    
}
