package mara.mybox.controller;

import java.awt.geom.Arc2D;
import java.util.ArrayList;
import java.util.List;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.control.CheckBox;
import javafx.scene.control.CheckMenuItem;
import javafx.scene.control.MenuItem;
import javafx.scene.control.SeparatorMenuItem;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Arc;
import javafx.scene.shape.ArcType;
import javafx.scene.shape.Circle;
import javafx.scene.shape.CubicCurve;
import javafx.scene.shape.Ellipse;
import javafx.scene.shape.Line;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.Polyline;
import javafx.scene.shape.QuadCurve;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.SVGPath;
import javafx.scene.shape.Shape;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import mara.mybox.data.DoubleArc;
import mara.mybox.data.DoubleCircle;
import mara.mybox.data.DoubleCubic;
import mara.mybox.data.DoubleEllipse;
import mara.mybox.data.DoubleLine;
import mara.mybox.data.DoublePath;
import mara.mybox.data.DoublePathSegment;
import mara.mybox.data.DoublePoint;
import mara.mybox.data.DoublePolygon;
import mara.mybox.data.DoublePolyline;
import mara.mybox.data.DoublePolylines;
import mara.mybox.data.DoubleQuadratic;
import mara.mybox.data.DoubleRectangle;
import mara.mybox.data.DoubleShape;
import mara.mybox.data.ShapeStyle;
import mara.mybox.dev.MyBoxLog;
import mara.mybox.fxml.menu.MenuTools;
import static mara.mybox.fxml.style.NodeStyleTools.attributeTextStyle;
import mara.mybox.fxml.style.StyleTools;
import mara.mybox.tools.StringTools;
import static mara.mybox.value.Languages.message;
import mara.mybox.value.UserConfig;

/**
 * @Author Mara
 * @CreateDate 2021-8-10
 * @License Apache License Version 2.0
 */
public abstract class BaseShapeController_Base extends BaseImageController {

    // Only one shape is shown at one time
    protected DoubleRectangle maskRectangleData;
    protected DoubleCircle maskCircleData;
    protected DoubleEllipse maskEllipseData;
    protected DoubleLine maskLineData;
    protected DoublePolygon maskPolygonData;
    protected DoublePolyline maskPolylineData;
    protected DoublePolylines maskPolylinesData;
    protected DoubleQuadratic maskQuadraticData;
    protected DoubleCubic maskCubicData;
    protected DoubleArc maskArcData;
    protected DoublePath maskPathData;
    protected boolean maskControlDragged, showAnchors, popItemMenu, popShapeMenu,
            addPointWhenClick;
    protected AnchorShape anchorShape;
    protected Polyline currentLine;
    protected List<DoublePoint> currentLineData;
    protected List<Polyline> maskPolylines;
    protected DoublePoint lastPoint;

    protected ShapeStyle shapeStyle = null;
    public SimpleBooleanProperty maskShapeChanged = new SimpleBooleanProperty(false);
    public SimpleBooleanProperty maskShapeDataChanged = new SimpleBooleanProperty(false);

    public enum AnchorShape {
        Rectangle, Circle, Name
    }
    @FXML
    protected Rectangle maskRectangle;
    @FXML
    protected Circle maskCircle;
    @FXML
    protected Ellipse maskEllipse;
    @FXML
    protected Line maskLine;
    @FXML
    protected Polygon maskPolygon;
    @FXML
    protected Polyline maskPolyline;
    @FXML
    protected Arc maskArc;
    @FXML
    protected CubicCurve maskCubic;
    @FXML
    protected QuadCurve maskQuadratic;
    @FXML
    protected SVGPath maskSVGPath;
    @FXML
    protected CheckBox fillCheck, dashCheck, anchorCheck, shapeCanMoveCheck,
            popAnchorMenuCheck, popLineMenuCheck, addPointCheck;

    /*
        values
     */
    public Color strokeColor() {
        try {
            return shapeStyle == null
                    ? Color.web(UserConfig.getString(baseName + "StrokeColor", ShapeStyle.DefaultStrokeColor))
                    : shapeStyle.getStrokeColor();
        } catch (Exception e) {
            return Color.web(ShapeStyle.DefaultStrokeColor);
        }
    }

    public float strokeWidth() {
        float v = shapeStyle == null ? UserConfig.getFloat(baseName + "StrokeWidth", 2)
                : shapeStyle.getStrokeWidth() * (float) viewXRatio();
        if (v < 0) {
            v = 2;
        }
        return v;
    }

