package shapeUtil;

import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.swing.border.Border.*;
import DrawTop.*;
import shape.*;
import geomExtension.*;
import menu.*;
import util.*;


public class Rotate extends JDialog implements ConnectionListener{

    RotateAction action=null;
    JLabel messageLabel=null;
    Dimension panelSize=new Dimension(220, 44);
    JRadioButton pixelButton=null;
    JRadioButton mmButton=null;
    JSpinner[] positionSpinners=new JSpinner[2];
    SpinnerNumberModel[] positionSpinnerModels=new SpinnerNumberModel[2];
    JCheckBox checkBox=null;
    JSpinner angleSpinner=null;
    SpinnerNumberModel angleSpinnerModel=null;
    JRadioButton copyButton=null;
    JButton goButton=null;
    JButton cancelButton=null;
    Vector shapesVector=new Vector();
    Point2D rotationCenterP=null;
    int debug=0;
    
    
    public Rotate(){
        super(ObjectTable.getDrawMain(), "rotate");
        this.setName("rotate");  
        this.action=new RotateAction(this);
        createDialog();
    }
    
    public void createDialog(){
        Component owner=this.getOwner();
        Point centerP=new Point(owner.getX()+10, owner.getY()+80);
        this.setLocation(centerP);
        this.addWindowListener(this.action);
        Container contentPane=this.getContentPane();
        Box box=Box.createVerticalBox();
        contentPane.add(box);

    // MessagePanel
        JPanel messagePanel=new JPanel();
        messagePanel.setLayout(new FlowLayout(FlowLayout.LEADING));
        this.messageLabel=new JLabel(" ");
        messagePanel.add(this.messageLabel);
    //uni Panel   
        JPanel unitPanel=new JPanel();
        unitPanel.setPreferredSize(this.panelSize);
        TitledBorder frameBorder=BorderFactory.createTitledBorder("Unit of length");
        GridLayout unitPanelLayout=new GridLayout(1,2);
        unitPanelLayout.setHgap(20);
        unitPanelLayout.setVgap(5);
        unitPanel.setLayout(unitPanelLayout);
        unitPanel.setBorder(frameBorder);
        ButtonGroup group = new ButtonGroup();
        this.pixelButton = new JRadioButton("pixel");
        this.mmButton = new JRadioButton("mm");
        group.add(this.mmButton);
        group.add(this.pixelButton);
        this.pixelButton.setActionCommand("pixel");
        this.mmButton.setActionCommand("mm");
        this.pixelButton.addActionListener(this.action);
        this.mmButton.addActionListener(this.action);
        unitPanel.add(mmButton);
        unitPanel.add(pixelButton);
        this.mmButton.setSelected(true);

      //Center position Panel    
        JPanel centerPositionPanel=new JPanel();
        frameBorder=BorderFactory.createTitledBorder("rotatation center x,y ");
        //frameBorder=BorderFactory.createTitledBorder("Size (mm)");
        frameBorder.setTitleColor(Color.BLACK);
        frameBorder.setTitleFont(MenuConstants.MenuFont);
        centerPositionPanel.setBorder(frameBorder);
        GridLayout centerPositionPanelLayout=new GridLayout(2,1);
        centerPositionPanelLayout.setHgap(20);
        centerPositionPanelLayout.setVgap(0);
        centerPositionPanel.setLayout(centerPositionPanelLayout);
      // xy panel  
        JPanel xyPanel=new JPanel();
        GridLayout xyPanelLayout=new GridLayout(1,2);
        xyPanelLayout.setHgap(20);
        xyPanelLayout.setVgap(0);
        xyPanel.setLayout(xyPanelLayout);
        JPanel[] xyPanels=new JPanel[2];
        String[] xyLabel={"x: ", "y: "};
        RotateSpinnerListener spinnerListener=new RotateSpinnerListener(this);
        Dimension spinnerSize=new Dimension(80,22);
        for(int i=0;i<2;i++){
            xyPanels[i]=new JPanel();
            xyPanels[i].setLayout(new BoxLayout(xyPanels[i],BoxLayout.X_AXIS));
            JLabel label=new JLabel(xyLabel[i]);
            xyPanels[i].add(label);
            this.positionSpinnerModels[i]=new SpinnerNumberModel(0d, 0d, 1000d, 1d);
            this.positionSpinners[i]=new JSpinner(positionSpinnerModels[i]);
            this.positionSpinners[i].addChangeListener(spinnerListener);
            this.positionSpinners[i].setPreferredSize(spinnerSize);
            xyPanels[i].add(this.positionSpinners[i]);
            xyPanel.add(xyPanels[i]);
        }
        centerPositionPanel.add(xyPanel);
      // checkbox panel
        JPanel checkBoxPanel=new JPanel();
        checkBoxPanel.setLayout(new BoxLayout(checkBoxPanel,BoxLayout.X_AXIS));
        JLabel checBoxLabel=new JLabel(": click the rotation center ");
        this.checkBox=new JCheckBox();
        checkBoxPanel.add(this.checkBox);
        checkBoxPanel.add(checBoxLabel);
        this.checkBox.addActionListener(this.action);
        this.checkBox.setActionCommand("click");
        centerPositionPanel.add(checkBoxPanel);
        
    //Rotation angle Panel    
        JPanel anglePanel=new JPanel();
        frameBorder=BorderFactory.createTitledBorder("rotation angle (degree)");
        frameBorder.setTitleColor(Color.BLACK);
        frameBorder.setTitleFont(MenuConstants.MenuFont);
        anglePanel.setBorder(frameBorder);
        JLabel angleLabel=new JLabel("angle: ");
        angleLabel.setPreferredSize(new Dimension(60,22));
        this.angleSpinnerModel=new SpinnerNumberModel(0d, -360d, 360d, 1d);
        this.angleSpinner=new JSpinner(angleSpinnerModel);
        this.angleSpinner.addChangeListener(spinnerListener);
        anglePanel.setLayout(new GridLayout(1,2));
        anglePanel.add(angleLabel);
        anglePanel.add(this.angleSpinner);
        //anglePanel.setPreferredSize(new Dimension(70,54));
        this.angleSpinner.setPreferredSize(new Dimension(60,22));

     //Copy Panel
        JPanel copyPanel=new JPanel();
        copyPanel.setPreferredSize(this.panelSize);
        frameBorder=BorderFactory.createTitledBorder("copy");
        copyPanel.setBorder(frameBorder);
        GridLayout copyPanelLayout=new GridLayout(1,2);
        copyPanelLayout.setHgap(20);
        copyPanelLayout.setVgap(0);
        copyPanel.setLayout(copyPanelLayout);
        group = new ButtonGroup();
        this.copyButton = new JRadioButton("yes");
        JRadioButton noCopyButton = new JRadioButton("no");
       // this.copyButton.setPreferredSize(buttonSize);
        //noCopyButton.setPreferredSize(buttonSize);
        group.add(copyButton);
        group.add(noCopyButton);
        this.copyButton.setSelected(true);
        copyPanel.add(copyButton);
        copyPanel.add(noCopyButton);
        
    //Go, OK, Cancel Button    
        JPanel buttonPanel=new JPanel();
        buttonPanel.setPreferredSize(this.panelSize);
        this.goButton=new JButton("GO !");
        this.cancelButton=new JButton("Cancel");
        Dimension buttonSize=new Dimension(80,22);
        this.goButton.setPreferredSize(buttonSize);
        this.cancelButton.setPreferredSize(buttonSize);
        this.goButton.setEnabled(false);
        this.goButton.setActionCommand("Go");
        this.cancelButton.setActionCommand("Cancel");
        this.goButton.addActionListener(action);
        this.cancelButton.addActionListener(action);

        buttonPanel.add(this.goButton);
        buttonPanel.add(this.cancelButton);
        
// layout the dialog
        box.add(Box.createVerticalStrut(5));
        box.add(messagePanel);
        box.add(Box.createVerticalStrut(5));
        box.add(unitPanel);
        box.add(Box.createVerticalStrut(5));
        box.add(centerPositionPanel);
        box.add(Box.createVerticalStrut(5));
        box.add(anglePanel);
        box.add(Box.createVerticalStrut(5));
        box.add(copyPanel);
        box.add(Box.createVerticalStrut(5));
        box.add(buttonPanel);
    }
    
