package shapeUtil;

import DrawTop.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
//import java.lang.reflect.*;
import shape.*;
import util.*;
import geomExtension.*;
//import DrawTop.*;


public class ConnectionLS implements MouseListener, MouseMotionListener{
    protected EventListenerList listenerList = new EventListenerList();  
    Point2D startPoint=null; 
    Point2D endPoint=null;
    Point2D oldPoint=null;
    CurvePT mouseHitPoint=null;
    boolean clicked=false;
    public int debug=0;
     
    public ConnectionLS(){}
    
    public boolean isMouseListener(MouseListener listener){
        ListenerPanel listenerPanel=ObjectTable.getListenerPanel();
        MouseListener[] mouseLS=listenerPanel.getMouseListeners();
        boolean isRegistered=false;
        for(int i=0;i<mouseLS.length;i++){
            if(listener.equals(mouseLS[i])) isRegistered=true;
        }
        return isRegistered;
    }
    
    public boolean isMouseMotionListener(MouseMotionListener listener){
        ListenerPanel listenerPanel=ObjectTable.getListenerPanel();
        MouseMotionListener[] mouseMotionLS=listenerPanel.getMouseMotionListeners();
        boolean isRegistered=false;
        for(int i=0;i<mouseMotionLS.length;i++){
            if(listener.equals(mouseMotionLS[i])) isRegistered=true;
        }
        return isRegistered;
    }
    
    public void showListeners(String message){
        ListenerPanel listenerPanel=ObjectTable.getListenerPanel();
        MouseListener[] mouseLS=listenerPanel.getMouseListeners();
        MouseMotionListener[] mouseMotionLS=listenerPanel.getMouseMotionListeners();
        String str=message;
        str+="\n num of MouseListeners="+mouseLS.length;
        for(int i=0;i<mouseLS.length;i++){
            str+="\n ["+i+"] "+mouseLS[i].toString();
        }
        str+="\n num of MouseMotionListeners="+mouseMotionLS.length;
        for(int i=0;i<mouseMotionLS.length;i++){
            str+="\n ["+i+"] "+mouseMotionLS[i].toString();
        }
        System.out.println(str);
    }
    
    public void activateListener(boolean activate){
        if(activate&&!this.isMouseListener(this)&&!this.isMouseMotionListener(this)){
            JComponent listenerPanel=ObjectTable.getListenerPanel();
            listenerPanel.addMouseListener(this);
            listenerPanel.addMouseMotionListener(this);
         }
         if(!activate&&this.isMouseListener(this)&&this.isMouseMotionListener(this)){
            JComponent listenerPanel=ObjectTable.getListenerPanel();
            listenerPanel.removeMouseListener(this);
            listenerPanel.removeMouseMotionListener(this);
         }
         if(debug>0) showListeners("ConnectionLS.activateListener end activate="+activate);
    }

    public void addConnectionListener(ConnectionListener listener) {
        this.listenerList.add(ConnectionListener.class, listener);
    }

    public void removeConnectionListener(ConnectionListener listener) {
        this.listenerList.remove(ConnectionListener.class, listener);
    }
    
    public void removeAllConnectionListeners() {
        this.listenerList=new EventListenerList();
    }
    
    public void fireEvent(ConnectionEvent event) {
        if(debug>0) showConnectionListeners("** ConnectionLS.fireEvent");
        Object[] listeners = this.listenerList.getListenerList();
        for (int i = 0; i < listeners.length; i = i + 2) {
            if (listeners[i] == ConnectionListener.class) {
                ((ConnectionListener) listeners[i + 1]).connected(event);
            }
        }
    }
    
    public void showConnectionListeners(String message) {
        Object[] listeners = this.listenerList.getListenerList();
        String str=message;
        str+="  num of ConnectionListeners="+listeners.length/2;
        for (int i = 0; i < listeners.length; i = i + 2) {
            str+="\n ["+i+"] "+listeners[i+1].toString();
        }
        System.out.println(str);
    }
    