    public Color anchorColor() {
        try {
            return shapeStyle == null
                    ? Color.web(UserConfig.getString(baseName + "AnchorColor", ShapeStyle.DefaultAnchorColor))
                    : shapeStyle.getAnchorColor();
        } catch (Exception e) {
            return Color.web(ShapeStyle.DefaultAnchorColor);
        }
    }

    public float anchorSize() {
        float v = shapeStyle == null ? UserConfig.getFloat(baseName + "AnchorSize", 10)
                : shapeStyle.getAnchorSize();
        if (v < 0) {
            v = 10;
        }
        return v;
    }

    /*
        shapes
     */
    public boolean drawMaskShape() {
        if (isMaskRectangleShown()) {
            return drawMaskRectangle();
        } else if (isMaskCircleShown()) {
            return drawMaskCircle();
        } else if (isMaskEllipseShown()) {
            return drawMaskEllipse();
        } else if (isMaskLineShown()) {
            return drawMaskLine();
        } else if (isMaskPolygonShown()) {
            return drawMaskPolygon();
        } else if (isMaskPolylineShown()) {
            return drawMaskPolyline();
        } else if (isMaskPolylinesShown()) {
            return drawMaskPolylines();
        } else if (isMaskQuadraticShown()) {
            return drawMaskQuadratic();
        } else if (isMaskCubicShown()) {
            return drawMaskCubic();
        } else if (isMaskArcShown()) {
            return drawMaskArc();
        } else if (isMaskPathShown()) {
            return drawMaskPath();
        }
        return false;
    }

    // Any mask operations when pane size is changed
    public boolean redrawMaskShape() {
        return drawMaskShape();
    }

    public void setMaskShapesStyle() {
        if (isMaskRectangleShown()) {
            setShapeStyle(maskRectangle);
        } else if (isMaskCircleShown()) {
            setShapeStyle(maskCircle);
        } else if (isMaskEllipseShown()) {
            setShapeStyle(maskEllipse);
        } else if (isMaskLineShown()) {
            setShapeStyle(maskLine);
        } else if (isMaskPolygonShown()) {
            setShapeStyle(maskPolygon);
        } else if (isMaskPolylineShown()) {
            setShapeStyle(maskPolyline);
        } else if (isMaskPolylinesShown()) {
            for (Polyline line : maskPolylines) {
                setShapeStyle(line);
            }
        } else if (isMaskQuadraticShown()) {
            setShapeStyle(maskQuadratic);
        } else if (isMaskCubicShown()) {
            setShapeStyle(maskCubic);
        } else if (isMaskArcShown()) {
            setShapeStyle(maskArc);
        } else if (isMaskPathShown()) {
            setShapeStyle(maskSVGPath);
        }
        setMaskAnchorsStyle();
    }

    public void setShapeStyle(Shape shape) {
        try {
            if (shape == null || !shape.isVisible()) {
                return;
            }
            double strokeWidth = strokeWidth();
            shape.setStrokeWidth(strokeWidth);
            shape.setStroke(strokeColor());
            shape.getStrokeDashArray().clear();
            if (shapeStyle != null) {
                if (shapeStyle.isIsFillColor()) {
                    shape.setFill(shapeStyle.getFillColor());
                    shape.setOpacity(shapeStyle.getFillOpacity());
                } else {
                    shape.setFill(Color.TRANSPARENT);
                }
                shape.setStrokeLineCap(shapeStyle.getStrokeLineCap());
                if (shapeStyle.isIsStrokeDash() && shapeStyle.getStrokeDash() != null) {
                    shape.getStrokeDashArray().addAll(shapeStyle.getStrokeDash());
                }
            } else {
                shape.setFill(Color.TRANSPARENT);
                shape.getStrokeDashArray().addAll(strokeWidth, strokeWidth * 3);
            }
            if (isPickingColor) {
                shape.setCursor(Cursor.HAND);
            } else {
                shape.setCursor(defaultShapeCursor());
            }

        } catch (Exception e) {
            MyBoxLog.error(e);
        }
    }

    public void clearMask() {
        clearMaskShapes();
        clearMaskShapesData();
    }