    public void showDialog(){
        Component owner=this.getOwner();
        Point centerP=new Point(owner.getX()+owner.getSize().width-250, owner.getY()+100);
        this.setLocation(centerP);
        if(debug>0) System.out.println("position spiner size="+positionSpinners[0].getSize());
        ContainerManager containerManager=ObjectTable.getContainerManager("");
        ShapeContainer[] containers=containerManager.getSelectedContainers();
        int size=0;
        if(containers!=null) size=containers.length;
        if(size==0){
            showMessage("Select shapes before this command !", Color.RED);
            this.shapesVector.clear();
            this.pack();
            this.setVisible(true);
            return;
        }
        this.shapesVector.clear();
        for(int i=0;i<size;i++) this.shapesVector.add(containers[i]);
        Vector vector=new Vector();
        for(int i=0;i<size;i++) {
            if(isTextBox(containers[i])) {
                vector.add(containers[i]);
                containers[i].setBlinking(true);
            }
        }
        int blinkingSize=vector.size();
        if(blinkingSize>0){
            BlinkingShape blinkingShape=ObjectTable.getBlinkingShape();
            blinkingShape.startBlinking();
            int result=0;
            String message1="Warning: Selected shape has text";
            String message2="Selected shape has comitted text.\n "
                    + "Rotated shape will lose the text\nContinue this process?";
            if(blinkingSize>1){
                message1="Warning: Selected shapes have text";
                message2="Selected shapes have comitted text.\n "
                    + "Rotated shape will lose the text\nContinue this process?";
            }
            showMessage(message1, Color.RED);
            int returnCode = JOptionPane.showConfirmDialog(ObjectTable.getDrawMain(),
                    message2, "", JOptionPane.YES_NO_OPTION);
            if (returnCode == JOptionPane.NO_OPTION) {
                result=-1;
            }

            for(int i=0;i<blinkingSize;i++){
                ShapeContainer container=(ShapeContainer)vector.get(i);
                container.setBlinking(false);
            }
            blinkingShape.stopBlinking();
            if(result!=0) return;
        }
        this.goButton.setEnabled(true);
        this.setRotationCenter();
        this.pack();
        this.setVisible(true);
    }
    