    public void mousePressed(MouseEvent e) {
        if(debug>0) System.out.println("ConnectioLS.mousePressed");
        this.clicked=false;
        double scale=DrawParameters.getScale();
        double X = e.getX()/scale;
        double Y = e.getY()/scale;
        this.startPoint=new Point2D.Double(X,Y);
      //----------------------------------------------------------------------------//
        double tol=DrawParameters.ConnectionSmallTolerance;
        this.mouseHitPoint=this.drawMouseHitPT(this.startPoint, null, tol, tol, null);
      //---------------------------------------------------------------------------//
        if(this.mouseHitPoint!=null) this.startPoint=(Point2D)this.mouseHitPoint.getP().clone();

      //----------------------------------------------------//
        ObjectTable.getDrawPanel().repaint("ConnectioLS");
    }

    public void mouseDragged(MouseEvent e) {
        if(debug>0) System.out.println("ConnectioLS.mouseDragged");
        double scale=DrawParameters.getScale();
        double X = e.getX()/scale;
        double Y = e.getY()/scale;
        this.endPoint=new Point2D.Double(X,Y);
        Vector2D vec=Vector2D.sub(this.endPoint,this.startPoint);
        vec=Vector2D.unitVector(vec);
      //--------------------------------------------------------------------------//
        double tol=DrawParameters.ConnectionSmallTolerance;
        this.mouseHitPoint=this.drawMouseHitPT(this.endPoint, null, tol, tol, null);
      //--------------------------------------------------------------------------//
        if(this.mouseHitPoint!=null) this.endPoint=(Point2D)this.mouseHitPoint.getP().clone();
        this.oldPoint=(Point2D)this.endPoint.clone();
        ObjectTable.getDrawPanel().repaint("ConnectioLS");
    } //mouseDragged

    public void mouseReleased(MouseEvent e) {
        if(debug>0) System.out.println("ConnectioLS.mouseReleased");
        double scale=DrawParameters.getScale();
        double X = e.getX()/scale;
        double Y = e.getY()/scale;
        if(this.endPoint!=null){
            double dist=Vector2D.dist(this.startPoint, this.endPoint);
            if(dist>0d&&dist<=DrawParameters.ConnectionTolerance){
                //System.out.println("ConnectionPointLS.mouseReleased called");
                if(this.mouseHitPoint==null) {
                    CurvePT curvePT=new CurvePT(-1.0, (Point2D)this.startPoint.clone());
                    ConnectionEvent event=new ConnectionEvent(this, curvePT);
                    this.fireEvent(event);
                }else {
                    ConnectionEvent event=new ConnectionEvent(this, this.mouseHitPoint);
                    this.fireEvent(event);
                }
                this.clicked=true;
            }
        }
        this.startPoint=null;
        this.endPoint=null;
        ObjectTable.getDrawPanel().repaint("ConnectioLS");
    } 

    public void mouseMoved(MouseEvent e) {
        if(debug>0) System.out.println("\nConnectioLS.mouseMoved");
        double scale=DrawParameters.getScale();
        double X = e.getX()/scale;
        double Y = e.getY()/scale;
        Point2D mousePT=new Point2D.Double(X,Y);
      //--------------------------------------------------------------------//
        double tol=DrawParameters.ConnectionSmallTolerance;
        this.mouseHitPoint=this.drawMouseHitPT(mousePT, null, tol, tol, null);
      //--------------------------------------------------------------------//
        if(this.mouseHitPoint!=null){
            Cursor newCursor=DrawParameters.DEFAULT_CURSOR;
            ObjectTable.getDrawMain().setCursor(newCursor);
        }
        ObjectTable.getDrawPanel().repaint("ConnectioLS");
    } //mouseMoved


    public void mouseClicked(MouseEvent e) {
        if(debug>0) System.out.println("ConnectioLS.mouseClicked"+
                ",  this.clicked="+this.clicked);
        if(this.clicked) return;
        double scale=DrawParameters.getScale();
        double X = e.getX()/scale;
        double Y = e.getY()/scale;
        Point2D pt=new Point2D.Double(X, Y);
        CurvePT curvePT=new CurvePT(-1.0, pt);
        if(this.mouseHitPoint!=null) curvePT=this.mouseHitPoint;
        String str="ConnectioLS.mouseClicked  Clicked Pt="+Util.Pt(curvePT.getP());
        if(this.mouseHitPoint!=null) str+=" ; this is a point on a shape";
        if(debug>0) System.out.println(str);

        ConnectionEvent event=new ConnectionEvent(this, curvePT);
        this.fireEvent(event);
        this.clicked=true;
        ObjectTable.getDrawPanel().repaint("ConnectioLS");
    } //mouseClicked