    public void clearMaskShapes() {
        try {
            drawMaskRulers();
            clearMaskRectangle();
            clearMaskCircle();
            clearMaskEllipse();
            clearMaskLine();
            clearMaskPolyline();
            clearMaskPolygon();
            clearMaskPolylines();
            clearMaskQuadratic();
            clearMaskCubic();
            clearMaskArc();
            clearMaskPath();
            clearMaskAnchors();
            maskControlDragged = false;
        } catch (Exception e) {
            MyBoxLog.error(e);
        }
    }

    public void clearMaskShapesData() {
        try {
            clearMaskRectangleData();
            clearMaskCircleData();
            clearMaskEllipseData();
            clearMaskLineData();
            clearMaskPolylineData();
            clearMaskPolygonData();
            clearMaskPolylinesData();
            clearMaskQuadraticData();
            clearMaskCubicData();
            clearMaskArcData();
            clearMaskPathData();
        } catch (Exception e) {
            MyBoxLog.error(e);
        }
    }

    public void maskShapeChanged() {
        if (!isSettingValues && maskShapeChanged != null) {
            maskShapeChanged.set(!maskShapeChanged.get());
        }
    }

    public void maskShapeDataChanged() {
        drawMaskShape();
        notifyShapeDataChanged();
    }

    public void notifyShapeDataChanged() {
        if (!isSettingValues && maskShapeDataChanged != null) {
            maskShapeDataChanged.set(!maskShapeDataChanged.get());
        }
        maskControlDragged = false;
    }

    public void hideMaskShape() {
        try {
            if (isMaskPolylinesShown()) {
                for (Polyline line : maskPolylines) {
                    line.setOpacity(0);
                }
                if (currentLine != null) {
                    maskPane.getChildren().remove(currentLine);
                    currentLine = null;
                }
                lastPoint = null;
            } else {
                Shape shape = currentMaskShape();
                if (shape != null) {
                    shape.setOpacity(0);
                }
            }
        } catch (Exception e) {
            MyBoxLog.error(e);
        }
    }

    public Shape currentMaskShape() {
        if (isMaskRectangleShown()) {
            return maskRectangle;
        } else if (isMaskCircleShown()) {
            return maskCircle;
        } else if (isMaskEllipseShown()) {
            return maskEllipse;
        } else if (isMaskLineShown()) {
            return maskLine;
        } else if (isMaskPolygonShown()) {
            return maskPolygon;
        } else if (isMaskPolylineShown()) {
            return maskPolyline;
        } else if (isMaskQuadraticShown()) {
            return maskQuadratic;
        } else if (isMaskCubicShown()) {
            return maskCubic;
        } else if (isMaskArcShown()) {
            return maskArc;
        } else if (isMaskPathShown()) {
            return maskSVGPath;
        }
        return null;
    }

    public DoubleShape currentMaskShapeData() {
        if (isMaskRectangleShown()) {
            return maskRectangleData;
        } else if (isMaskCircleShown()) {
            return maskCircleData;
        } else if (isMaskEllipseShown()) {
            return maskEllipseData;
        } else if (isMaskLineShown()) {
            return maskLineData;
        } else if (isMaskPolygonShown()) {
            return maskPolygonData;
        } else if (isMaskPolylineShown()) {
            return maskPolylineData;
        } else if (isMaskPolylinesShown()) {
            return maskPolylinesData;
        } else if (isMaskQuadraticShown()) {
            return maskQuadraticData;
        } else if (isMaskCubicShown()) {
            return maskCubicData;
        } else if (isMaskArcShown()) {
            return maskArcData;
        } else if (isMaskPathShown()) {
            return maskPathData;
        }
        return null;
    }

    public DoubleShape shapeData(Shape shape) {
        if (shape == null) {
            return null;
        }
        if (shape == maskRectangle) {
            return maskRectangleData;
        } else if (shape == maskCircle) {
            return maskCircleData;
        } else if (shape == maskEllipse) {
            return maskEllipseData;
        } else if (shape == maskLine) {
            return maskLineData;
        } else if (shape == maskPolygon) {
            return maskPolygonData;
        } else if (shape == maskPolyline) {
            return maskPolylineData;
        } else if (shape == maskQuadratic) {
            return maskQuadraticData;
        } else if (shape == maskCubic) {
            return maskCubicData;
        } else if (shape == maskArc) {
            return maskArcData;
        } else if (shape == maskSVGPath) {
            return maskPathData;
        }
        return null;
    }