    private boolean isTextBox(ShapeContainer shapeContainer){
        boolean suitable=false;
        ShapeContainer[] containers=shapeContainer.getGroupedSingleShapeContainers();
        int size=containers.length;

        for(int i=0;i<size;i++){
            if (containers[i].isCommittedText()) suitable=true;
        }
        return suitable;
    }

   public void addConnectionListener(){
        ConnectionLS connectionLS=ObjectTable.getConnectionLS();
        connectionLS.activateListener(true);
        connectionLS.addConnectionListener(this);
        ObjectTable.getSelectionLS().end();
    }

    public void removeConnectionListener(){
        ConnectionLS connectionLS=ObjectTable.getConnectionLS();
        connectionLS.activateListener(false);
        ObjectTable.getSelectionLS().start();
    }

    public void connected(ConnectionEvent event){
        CurvePT curvePT=event.getCurvePT();
        this.rotationCenterP=curvePT.getP();
        this.setValuesToPositionSpinners();
    }
    
    protected void showMessage(String message, Color color){
        this.messageLabel.setForeground(color);
        this.messageLabel.setFont(DrawParameters.Font12Bold);
        this.messageLabel.setText(message);

    }

    public ShapeContainer[] getSelectedContainers(){
        int size=this.shapesVector.size();
        ShapeContainer[] containers=new ShapeContainer[size];
        for(int i=0;i<size;i++){
            containers[i]=(ShapeContainer)this.shapesVector.get(i);
        }
        return containers;
    }
    
    public void setRotationCenter(){
        int size=this.shapesVector.size();
        double xmin=1e+5, ymin=xmin, xmax=-xmin, ymax=-xmin;
        for(int i=0;i<size;i++){
            ShapeContainer container=(ShapeContainer)this.shapesVector.get(i);
            Rectangle2D boundingBox=container.getBoundingBox();
             double x=boundingBox.getX();
             double y=boundingBox.getY();
             double w=boundingBox.getWidth();
             double h=boundingBox.getHeight();
             if(x<xmin) xmin=x;
             if(y<ymin) ymin=y;
             if(x+w>xmax) xmax=x+w;
             if(y+h>ymax) ymax=y+h;
        }
        this.rotationCenterP=new Point2D.Double(0.5*(xmin+xmax), 0.5*(ymin+ymax));
        this.setValuesToPositionSpinners();
    }
    
    public void setValuesToPositionSpinners(){
        double centerX=this.rotationCenterP.getX();
        double centerY=this.rotationCenterP.getY();
        double mm=DrawParameters.InchToMM/DrawParameters.InchToPixels;
        double unit=1.0d;
        if(this.mmButton.isSelected()) unit=mm;
        centerX=centerX*unit;
        centerY=centerY*unit;
        this.positionSpinners[0].setValue(centerX);
        this.positionSpinners[1].setValue(centerY);
    }

    public JSpinner[] getPositionSpinners(){
        return this.positionSpinners;
    }
    
    public SpinnerNumberModel[] getPositionSpinnerModels(){
        return this.positionSpinnerModels;
    }
    
    public JSpinner getAngleSpinner(){
        return this.angleSpinner;
    }
    
    public SpinnerNumberModel getAngleSpinnerModel(){
        return this.angleSpinnerModel;
    }
    
