/* Copyright (C) 2005-2011 Fabio Riccardi */
/* Copyright (C) 2017-     Masahiro Kitagawa */

package com.lightcrafts.ui.crop;

import com.lightcrafts.model.CropBounds;
import static com.lightcrafts.ui.crop.Locale.LOCALE;

import java.awt.geom.Point2D;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

class AspectConstraint {

    private static final String NoConstraintName =
        LOCALE.get("NoConstraintName");

    private int numerator;
    private int denominator;
    private String name;
    private String description;

    private static final Pattern pattern =
            Pattern.compile("([0-9]+) x ([0-9]+)");

    // The default constructor makes a no-constraint object that accepts
    // any rectangle.
    AspectConstraint() {
        name = NoConstraintName;
    }

    AspectConstraint(int numerator, int denominator) {
        this.numerator = numerator;
        this.denominator = denominator;
        name = denominator + " x " + numerator;
    }

    AspectConstraint(int numerator, int denominator, String description) {
        this(numerator, denominator);
        this.description = description;
    }

    AspectConstraint getInverse() {
        if (name.equals(NoConstraintName)) {
            return new AspectConstraint();
        }
        return new AspectConstraint(denominator, numerator);
    }

    String getNameWithDescription() {
        String s = String.format("%.2f", 1 / getAspectRatio()) + " | " + name;
        if (description != null && !description.isEmpty()) {
            s += " (" + description + ")";
        }
        return s;
    }

    @Override
    public String toString() {
        return name;
    }

    // Decode a String generated by toString().
    static AspectConstraint fromString(String s) {
        if (s.equals(NoConstraintName)) {
            return new AspectConstraint();
        }
        Matcher matcher = pattern.matcher(s);
        if (! matcher.matches()) {
            return null;
        }
        String numText = matcher.replaceAll("$1");
        String denText = matcher.replaceAll("$2");
        int num = Integer.parseInt(numText);
        int den = Integer.parseInt(denText);

        return new AspectConstraint(num, den);
    }

    double getAspectRatio() {
        return numerator / (double) denominator;
    }

    int getNumerator() {
        return numerator;
    }

    int getDenominator() {
        return denominator;
    }

    AspectConstraint transpose() {
        return new AspectConstraint(denominator, numerator);
    }

    // Make a new crop with the same center and area as the given
    // crop but having the constrained aspect ratio.
    CropBounds adjust(CropBounds bad) {
        if (isNoConstraint()) {
            return bad;
        }
        double ratio = getAspectRatio();
        double area = bad.getWidth() * bad.getHeight();
        Point2D center = bad.getCenter();
        double width = Math.sqrt(area / ratio);
        double height = Math.sqrt(area * ratio);
        double angle = bad.getAngle();
        CropBounds good = new CropBounds(
            center, width, height, angle
        );
        return good;
    }

    // Make a new crop like the given one but with left edge adjusted to
    // match the constrained aspect ratio.
    CropBounds adjustLeft(CropBounds bad) {
        if (isNoConstraint()) {
            return bad;
        }
        double ratio = getAspectRatio();
        double badW = bad.getWidth();
        double goodW = bad.getHeight() / ratio;
        double angle = bad.getAngle();
        double dx = - Math.cos(angle) * (goodW - badW) / 2;
        double dy = - Math.sin(angle) * (goodW - badW) / 2;
        Point2D center = bad.getCenter();
        center.setLocation(center.getX() + dx, center.getY() + dy);
        CropBounds good = new CropBounds(
            center, goodW, bad.getHeight(), angle
        );
        return good;
    }

    // Make a new crop like the given one but with top edge adjusted to
    // match the constrained aspect ratio.
    CropBounds adjustTop(CropBounds bad) {
        if (isNoConstraint()) {
            return bad;
        }
        double ratio = getAspectRatio();
        double badH = bad.getHeight();
        double goodH = bad.getWidth() * ratio;
        double angle = bad.getAngle();
        double dx = Math.sin(angle) * (goodH - badH) / 2;
        double dy = - Math.cos(angle) * (goodH - badH) / 2;
        Point2D center = bad.getCenter();
        center.setLocation(center.getX() + dx, center.getY() + dy);
        CropBounds good = new CropBounds(
            center, bad.getWidth(), goodH, angle
        );
        return good;
    }

    // Make a new crop like the given one but with right edge adjusted to
    // match the constrained aspect ratio.
    CropBounds adjustRight(CropBounds bad) {
        if (isNoConstraint()) {
            return bad;
        }
        double ratio = getAspectRatio();
        double badW = bad.getWidth();
        double goodW = bad.getHeight() / ratio;
        double angle = bad.getAngle();
        double dx = Math.cos(angle) * (goodW - badW) / 2;
        double dy = Math.sin(angle) * (goodW - badW) / 2;
        Point2D center = bad.getCenter();
        center.setLocation(center.getX() + dx, center.getY() + dy);
        CropBounds good = new CropBounds(
            center, goodW, bad.getHeight(), angle
        );
        return good;
    }

    // Make a new crop like the given one but with bottom edge adjusted to
    // match the constrained aspect ratio.
    CropBounds adjustBottom(CropBounds bad) {
        if (isNoConstraint()) {
            return bad;
        }
        double ratio = getAspectRatio();
        double badH = bad.getHeight();
        double goodH = bad.getWidth() * ratio;
        double angle = bad.getAngle();
        double dx = - Math.sin(angle) * (goodH - badH) / 2;
        double dy = Math.cos(angle) * (goodH - badH) / 2;
        Point2D center = bad.getCenter();
        center.setLocation(center.getX() + dx, center.getY() + dy);
        CropBounds good = new CropBounds(
            center, bad.getWidth(), goodH, angle
        );
        return good;
    }

    // Make a new crop like the given one but with width adjusted to
    // match the constrained aspect ratio.
    CropBounds adjustWidth(CropBounds bad) {
        if (isNoConstraint()) {
            return bad;
        }
        double ratio = getAspectRatio();
        Point2D center = bad.getCenter();
        double height = bad.getHeight();
        double width = height / ratio;
        double angle = bad.getAngle();
        CropBounds good = new CropBounds(
            center, width, height, angle
        );
        return good;
    }

    // Make a new crop like the given one but with height adjusted to
    // match the constrained aspect ratio.
    CropBounds adjustHeight(CropBounds bad) {
        if (isNoConstraint()) {
            return bad;
        }
        double ratio = getAspectRatio();
        Point2D center = bad.getCenter();
        double width = bad.getWidth();
        double height = width * ratio;
        double angle = bad.getAngle();
        CropBounds good = new CropBounds(
            center, width, height, angle
        );
        return good;
    }

    boolean isNoConstraint() {
        return ((numerator == 0) && (denominator == 0));
    }
}

@@ -1343,24 +1343,65 @@
 or);%0A    %7D%0A%0A
+    private String nameWithDescription;%0A%0A
     String g
@@ -1434,24 +1434,82 @@
         
-String s
+if (nameWithDescription == null) %7B%0A            nameWithDescription
  = Strin
@@ -1567,24 +1567,28 @@
 me;%0A        
+    
 if (descript
@@ -1640,17 +1640,39 @@
         
+    
-s
+nameWithDescription
  += %22 (%22
@@ -1693,16 +1693,30 @@
  + %22)%22;%0A
+            %7D%0A
         
@@ -1732,17 +1732,35 @@
  return 
-s
+nameWithDescription
 ;%0A    %7D%0A

Create String once and reuse it