    /*
        anchor
        index: 0-based
     */
    public Node addMaskAnchor(int index, DoublePoint p, double x, double y) {
        return addMaskAnchor(index, "p" + (index + 1) + "", message("Point") + " " + (index + 1),
                p, x, y, defaultShapeCursor());
    }

    public Node addMaskAnchor(int index, String name, String title,
            DoublePoint p, double x, double y) {
        return addMaskAnchor(index, name, title, p, x, y, Cursor.HAND);
    }

    public Node addMaskAnchor(int index, String name, String title, DoublePoint p,
            double x, double y, Cursor cursor) {
        try {
            Node anchor;
            if (anchorShape == AnchorShape.Name) {
                Text text = new Text(name == null || name.isBlank() ? "p" + index : name);
                text.setX(x - anchorSize() * 0.5);
                text.setY(y);
                anchor = text;
            } else if (anchorShape == AnchorShape.Circle) {
                Circle circle = new Circle();
                circle.setCenterX(x);
                circle.setCenterY(y);
                anchor = circle;
            } else {
                double anchorHW = anchorSize() * 0.5;
                Rectangle rect = new Rectangle();
                rect.setX(x - anchorHW);
                rect.setY(y - anchorHW);
                anchor = rect;
            }
            anchor.setLayoutX(imageView.getLayoutX());
            anchor.setLayoutY(imageView.getLayoutY());
            setAnchorStyle(anchor);

            anchor.setId("MaskShapeAnchor" + index + (name != null ? "_" + name : ""));
            anchor.setOnMousePressed(new EventHandler<MouseEvent>() {
                @Override
                public void handle(MouseEvent event) {
                    controlPressed(event);
                    if (isPickingColor) {
                        return;
                    }
                    maskControlDragged = true;
                }
            });
            anchor.setOnMouseReleased(new EventHandler<MouseEvent>() {
                @Override
                public void handle(MouseEvent event) {
                    scrollPane.setPannable(true);
                    if (isPickingColor) {
                        return;
                    }
                    maskControlDragged = true;
                    double nx = maskEventX(event);
                    double ny = maskEventY(event);
                    if (DoubleShape.changed(nx - p.getX(), ny - p.getY())) {
                        moveMaskAnchor(index, name, new DoublePoint(nx, ny));
                    }
                }
            });
            anchor.hoverProperty().addListener(new ChangeListener<Boolean>() {
                @Override
                public void changed(ObservableValue ov, Boolean oldValue, Boolean newValue) {
                    if (newValue) {
                        if (isPickingColor) {
                            anchor.setCursor(Cursor.HAND);
                        } else {
                            anchor.setCursor(cursor);
                            if (popItemMenu) {
                                popNodeMenu(anchor, maskAnchorMenu(index, name, title, p));
                            }
                        }
                    }
                }
            });

            maskPane.getChildren().add(anchor);

            return anchor;
        } catch (Exception e) {
            MyBoxLog.error(e);
            return null;
        }
    }

    public boolean canDeleteAnchor() {
        return isMaskPolylineShown() || isMaskPolygonShown();
    }

    protected List<MenuItem> maskAnchorMenu(int index, String name, String title, DoublePoint p) {
        try {
            List<MenuItem> items = MenuTools.initMenu(title + "\n" + StringTools.menuPrefix(p.text(2)), false);
            MenuItem menu = new MenuItem(message("EditAnchor"), StyleTools.getIconImageView("iconEdit.png"));
            menu.setOnAction((ActionEvent menuItemEvent) -> {
                PointInputController inputController = PointInputController.open(this, title, p);
                inputController.getNotify().addListener(new ChangeListener<Boolean>() {
                    @Override
                    public void changed(ObservableValue v, Boolean ov, Boolean nv) {
                        moveMaskAnchor(index, name, inputController.picked);
                        inputController.close();
                    }
                });
            });
            items.add(menu);

            if (canDeleteAnchor()) {
                menu = new MenuItem(message("DeleteAnchor"), StyleTools.getIconImageView("iconDelete.png"));
                menu.setOnAction((ActionEvent menuItemEvent) -> {
                    deleteMaskAnchor(index, name);
                });
                items.add(menu);
            }

            items.add(anchorMenuItem());

            items.add(moveShapeMenu());

            if (isMaskPolygonShown() || isMaskPolylineShown()) {
                items.add(addPointMenu());
            }

            items.add(optionsMenu());

            items.add(new SeparatorMenuItem());

            return items;

        } catch (Exception e) {
            MyBoxLog.error(e);
            return null;
        }
    }