    public JRadioButton getCopyButton(){
        return this.copyButton;
    } 
    
    public Vector getShapesVector(){
        return this.shapesVector;
    } 
    
} //end of DialogOfConnectCurves

//**** RotateAction ****
class RotateAction extends AbstractAction  implements WindowListener {
    Rotate dialog;
    int cutShapeContainerIndex=-1;
    ShapeContainer[] trimmedShapeContainers=null;
    int debug=0;
    
    public RotateAction(Rotate dialog){
        this.dialog=dialog;
    }
    
    public void actionPerformed(ActionEvent e) {

        String commandName=e.getActionCommand();
        String componentClassName=""; 
        componentClassName=e.getSource().getClass().getSimpleName();
        String componentName=((Component)e.getSource()).getName();
        if(debug>0) System.out.println("- RotateAction.actionPerformed  " +
                "commandName="+commandName+
                ", source component name="+componentName+
                ", source class simple name="+componentClassName);
        
        if(commandName.equals("click")){
            //System.out.println(" -- actionPerformed clicked");
            JCheckBox checkBox=this.dialog.checkBox;
            if(checkBox.isSelected()) {
                this.dialog.addConnectionListener();
            } else {
                this.dialog.removeConnectionListener();
                ObjectTable.getSelectionLS().start();
            }
        }
        
        if(commandName.equals("pixel")||commandName.equals("mm")){
            this.dialog.setValuesToPositionSpinners();
        }
        
        if(commandName.equals("Go")){
            //System.out.println(" -- actionPerformed Go");
            JRadioButton copyButton=this.dialog.getCopyButton();
            boolean copy=copyButton.isSelected();
            Vector shapesVector=this.dialog.getShapesVector();
            int size=shapesVector.size();
            if(size==0) return;
            ShapeContainer[] containers=new ShapeContainer[size];
            for(int i=0;i<size;i++) containers[i]=(ShapeContainer)shapesVector.get(i);
            ShapeContainer[] newContainers=new ShapeContainer[size];
            ContainerManager containerManager=ObjectTable.getContainerManager("");
            Matrix2D rotationMatrix=this.getRotationMatrix();
            if(rotationMatrix==null){
                this.dialog.showMessage("** Error: specify non-zero angle!!", Color.RED);
                return;
            }
          //-----------------------------------------------------------//
            containerManager.undoSetupStart(containers);
          //-----------------------------------------------------------//
            for(int i=0;i<size;i++) {
              //------------------------------------------------------//
                newContainers[i]=this.rotateContainer(containers[i], rotationMatrix, copy);
              //------------------------------------------------------//
                //newContainers[i].deleteTextBox();
                containers[i].setSelected(false);
                //containers[i].deleteTextBox();
                if(copy) {
                    newContainers[i].setSelected(true);
                    //newContainers[i].clearShapeIdWithChildren();
                    newContainers[i].setNewShapeId(containerManager, true);
                    containerManager.addContainer(newContainers[i]);
                } else {
                    newContainers[i].setChangeCode(UndoConstants.CONTAINER);
                }
            }
          //--------------------------------------------------------//  
            containerManager.undoSetupEnd();
          //--------------------------------------------------------//
            shapesVector.clear();
            for(int i=0;i<newContainers.length;i++) shapesVector.add(newContainers[i]);
            ObjectTable.getDrawPanel().repaint("rotate");
        }
        
        if(commandName.equals("Cancel")){
            closeDialog();
        }
    }//End of actionPerformed
    
    private ShapeContainer rotateContainer(ShapeContainer container, 
            Matrix2D rotationMatrix,boolean copy){
        //Matrix2D rotationMatrix=this.getRotationMatrix();
        if(rotationMatrix==null) return null;
        ShapeContainer[] children=null;
        ShapeContainer newContainer=container;
        if(copy) {
            newContainer=(ShapeContainer)container.clone();
        }
        if(newContainer.getContainerType()==ShapeContainer.SHAPE){
            newContainer.deleteTextBox();
            rotate(rotationMatrix, newContainer);
        } else{
            GroupElement groupElement=(GroupElement)newContainer.getElement();
            children=groupElement.getGroupedSingleShapeContainers();
            for(int j=0;j<children.length;j++){
                children[j].deleteTextBox();
                rotate(rotationMatrix, children[j]);
            }
        }
        return newContainer;
    }
    