    public void mouseEntered(MouseEvent e) {
        ObjectTable.getDrawMain().setCursor(DrawParameters.CROSSHAIR_CURSOR);
    }
    
    public void mouseExited(MouseEvent e) {
        ObjectTable.getDrawMain().setCursor(DrawParameters.DEFAULT_CURSOR);
    }

    public CurvePT drawMouseHitPT(int ctrl, Point2D mousePT, Vector2D vec,
            ShapeContainer movingContainer){
        double boundaryTol=DrawParameters.ConnectionTolerance;
        double pointTol=DrawParameters.ConnectionTolerance;
        CurvePT onlinePT=null;
        if(ctrl>0) {
             double pointTol0=0.1*pointTol;
             onlinePT = this.drawMouseHitPT(mousePT, vec,
                        boundaryTol, pointTol0, movingContainer);
        } else{
            onlinePT = this.drawMouseHitPT(mousePT, null,
                        boundaryTol, pointTol, movingContainer);
        }
        return onlinePT;
    }
    
    private CurvePT drawMouseHitPT(Point2D mousePT, Vector2D vec, double boundaryTol,
            double pointTol, ShapeContainer movingContainer){
        MousePositionLS mousePositionLS=ObjectTable.getMousePositionLS();
        MousePositionInfo[] infos=mousePositionLS.getAllMousePositionInfo();
        DrawShapeUtil.clearTempShape("ConnectionMark");
        double distMin=1.0e+5;
        CurvePT targetPT=null;
        ShapeContainer target=null;
        if(movingContainer!=null&&debug>0) {
            System.out.println("\ndrawMouseHitPT print mousePositionInfo >=SHAPE_BOUNDARY"
                +"movingContainer"+movingContainer.getShapeId());
        }
        int index=-1;
        for(int i=0;i<infos.length;i++){
            if(infos[i].getPosition()!=MousePositionInfo.SHAPE_BOUNDARY) continue;
            ShapeContainer targetContainer=infos[i].getContainer();
            ShapeContainer shapeContainerInGroup=infos[i].getShapeContainerInGroup();
          //------------------------------------------------------------------//  
            if(movingContainer!=null&&targetContainer.compare(movingContainer)) continue;
            if(targetContainer.getContainerType()==ShapeContainer.GROUP&&
                    shapeContainerInGroup!=null) targetContainer=shapeContainerInGroup;
            if(debug>0) System.out.println("   info: "+infos[i].toString()+
                    ",   targetContainer="+targetContainer.getShapeId());
          //---------------------------------------------------------------------------//
            CurvePT PT=this.getHitPT(mousePT, vec, boundaryTol, targetContainer);
          //---------------------------------------------------------------------------//
            if(PT==null) continue;
            double dist=Vector2D.dist(mousePT, PT.getP());
            index=i;
            if(dist<distMin){
                distMin=dist;
                target=targetContainer;
                targetPT=PT;
            }
        }
        if( targetPT==null) return null;
        double tIndex=this.getCloseCharacteristicPoint(target, targetPT.getParameter(), pointTol);
        Curve2D connectionCurve=target.getElement().getCurve2D();
        if(tIndex>=0) targetPT=new CurvePT(tIndex, connectionCurve.getP(tIndex), connectionCurve);
        int type=0;
        String typeStr="Normal";
        if(tIndex>=0) {
            type=1;
            typeStr="Characteristic";
        }
        double dist=Vector2D.dist(mousePT, targetPT.getP());
        
        if(debug>0) System.out.println("  mousePT="+Util.Pt(mousePT)
                +"connectionPT target="+target.getShapeId()
                + ",  MousePosition="+MousePositionInfo.getPositionString(infos[index].getPosition())
                + ",  Connection point type="+typeStr
                + ", dist="+Util.Num(dist)+", targetPT="+targetPT.toString()+
                ", boundaryTol="+boundaryTol+", pointTol="+pointTol);
        this.drawMark(targetPT.getP(), type);
        return targetPT;
    }