    public MenuItem anchorShowItem() {
        CheckMenuItem anchorShowItem = new CheckMenuItem(message("ShowAnchors"), StyleTools.getIconImageView("iconAnchor.png"));
        anchorShowItem.setSelected(UserConfig.getBoolean(baseName + "ShowAnchor", true));
        anchorShowItem.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent cevent) {
                if (anchorCheck != null) {
                    anchorCheck.setSelected(anchorShowItem.isSelected());
                } else {
                    UserConfig.setBoolean(baseName + "ShowAnchor", anchorShowItem.isSelected());
                    showAnchors = anchorShowItem.isSelected();
                    setMaskAnchorsStyle();
                }
            }
        });
        return anchorShowItem;
    }

    public MenuItem anchorMenuItem() {
        CheckMenuItem anchorMenuItem = new CheckMenuItem(
                isMaskPolylinesShown() ? message("PopLineMenu") : message("PopAnchorMenu"),
                StyleTools.getIconImageView("iconShape.png"));
        anchorMenuItem.setSelected(UserConfig.getBoolean(baseName + "ItemPopMenu", true));
        anchorMenuItem.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent cevent) {
                if (popAnchorMenuCheck != null) {
                    popAnchorMenuCheck.setSelected(anchorMenuItem.isSelected());
                } else if (popLineMenuCheck != null) {
                    popLineMenuCheck.setSelected(anchorMenuItem.isSelected());
                } else {
                    UserConfig.setBoolean(baseName + "ItemPopMenu", anchorMenuItem.isSelected());
                    popItemMenu = anchorMenuItem.isSelected();
                }
            }
        });
        return anchorMenuItem;
    }

    public MenuItem optionsMenu() {
        MenuItem menu = new MenuItem(message("Options"), StyleTools.getIconImageView("iconOptions.png"));
        menu.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent mevent) {
                options();
            }
        });
        return menu;
    }

    public MenuItem moveShapeMenu() {
        CheckMenuItem moveMenuItem = new CheckMenuItem(message("ShapeCanMove"), StyleTools.getIconImageView("iconMove.png"));
        moveMenuItem.setSelected(UserConfig.getBoolean(baseName + "ShapeCanMove", true));
        moveMenuItem.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent cevent) {
                if (shapeCanMoveCheck != null) {
                    shapeCanMoveCheck.setSelected(moveMenuItem.isSelected());
                } else {
                    UserConfig.setBoolean(baseName + "ShapeCanMove", moveMenuItem.isSelected());
                }
            }
        });
        return moveMenuItem;
    }

    public MenuItem addPointMenu() {
        CheckMenuItem pointMenuItem = new CheckMenuItem(message("AddPointWhenLeftClick"), StyleTools.getIconImageView("iconNewItem.png"));
        pointMenuItem.setSelected(UserConfig.getBoolean(baseName + "AddPointWhenLeftClick", true));
        pointMenuItem.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent cevent) {
                if (addPointCheck != null) {
                    addPointCheck.setSelected(pointMenuItem.isSelected());
                } else {
                    UserConfig.setBoolean(baseName + "AddPointWhenLeftClick", pointMenuItem.isSelected());
                    addPointWhenClick = pointMenuItem.isSelected();
                }
            }
        });
        return pointMenuItem;
    }

    public void moveMaskAnchor(int index, String name, DoublePoint p) {
        if (isMaskRectangleShown()) {
            moveMaskRectangleAnchor(index, p);

        } else if (isMaskCircleShown()) {
            moveMaskCircleAnchor(index, p);

        } else if (isMaskEllipseShown()) {
            moveMaskEllipseAnchor(index, p);

        } else if (isMaskLineShown()) {
            moveMaskLineAnchor(index, p);

        } else if (isMaskPolylineShown()) {
            maskPolylineData.set(index, p);
            maskShapeDataChanged();

        } else if (isMaskPolygonShown()) {
            maskPolygonData.set(index, p);
            maskShapeDataChanged();

        } else if (isMaskQuadraticShown()) {
            moveMaskQuadraticAnchor(index, p);

        } else if (isMaskCubicShown()) {
            moveMaskCubicAnchor(index, p);

        } else if (isMaskArcShown()) {
            moveMaskArcAnchor(index, p);

        } else if (isMaskPathShown()) {
            moveMaskPathAnchor(index, name, p);

        }

    }

    public void deleteMaskAnchor(int index, String name) {
        if (maskPolyline != null && maskPolyline.isVisible() && maskPolylineData != null) {
            maskPolylineData.remove(index);
            maskShapeDataChanged();

        } else if (maskPolygon != null && maskPolygon.isVisible() && maskPolygonData != null) {
            maskPolygonData.remove(index);
            maskShapeDataChanged();

        }
    }

    public void setAnchorStyle(Node anchor) {
        if (anchor == null) {
            return;
        }
        if (showAnchors) {
            Color anchorColor = anchorColor();
            float anchorSize = anchorSize();
            setAnchorStyle(anchor, anchorColor, anchorSize, new Font(anchorSize));
        } else {
            anchor.setVisible(false);
        }
    }

    public void setAnchorStyle(Node anchor, Color anchorColor, float anchorSize, Font font) {
        if (anchor == null) {
            return;
        }
        if (showAnchors) {
            if (anchor instanceof Rectangle) {
                Rectangle rect = (Rectangle) anchor;
                rect.setStrokeWidth(0);
                rect.setFill(anchorColor);
                rect.setWidth(anchorSize);
                rect.setHeight(anchorSize);

            } else if (anchor instanceof Circle) {
                Circle circle = (Circle) anchor;
                circle.setStrokeWidth(0);
                circle.setFill(anchorColor);
                circle.setRadius(anchorSize * 0.5);

            } else if (anchor instanceof Text) {
                Text text = (Text) anchor;
                text.setFill(anchorColor);
                text.setFont(font);

            }
            anchor.setVisible(true);
            anchor.toFront();
        } else {
            anchor.setVisible(false);
        }
    }

    public void setMaskAnchorsStyle() {
        try {
            if (maskPane == null) {
                return;
            }
            Color anchorColor = anchorColor();
            float anchorSize = anchorSize();
            Font font = new Font(anchorSize);
            List<Node> nodes = new ArrayList<>();
            nodes.addAll(maskPane.getChildren());
            for (Node node : nodes) {
                if (node == null || node.getId() == null) {
                    continue;
                }
                if (node.getId().startsWith("MaskShapeAnchor")) {
                    setAnchorStyle(node, anchorColor, anchorSize, font);
                }

            }
        } catch (Exception e) {
            MyBoxLog.error(e);
        }
    }

    public void clearMaskAnchors() {
        if (maskPane == null) {
            return;
        }
        List<Node> nodes = new ArrayList<>();
        nodes.addAll(maskPane.getChildren());
        for (Node node : nodes) {
            if (node == null || node.getId() == null) {
                continue;
            }
            if (node.getId().startsWith("MaskShapeAnchor")) {
                maskPane.getChildren().remove(node);
            }
        }
    }

    /*
        rectangle
     */
    public boolean isMaskRectangleShown() {
        return imageView != null && maskPane != null
                && maskRectangle != null && maskRectangle.isVisible()
                && maskRectangle != null;
    }

    public boolean showMaskRectangle() {
        if (imageView == null || maskPane == null || maskRectangle == null) {
            return false;
        }
        try {
            if (!maskPane.getChildren().contains(maskRectangle)) {
                maskPane.getChildren().addAll(maskRectangle);
            }
            maskRectangle.setOpacity(1);
            maskRectangle.setVisible(true);
            return drawMaskRectangle();
        } catch (Exception e) {
            MyBoxLog.error(e);
            return false;
        }
    }

    public void setMaskRectangleDefaultValues() {
        if (imageView == null || maskPane == null || maskRectangle == null) {
            return;
        }
        double w = imageWidth();
        double h = imageHeight();
        maskRectangleData = DoubleRectangle.xywh(w / 4, h / 4, w / 2, h / 2);
    }

    public boolean drawMaskRectangle() {
        try {
            if (maskRectangle == null
                    || !maskPane.getChildren().contains(maskRectangle)
                    || imageView == null || imageView.getImage() == null) {
                return false;
            }
            if (maskRectangleData == null) {
                setMaskRectangleDefaultValues();
            }
            double xRatio = viewXRatio();
            double yRatio = viewYRatio();
            double layoutX = imageView.getLayoutX();
            double layoutY = imageView.getLayoutY();
            double px1 = maskRectangleData.getX();
            double py1 = maskRectangleData.getY();
            double px2 = maskRectangleData.getMaxX();
            double py2 = maskRectangleData.getMaxY();
            double prx = maskRectangleData.getRoundx();
            double pry = maskRectangleData.getRoundy();
            double pcx = (px1 + px2) * 0.5;
            double pcy = (py1 + py2) * 0.5;
            double x1 = px1 * xRatio;
            double y1 = py1 * yRatio;
            double x2 = px2 * xRatio;
            double y2 = py2 * yRatio;
            double cx = pcx * xRatio;
            double cy = pcy * yRatio;
            double crx = prx * xRatio;
            double cry = pry * yRatio;
            maskRectangle.setLayoutX(layoutX);
            maskRectangle.setLayoutY(layoutY);
            maskRectangle.setX(x1);
            maskRectangle.setY(y1);
            maskRectangle.setWidth(x2 - x1);
            maskRectangle.setHeight(y2 - y1);
            maskRectangle.setArcWidth(crx);
            maskRectangle.setArcHeight(cry);

            setShapeStyle(maskRectangle);

            clearMaskAnchors();
            addMaskAnchor(1, message("LeftTop"), message("LeftTop"), new DoublePoint(px1, py1),
                    x1, y1, Cursor.NW_RESIZE);
            addMaskAnchor(2, message("TopCenter"), message("TopCenter"), new DoublePoint(pcx, py1),
                    cx, y1, Cursor.N_RESIZE);
            addMaskAnchor(3, message("RightTop"), message("RightTop"), new DoublePoint(px2, py1),
                    x2, y1, Cursor.NE_RESIZE);
            addMaskAnchor(4, message("LeftCenter"), message("LeftCenter"), new DoublePoint(px1, pcy),
                    x1, cy, Cursor.W_RESIZE);
            addMaskAnchor(5, message("RightCenter"), message("RightCenter"), new DoublePoint(px2, pcy),
                    x2, cy, Cursor.E_RESIZE);
            addMaskAnchor(6, message("LeftBottom"), message("LeftBottom"), new DoublePoint(px1, py2),
                    x1, y2, Cursor.SW_RESIZE);
            addMaskAnchor(7, message("BottomCenter"), message("BottomCenter"), new DoublePoint(pcx, py2),
                    cx, y2, Cursor.S_RESIZE);
            addMaskAnchor(8, message("RightBottom"), message("RightBottom"), new DoublePoint(px2, py2),
                    x2, y2, Cursor.SE_RESIZE);

            maskShapeChanged();

            updateLabelsTitle();
            return true;
        } catch (Exception e) {
            MyBoxLog.error(e);
            return false;
        }
    }

    public boolean moveMaskRectangleAnchor(int index, DoublePoint p) {
        double x = p.getX();
        double y = p.getY();
        double x1 = maskRectangleData.getX();
        double y1 = maskRectangleData.getY();
        double x2 = maskRectangleData.getMaxX();
        double y2 = maskRectangleData.getMaxY();
        double rx = maskRectangleData.getRoundx();
        double ry = maskRectangleData.getRoundy();
        switch (index) {
            case 1:
                maskRectangleData = DoubleRectangle.xy12(x, y, x2, y2);
                break;
            case 2:
                maskRectangleData = DoubleRectangle.xy12(x1, y, x2, y2);
                break;
            case 3:
                maskRectangleData = DoubleRectangle.xy12(x1, y, x, y2);
                break;
            case 4:
                maskRectangleData = DoubleRectangle.xy12(x, y1, x2, y2);
                break;
            case 5:
                maskRectangleData = DoubleRectangle.xy12(x1, y1, x, y2);
                break;
            case 6:
                maskRectangleData = DoubleRectangle.xy12(x, y1, x2, y);
                break;
            case 7:
                maskRectangleData = DoubleRectangle.xy12(x1, y1, x2, y);
                break;
            case 8:
                maskRectangleData = DoubleRectangle.xy12(x1, y1, x, y);
                break;
            default:
                return false;
        }
        maskRectangleData.setRoundx(rx);
        maskRectangleData.setRoundy(ry);
        maskShapeDataChanged();
        return true;
    }

    public void clearMaskRectangle() {
        try {
            if (imageView == null || maskPane == null || maskRectangle == null) {
                return;
            }
            maskPane.getChildren().removeAll(maskRectangle);