    private void rotate(Matrix2D rotationMatrix, ShapeContainer container){
        ShapeElement element=container.getElement();
        Curve2D curve2D=element.getCurve2D();
        GeneralCurve2DE generalcurve=curve2D.convertToGeneralCurve2DE();
        if(debug>0) System.out.println(" Before Rotation generalcurve="+generalcurve.toString());
        int numseg=generalcurve.getNumOfSegments();
        Segment2D[]segments=new Segment2D[numseg];
        for(int i=0;i<numseg;i++) {
            Segment2D segment=(Segment2D)generalcurve.getSegment2D(i).clone();
            segments[i]=segment.transformSegment(rotationMatrix);
            //segments[i]=segment;
        }
        generalcurve.setData(segments);
        if(debug>0) System.out.println(" After Rotation generalcurve="+generalcurve.toString());
        Curve2D simplerCurve=generalcurve.getSimpleCurve2D();
        ShapeElement newElement=new GeneralCurveElement();
        if(simplerCurve.getType2DE()==Command.LINE) newElement=new LineElement();
        if(simplerCurve.getType2DE()==Command.POLYLINE) newElement=new PolylineElement();
        if(simplerCurve.getType2DE()==Command.CUBIC_CURVE) newElement=new CubicCurveElement();
        newElement.setCurve2D(simplerCurve);
        container.setElement(newElement);
        newElement.setShapeContainer(container);
    }
    
    private Matrix2D getRotationMatrix(){
        SpinnerNumberModel angleSpinnerModel
                =this.dialog.getAngleSpinnerModel();
        Point2D anchorP=this.dialog.rotationCenterP;
        double angle=angleSpinnerModel.getNumber().doubleValue();
        if(angle==0d) return null;
        angle=-angle*Math.PI/180.0d;
        Vector2D r0=new Vector2D(1,0);
        Vector2D r=new Vector2D(Math.cos(angle),Math.sin(angle));
        Matrix2D rotationMatrix=Matrix2D.getRotationMatrix(anchorP, r0, r);
        return rotationMatrix;
    }

    protected void closeDialog(){
        this.dialog.setVisible(false);
      // clear mark and "selected" on the selected curve
        DrawPanel drawPanel=ObjectTable.getDrawPanel("");
      //-------------------------------// 
        DrawShapeUtil.clearTempShape("Rotate");
        drawPanel.repaint();
      //-------------------------------// 
      //------------------------------// 
        this.dialog.setVisible(false);
      //------------------------------//  
        this.dialog.removeConnectionListener();
        ObjectTable.getSelectionLS().start();
        return;
    }
    
    public void windowActivated(WindowEvent e) {}
    public void windowClosed(WindowEvent e) {}
    public void windowClosing(WindowEvent e) {
        this.closeDialog();
    }
    public void windowDeactivated(WindowEvent e) {}
    public void windowDeiconified(WindowEvent e) {}
    public void windowIconified(WindowEvent e) {}
    public void windowOpened(WindowEvent e) {}

} // end of class

class RotateSpinnerListener implements ChangeListener{
    Rotate dialog=null;
    int debug=0;
    RotateSpinnerListener(Rotate dialog){
        this.dialog=dialog;
    }
    public void stateChanged(ChangeEvent e){
        JSpinner spinner=(JSpinner)e.getSource();
        if(spinner==this.dialog.positionSpinners[0]||
               spinner==this.dialog.positionSpinners[1]){
            SpinnerNumberModel[] positionSpinnerModels
                =this.dialog.getPositionSpinnerModels();
            double pixel=DrawParameters.InchToPixels/DrawParameters.InchToMM;
            double mm=DrawParameters.InchToMM/DrawParameters.InchToPixels;
            double unit=1.0d;
            if(this.dialog.mmButton.isSelected()) unit=pixel;
            double x=unit*positionSpinnerModels[0].getNumber().doubleValue();
            double y=unit*positionSpinnerModels[1].getNumber().doubleValue();
            this.dialog.rotationCenterP=new Point2D.Double(x,y);
            this.drawMarkAtCenterP();
        }
        if(spinner==this.dialog.angleSpinner){
            SpinnerNumberModel angleSpinnerModel
                =this.dialog.getAngleSpinnerModel();
            double angle=angleSpinnerModel.getNumber().doubleValue();
            if(angle==0d) this.dialog.showMessage("** Error: specify non-zero angle !!", Color.RED);
            else this.dialog.showMessage("Angle specified !", Color.BLUE);
        }
    }
    
    public void drawMarkAtCenterP(){
        DrawShapeUtil.clearTempShape("Rotate");
        ObjectTable.getDrawPanel().repaint();
        DrawShapeUtil.drawTempShape("Rotate", this.dialog.rotationCenterP,
                DrawParameters.Mark_SmallSize, "Rotation center", Color.RED);
    }
}
    