    private CurvePT getHitPT(Point2D mousePT, Vector2D vec, double boundaryTol,
            ShapeContainer target){
        CurvePT curvePT=null;
        Curve2D curve=target.getElement().getCurve2D();
        int type=-1;
        if(vec!=null&&Vector2D.length(vec)>0.0) {
            Vector2D unitVec=Vector2D.unitVector(vec);
          //----------------------------------------------------------------//
            CurvePT[] PTs=Curve2DUtil.getProjectionLines(mousePT, unitVec, curve);
          //----------------------------------------------------------------//
            double distMin=1.0e+5;
            int index=-1;
            int size=0;
            if(PTs!=null) size=PTs.length;
            for(int i=0;i<size;i++){
                double dist=Vector2D.dist(PTs[i].getP(),mousePT);
                if(dist>boundaryTol) continue;
                if(dist<distMin){
                    distMin=dist;
                    index=i;
                }
            }
            if(index>=0) {
                curvePT=PTs[index];
                type=0;
            }
        } else {
          //--------------------------------------------------------//
            CurvePT nearestPT=Curve2DUtil.getShortestLine(mousePT,curve);
          //--------------------------------------------------------//
            if(nearestPT!=null){
                double dist=Vector2D.dist(mousePT, nearestPT.getP());
                if(dist<boundaryTol) {
                    curvePT=nearestPT;
                    type=1;
                }
            }
        }
        if(curvePT!=null&&debug>0){
            if(type==0) System.out.println("*** ConnectionPointLS.getConnectionPT" +
                    " , found by the getProjectionLine, curvePT="+curvePT.toString());
            if(type==1) System.out.println("*** ConnectionPointLS.getConnectionPT" +
                    " , found by the getShortestLine, curvePT="+curvePT.toString());
        }
        return curvePT;
    }

    public double getCloseCharacteristicPoint(ShapeContainer container, double t, double pointTol){
        Curve2D curve=container.getElement().getCurve2D();
        Point2D PT=curve.getP(t);
        CurvePT[] curvePTs=curve.getCharacteristicPoints();
        double distMin=1.0e+5;
        int index=-1;
        for(int i=0;i<curvePTs.length;i++){
            double dist=Vector2D.dist(PT, curvePTs[i].getP());
            if(dist<pointTol&&dist<distMin){
                distMin=dist;
                index=i;
            }
        }
        double tIndex=-1.0;
        if(index>=0) tIndex=curvePTs[index].getParameter();
        if(debug>0) System.out.println("getCloseSegmentPTIndex tIndex="+tIndex+", distMin="+Util.Num(distMin));
        return tIndex;
    }

    private void drawMark(Point2D point, int type){
        double scale=DrawParameters.getScale();
        double x=point.getX();
        double y=point.getY();
        double width=16/scale;
        Ellipse2D largeCircle=new Ellipse2D.Double(x-0.5*width, y-0.5*width, width, width);
        width=14/scale;
        Rectangle2D largeRect=new Rectangle2D.Double(x-0.5*width, y-0.5*width, width, width);
        Color color=Color.RED;
        Shape shapeL=largeCircle;
        float lineWidth=1.0f;
        if(type>0) {
            color=Color.BLUE;
            shapeL=largeRect;
            lineWidth=1.0f/(float)scale;
        }
        width=6/scale;
        Rectangle2D smallRect=new Rectangle2D.Double(x-0.5*width, y-0.5*width, width, width);
            DrawShapeUtil.drawTempShape("ConnectionMark", smallRect, null,
                    color, color, "",null,  null);
        BasicStroke defaultStroke=new BasicStroke(lineWidth, BasicStroke.CAP_SQUARE, 
                    BasicStroke.JOIN_MITER, 10.0f);
        DrawShapeUtil.drawTempShape("ConnectionMark", shapeL, defaultStroke,
                    color, null, "", null,  null);
    }
}
