import javax.imageio.ImageIO;
import javax.swing.JCheckBox;
import javax.swing.JTextField;
import javax.swing.JPanel;
import java.awt.GridBagLayout;
import javax.swing.JButton;
import javax.swing.border.BevelBorder;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EtchedBorder;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Label;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;


public class MapEditor extends JPanel{

	private static final long serialVersionUID = 1L;
	private MapPanel mapPanel = null;
	private JPanel operationPanel = null;
	private JPanel nodePanel = null;
	private JTextField nodeNumText = null;
	private JTextField nodeXtext = null;
	private JTextField nodeYtext = null;
	private JTextField nodeThetaText = null;
	private JTextField nodeNameText = null;
	private JButton setNodeButton = null;
	private JButton nodeButton = null;
	private boolean nodeBtnPressed = false;
	private JButton linkButton = null;
	private boolean linkBtnPressed = false;
	private JButton selectAllButton = null;
	private JButton releaseAllButton = null;

	private JPanel linkPanel = null;
	private JTextField linkNumText = null;
	private JTextField linkOrigNumText_1 = null;
	private JTextField linkEndNumText_1= null;
	private JTextField linkOrigNumText_2 = null;
	private JTextField linkEndNumText_2= null;
	private JButton setLinkButton = null;
	private JCheckBox backCB_1 = null;
	private JCheckBox backCB_2 = null;

	//フォントの設定
	private Font font1 = new Font("Tahoma", Font.BOLD, 12 );

	private void initialize() {
		this.setLayout(new GridBagLayout());
		GridBagConstraints constraints = new GridBagConstraints();

		constraints.fill = GridBagConstraints.BOTH;

		//MapPanelの貼り込み
		constraints.weightx = 100;
		constraints.weighty = 100;
		constraints.gridx = 0;
		constraints.gridy = 0;
		constraints.gridwidth = 1;
		constraints.gridheight = GridBagConstraints.REMAINDER;
		constraints.insets = new Insets(5,5,5,5);
		this.add(getMapPanel(), constraints);

		//ButtonPanelの貼り込み
		constraints.weightx = 0;
		constraints.weighty = 0;
		constraints.gridx = GridBagConstraints.RELATIVE;
		constraints.gridy = GridBagConstraints.REMAINDER;
		constraints.insets = new Insets(5, 5, 5, 5);
		this.add(getOperationPanel(), constraints);
	}

	public MapPanel getMapPanel() {
		if (mapPanel == null) {
			mapPanel = new MapPanel(this);
			mapPanel.setLayout(new GridBagLayout());
		}
		return mapPanel;
	}

	public JPanel getOperationPanel() {
		if (operationPanel == null) {
			GridBagConstraints gridBagConstraints = new GridBagConstraints();
			gridBagConstraints.fill = GridBagConstraints.BOTH ;
			gridBagConstraints.insets = new Insets(5, 5, 5, 5);
			operationPanel = new JPanel();
			operationPanel.setLayout(new GridBagLayout());
	        operationPanel.setBorder(new EtchedBorder(EtchedBorder.LOWERED));
	        gridBagConstraints.gridx = 0;
			gridBagConstraints.gridy = 0;
			operationPanel.add(getNodeButton(),gridBagConstraints);
			gridBagConstraints.gridy = 1;
			operationPanel.add(getLinkButton(), gridBagConstraints);
			gridBagConstraints.gridy = 2;
			operationPanel.add(getSelectAllButton(), gridBagConstraints);
			gridBagConstraints.gridy = 3;
			operationPanel.add(getReleaseAllButton(), gridBagConstraints);
	        gridBagConstraints.gridy = 4;
			operationPanel.add(getNodePanel(), gridBagConstraints);
	        gridBagConstraints.gridy = 4;
			operationPanel.add(getLinkPanel(), gridBagConstraints);
			getLinkPanel().setVisible(false);
			}
		return operationPanel;
	}

	public JPanel getNodePanel(){
		if(nodePanel==null){
			GridBagConstraints gridBagConstraints = new GridBagConstraints();
			gridBagConstraints.fill = GridBagConstraints.CENTER;
			gridBagConstraints.insets = new Insets(4, 2, 0, 3);
			nodePanel = new JPanel();
			nodePanel.setLayout(new GridBagLayout());
			nodePanel.setBorder(new EtchedBorder(EtchedBorder.LOWERED));
			nodePanel.setFont(font1);
			gridBagConstraints.gridx = 0;
			gridBagConstraints.gridy = 0;
			nodePanel.add(new Label("nodeID"), gridBagConstraints);
			gridBagConstraints.gridx = GridBagConstraints.RELATIVE;
			nodeNumText = new JTextField(4);
			nodeNumText.setEditable(false);
			nodeNumText.setHorizontalAlignment(JTextField.CENTER);
			gridBagConstraints.fill = GridBagConstraints.BOTH;
			nodePanel.add(nodeNumText, gridBagConstraints);
			gridBagConstraints.gridx = 0;
			gridBagConstraints.gridy = 1;
			gridBagConstraints.fill = GridBagConstraints.CENTER;
			nodePanel.add(new Label("X"), gridBagConstraints);
			gridBagConstraints.gridx = GridBagConstraints.RELATIVE;
			nodeXtext = new JTextField(4);
			nodeXtext.setHorizontalAlignment(JTextField.CENTER);
			gridBagConstraints.fill = GridBagConstraints.BOTH;
			nodePanel.add(nodeXtext, gridBagConstraints);
			gridBagConstraints.gridx = 0;
			gridBagConstraints.gridy = 2;
			gridBagConstraints.fill = GridBagConstraints.CENTER;
			nodePanel.add(new Label("Y"), gridBagConstraints);
			gridBagConstraints.gridx = GridBagConstraints.RELATIVE;
			nodeYtext = new JTextField(4);
			nodeYtext.setHorizontalAlignment(JTextField.CENTER);
			gridBagConstraints.fill = GridBagConstraints.BOTH;
			nodePanel.add(nodeYtext, gridBagConstraints);
			gridBagConstraints.gridx = 0;
			gridBagConstraints.gridy = 3;
			gridBagConstraints.fill = GridBagConstraints.CENTER;
			nodePanel.add(new Label("THETA"), gridBagConstraints);
			gridBagConstraints.gridx = GridBagConstraints.RELATIVE;
			nodeThetaText = new JTextField(4);
			nodeThetaText.setHorizontalAlignment(JTextField.CENTER);
			gridBagConstraints.fill = GridBagConstraints.BOTH;
			nodePanel.add(nodeThetaText, gridBagConstraints);
			gridBagConstraints.insets = new Insets(5, 2, 5, 3);
			gridBagConstraints.gridx = 0;
			gridBagConstraints.gridy = 4;
			gridBagConstraints.fill = GridBagConstraints.CENTER;
			nodePanel.add(new Label("NAME"), gridBagConstraints);
			gridBagConstraints.gridx = GridBagConstraints.RELATIVE;
			nodeNameText = new JTextField(6);
			nodeNameText.setHorizontalAlignment(JTextField.CENTER);
			gridBagConstraints.fill = GridBagConstraints.BOTH;
			nodePanel.add(nodeNameText, gridBagConstraints);
			gridBagConstraints.gridwidth = GridBagConstraints.REMAINDER;
			gridBagConstraints.gridy = 5;
			gridBagConstraints.fill = GridBagConstraints.CENTER;
			gridBagConstraints.insets = new Insets(5, 20, 5, 20);
			nodePanel.add(getSetNodeButton(), gridBagConstraints);
		}
		return nodePanel;
	}

	public JPanel getLinkPanel(){
		if(linkPanel == null){
			GridBagConstraints gridBagConstraints = new GridBagConstraints();
			gridBagConstraints.fill = GridBagConstraints.BOTH;
			linkPanel = new JPanel();
			linkPanel.setLayout(new GridBagLayout());
			linkPanel.setBorder(new EtchedBorder(EtchedBorder.LOWERED));
			linkPanel.setFont(font1);

			linkNumText = new JTextField(2);
			linkNumText.setHorizontalAlignment(JTextField.CENTER);
			linkNumText.setEditable(false);
			linkOrigNumText_1 = new JTextField(2);
			linkOrigNumText_1.setHorizontalAlignment(JTextField.CENTER);
			linkOrigNumText_1.setEditable(false);
			linkEndNumText_1 = new JTextField(2);
			linkEndNumText_1.setHorizontalAlignment(JTextField.CENTER);
			linkEndNumText_1.setEditable(false);
			linkOrigNumText_2 = new JTextField(2);
			linkOrigNumText_2.setHorizontalAlignment(JTextField.CENTER);
			linkOrigNumText_2.setEditable(false);
			linkEndNumText_2 = new JTextField(2);
			linkEndNumText_2.setHorizontalAlignment(JTextField.CENTER);
			linkEndNumText_2.setEditable(false);
			backCB_1 = new JCheckBox();
			backCB_2 = new JCheckBox();

			gridBagConstraints.gridx = 1;
			gridBagConstraints.gridy = 0;
			gridBagConstraints.insets = new Insets(0, 0, 0, 0);
			linkPanel.add(new Label("linkID"), gridBagConstraints);
			gridBagConstraints.gridx = GridBagConstraints.RELATIVE;
			linkPanel.add(linkNumText, gridBagConstraints);

			gridBagConstraints.fill = GridBagConstraints.CENTER;
			gridBagConstraints.gridx = 1;
			gridBagConstraints.gridy = 1;
			linkPanel.add(new Label("dir"), gridBagConstraints);
			gridBagConstraints.gridx = 3;
			gridBagConstraints.insets = new Insets(0, 0, 0, 0);
			linkPanel.add(new Label("  back"), gridBagConstraints);

			gridBagConstraints.gridx = 0;
			gridBagConstraints.gridy = 2;
			gridBagConstraints.insets = new Insets(0, 5, 3, 0);
			linkPanel.add(linkOrigNumText_1, gridBagConstraints);
			gridBagConstraints.gridx = GridBagConstraints.RELATIVE;
			gridBagConstraints.insets = new Insets(0, 0, 3, 0);
			linkPanel.add(new Label("  >>"), gridBagConstraints);
			gridBagConstraints.insets = new Insets(0, 0, 3, 0);
			linkPanel.add(linkEndNumText_1, gridBagConstraints);
			linkPanel.add(backCB_1, gridBagConstraints);

			gridBagConstraints.gridx = 0;
			gridBagConstraints.gridy = 3;
			gridBagConstraints.gridx = GridBagConstraints.RELATIVE;
			gridBagConstraints.insets = new Insets(0, 5, 0, 0);
			linkPanel.add(linkOrigNumText_2, gridBagConstraints);
			gridBagConstraints.insets = new Insets(0, 0, 0, 0);
			linkPanel.add(new Label("  >>"), gridBagConstraints);
			gridBagConstraints.insets = new Insets(0, 0, 0, 0);
			linkPanel.add(linkEndNumText_2, gridBagConstraints);
			linkPanel.add(backCB_2, gridBagConstraints);

			gridBagConstraints.gridx = 2;
			gridBagConstraints.gridy = 4;
			gridBagConstraints.gridwidth = 2;
			gridBagConstraints.insets = new Insets(5, -10, 4, 0);
			linkPanel.add(getSetLinkButton(), gridBagConstraints);
		}
		return linkPanel;
	}

	public JTextField getNodeNumTextField(){
		return nodeNumText;
	}

	public JTextField getNodeXtextField(){
		return nodeXtext;
	}

	public JTextField getNodeYtextField(){
		return nodeYtext;
	}

	public JTextField getNodeThetaTextField(){
		return nodeThetaText;
	}

	public JTextField getNodeNameTextField(){
		return nodeNameText;
	}

	public JTextField getLinkNumTextField(){
		return linkNumText;
	}

	public JTextField getLinkOrigNumText_1(){
		return linkOrigNumText_1;
	}

	public JTextField getLinkOrigNumText_2(){
		return linkOrigNumText_2;
	}

	public JTextField getLinkEndNumText_1(){
		return linkEndNumText_1;
	}

	public JTextField getLinkEndNumText_2(){
		return linkEndNumText_2;
	}

	public JCheckBox getBackCB_1(){
		return backCB_1;
	}

	public JCheckBox getBackCB_2(){
		return backCB_2;
	}

	public JButton getSetNodeButton(){
		if(setNodeButton==null){
			setNodeButton = new JButton("     Set     ");
			setNodeButton.setFont(font1);
			setNodeButton.addActionListener(new java.awt.event.ActionListener() {
				public void actionPerformed(java.awt.event.ActionEvent e) {
					if(! getNodeNameTextField().getText().equals("")){
						int nodeid = Integer.parseInt(getNodeNumTextField().getText());
						double posx = Double.parseDouble(getNodeXtextField().getText());
						double posy = Double.parseDouble(getNodeYtextField().getText());
						Point2D.Double pos = new Point2D.Double(posx, posy);
						double theta = Double.parseDouble(getNodeThetaTextField().getText());
						String name = getNodeNameTextField().getText();
						pos = mapPanel.getLogicalPositionFromRealPosition(pos);
						if (nodeid >= 0 && nodeid < mapPanel.getNodes().getArrayListLength()){
							mapPanel.getNodes().getNode(nodeid).setPosition(pos.x, pos.y);
							mapPanel.getNodes().getNode(nodeid).setTheta(theta);
							mapPanel.getNodes().getNode(nodeid).setName(name);
							mapPanel.getLinks().setPositionOfRelatedLinks(mapPanel.getNodes().getNode(nodeid).getSerial(), pos.x, pos.y);
							mapPanel.repaint();
						}
					}
				}
			});
		}
		return setNodeButton;
	}

	public JButton getSetLinkButton(){
		if(setLinkButton==null){
			setLinkButton = new JButton("Set");
			setLinkButton.setFont(font1);
			setLinkButton.addActionListener(new java.awt.event.ActionListener() {
				public void actionPerformed(java.awt.event.ActionEvent e) {
					int linkid = Integer.parseInt(getLinkNumTextField().getText());
					if(getBackCB_1().isSelected())
						mapPanel.getLinks().getLink(linkid).setBackFWD(1);
					else
						mapPanel.getLinks().getLink(linkid).setBackFWD(0);
					if(getBackCB_2().isSelected())
						mapPanel.getLinks().getLink(linkid).setBackBWD(1);
					else
						mapPanel.getLinks().getLink(linkid).setBackBWD(0);
				}
			});
		}
		return setLinkButton;
	}

	private JButton getNodeButton() {
		if (nodeButton == null) {
			nodeButton = new JButton();
			nodeButton.setText("Add Node");
			nodeButton.setBackground(Color.white);
			nodeButton.setFont(font1);
			nodeButton.addActionListener(new java.awt.event.ActionListener() {
				public void actionPerformed(java.awt.event.ActionEvent e) {
					if(nodeBtnPressed == true){
						nodeBtnPressed = false;
						nodeButton.setBackground(Color.white);
						nodeButton.setForeground(Color.black);
					}
					else{
						nodeBtnPressed = true;
						nodeButton.setBackground(Color.red);
						nodeButton.setForeground(Color.white);
					}
				}
			});
		}
		return nodeButton;
	}

	private JButton getLinkButton() {
		if (linkButton == null) {
			linkButton = new JButton();
			linkButton.setText("Set Link");
			linkButton.setBackground(Color.white);
			linkButton.setFont(font1);
			linkButton.addActionListener(new java.awt.event.ActionListener() {
				public void actionPerformed(java.awt.event.ActionEvent e) {
					mapPanel.setLinks();
					mapPanel.repaint();
				}
			});
		}
		return linkButton;
	}

	private JButton getReleaseAllButton() {
		if (releaseAllButton == null) {
			releaseAllButton = new JButton();
			releaseAllButton.setText("Release All");
			releaseAllButton.setBackground(Color.white);
			releaseAllButton.setFont(font1);
			releaseAllButton.addActionListener(new java.awt.event.ActionListener() {
				public void actionPerformed(java.awt.event.ActionEvent e) {
					mapPanel.releaseAll();
					mapPanel.repaint();
				}
			});
		}
		return releaseAllButton;
	}

	private JButton getSelectAllButton() {
		if (selectAllButton == null) {
			selectAllButton = new JButton();
			selectAllButton.setText("Select All");
			selectAllButton.setBackground(Color.white);
			selectAllButton.setFont(font1);
			selectAllButton.addActionListener(new java.awt.event.ActionListener() {
				public void actionPerformed(java.awt.event.ActionEvent e) {
					mapPanel.selectAll();
					mapPanel.repaint();
				}
			});
		}
		return selectAllButton;
	}


	public MapEditor() {
		super();
		initialize();
		this.setVisible(true);
	}

	public boolean getNodeBtnStatus(){
		return nodeBtnPressed;
	}

	public boolean getLinkBtnStatus(){
		return linkBtnPressed;
	}

}


class MapPanel extends JPanel implements
	MouseListener, MouseMotionListener,
	MouseWheelListener, ComponentListener, KeyListener{

	private static final long serialVersionUID = 1L;
	private Point2D preMousePoint; // 直前にマウスボタンが押されたポイント（スクリーン座標）
    private double scale; // 拡大率
    //論理座標とは，mapPanelの初期状態の右下隅を原点とし，右がX+, 下がY+である座標系．
    private double moveX; // X座標の平行移動量（論理座標）画面中央のX論理座標に-1をかけたもの
    private double moveY; // Y座標の平行移動量（論理座標）画面中央のY論理座標に-1をかけたもの
    private double moveXnode;//ノードのX移動量（論理座標値）
    private double moveYnode;//ノードのY移動量（論理座標値）
    private Dimension preSize;  // アプレットの大きさが変更される前のサイズ
    private Image bufferImage;  // バッファイメージ
    private Graphics2D bufferg; // バッファイメージのグラフィックコンテキスト
    private AffineTransform affineTransform = new AffineTransform(); // アフィン変換情報
    private BufferedImage readImage;
    private int  imgWidth, imgHeight;
    private Nodes nodes = null;
    private Links links = null;
    private int touchedNode = -1; //マウスでクリックしているノード
    private int touchedLink = -1; //マウスでクリックしているノード
    private MapEditor me = null;
    private Font f1 = new Font("Taohoma",Font.PLAIN,14);
    private Point2D.Double origin = new Point2D.Double(0.0, 0.0);
    private double mPerPix; //画像1pixelあたりの実際の長さ(m)

    public MapPanel(MapEditor me){
    	this.me = me;
 		initialize();
        // 各イベントリスナの登録
        addMouseListener(this);
        addMouseMotionListener(this);
        addMouseWheelListener(this);
        addComponentListener(this);
        addKeyListener(this);
        this.setBorder(new CompoundBorder(
        		  new BevelBorder(BevelBorder.RAISED),  //外側
        		  new BevelBorder(BevelBorder.LOWERED)  //内側
        		));
    }

	private void initialize() {
        try {
            readImage = ImageIO.read(new File("rtc-center.bmp"));
            imgWidth = readImage.getWidth();
            imgHeight = readImage.getHeight();
          } catch (Exception e) {
            e.printStackTrace();
            readImage = null;
          }

          moveX = -15;
          moveY = -10;
          moveXnode = 0;
          moveYnode = 0;
          preSize = getSize();
          scale = 1.0;
          mPerPix = 0.05; //1pixelあたり5cm
          setOrigin(-262.0, -9.0);
          setBackground(Color.gray);
          updateAffineTransform();
          nodes = new Nodes();
          links = new Links();
	}

	public int getImgWidth(){
		return imgWidth;
	}

	public int getImgHeight(){
		return imgHeight;
	}

    // paint(), repaint()で実行される描画処理
	//（拡大縮小は画面中央が基準）
    public void paintComponent(Graphics g) {
		try {
			if (bufferg !=null){
				bufferg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				     RenderingHints.VALUE_ANTIALIAS_ON);

				// buffergのAffineTransformを初期化
				bufferg.setTransform(new AffineTransform());

				// バッファのイメージをクリア
				bufferg.setColor(Color.white);
				bufferg.fillRect(0, 0, getWidth(), getHeight());

				// バッファのAffineTransformを設定する
				bufferg.setTransform(affineTransform);

				if (readImage != null){
				    bufferg.drawImage(readImage, -readImage.getWidth(),  -readImage.getHeight(), this);
				  }

				// ストロークの設定と描画
				bufferg.setColor(Color.red);

				//リンクの描画
				drawLinks(bufferg);
				//ノードの描画
				drawNodes(bufferg);

				// バッファイメージをアプレット画面に転送
				g.drawImage(bufferImage, 0,0, this);

			}
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		requestFocus();// これを入れるとKeyListenerが反応するようになる．
		}

    // リンクを描画する
    private void drawLinks(Graphics2D gr){
    	for(int i =0; i<links.getArrayListLength(); i++){
    		if(links.getLink(i).getIsSelected())
    			gr.setColor(Color.red);
    		else
    			gr.setColor(Color.magenta);
    		gr.draw(links.getLink(i).getLine());
    	}
    }

    // ノードを描画する
    private void drawNodes(Graphics2D gr){
		gr.setFont(f1);
    	for(int i =0; i<nodes.getArrayListLength(); i++){
    		if(nodes.getNode(i).getIsSelected())
    			gr.setColor(Color.red);
    		else
    			gr.setColor(Color.magenta);
    		//ノードの描画
    		gr.fill(nodes.getNode(i).getEllipse());
    		// ノードIDの表示
    		gr.drawString(""+i, (float)nodes.getNode(i).getEllipse().x - 4.0f, (float)nodes.getNode(i).getEllipse().y - 4.0f);
    	}
    	return;
    }

    // 現在のAffineTransformを更新する
    private void updateAffineTransform() {
        affineTransform.setToIdentity(); // 初期化
        affineTransform.translate(getWidth() * 0.5, getHeight() * 0.5); // 画面中央を基準
        affineTransform.scale(scale, scale); // スケール変換
        affineTransform.translate(moveX, moveY); // 平行移動
    }

    // スクリーン座標から論理値座標を返す
    private Point2D.Double getLogicalPositionFromScreenPosition(Point p)
    throws NoninvertibleTransformException {
        return (Point2D.Double)affineTransform.inverseTransform(
		            new Point2D.Double(p.x, p.y), null);
    }

    public void update(Graphics g) {
		paint(g);
	}

    // マウス左ボタンが押されたときの処理（クリックとは違う）
    public void mousePressed(MouseEvent e) {
    	preMousePoint = e.getPoint();
    	if((e.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) {
    		Point2D.Double mousePoint;
    		try {
    			mousePoint = getLogicalPositionFromScreenPosition(e.getPoint());
				touchedNode = nodes.getTouchedNodeID(mousePoint.x, mousePoint.y);
				touchedLink = links.getTouchedLinkID(mousePoint.x, mousePoint.y);
    		} catch (Exception ex){
    			return;
    		}
    	}
    }

    // マウスのドラッグに対応する処理
    public void mouseDragged(MouseEvent e) {
    	if((e.getModifiers() & MouseEvent.BUTTON3_MASK) != 0) { //右ボタンの場合
    		// 画面全体の平行移動
            moveX += (double)(e.getX() - preMousePoint.getX()) / scale;
            moveY += (double)(e.getY() - preMousePoint.getY()) / scale;
        	preMousePoint = e.getPoint();
        	updateAffineTransform();
        	repaint();
        }
    	else if((e.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) { //左ボタンの場合
    		// 選択されているノードを触っている場合はそのノードを平行移動
        	if(touchedNode != -1 && nodes.getNode(touchedNode).getIsSelected()){
                moveXnode = (double)(e.getX() - preMousePoint.getX()) / scale;
                moveYnode = (double)(e.getY() - preMousePoint.getY()) / scale;
            	preMousePoint = e.getPoint();
            	nodes.getNode(touchedNode).incrementPosition(moveXnode, moveYnode);
            	//移動するノードに合わせたリンクの移動
            	links.incrementPositionOfRelatedLinks(nodes.getNode(touchedNode).getSerial(), moveXnode, moveYnode);
            	changeNodeStatusDisplay(touchedNode);
                repaint();
                }
        }
    }

    // マウスホイールによるズームイン・ズームアウト
    public void mouseWheelMoved(MouseWheelEvent e) {
        double scale_ = (100.0 - e.getWheelRotation() * 5) / 100.0;
        scale *= scale_;
    	updateAffineTransform();
        repaint();
    }

    // マウスボタンのクリックに対応する処理
    public void mouseClicked(MouseEvent e) {
		// マウスの論理座標を取得
		Point2D.Double mousePoint;
		try {
			mousePoint = getLogicalPositionFromScreenPosition(e.getPoint());

			// AddNodeモードになっている場合
			if(me.getNodeBtnStatus()==true){
				if(touchedNode == -1){
					// どのノードにも触れていなかったらノード追加
					nodes.addNode(mousePoint.x, mousePoint.y);
					changeNodeStatusDisplay(nodes.getArrayListLength()-1);
					}
				else{
					// ノードに触れていたら選択状態をフリップ
					nodes.getNode(touchedNode).flipSelected();
					//表示変更
					changeNodeStatusDisplay(touchedNode);
					touchedNode = -1;
					me.getNodePanel().setVisible(true);
					me.getLinkPanel().setVisible(false);
					me.repaint();
				}
			}
			else{ //AddNodeモードになっていない場合
				if(touchedNode != -1){
					// ノードに触れていたらその選択状態をフリップ
					nodes.getNode(touchedNode).flipSelected();
					// 表示変更
					changeNodeStatusDisplay(touchedNode);
					touchedNode = -1;
					GridBagConstraints gridBagConstraints = new GridBagConstraints();
					gridBagConstraints.gridy = 4;
					me.getNodePanel().setVisible(true);
					me.getLinkPanel().setVisible(false);
					me.repaint();
				}
				else if(touchedLink != -1){
					// リンクに触れていたらその選択状態をフリップ
					links.getLink(touchedLink).flipSelected();
					// 表示変更
					changeLinkStatusDisplay(touchedLink);
					touchedLink = -1;
					GridBagConstraints gridBagConstraints = new GridBagConstraints();
					gridBagConstraints.gridy = 4;
					me.getNodePanel().setVisible(false);
					me.getLinkPanel().setVisible(true);
					me.repaint();
				}
			}
		} catch (Exception e1) {
			return;
		}
		repaint();
    }

    public void changeNodeStatusDisplay(int nodeID){
		me.getNodeNumTextField().setText(Integer.toString(nodeID));
		me.getNodeXtextField().setText(String.format("%3.2f",getRealPositionFromLogicalPosition(nodes.getNode(nodeID).getCenterPosition()).x));
		me.getNodeYtextField().setText(String.format("%3.2f",getRealPositionFromLogicalPosition(nodes.getNode(nodeID).getCenterPosition()).y));
		me.getNodeThetaTextField().setText(String.format("%3.2f",nodes.getNode(nodeID).getTheta()));
		me.getNodeNameTextField().setText(nodes.getNode(nodeID).getName());
    }

    public void changeLinkStatusDisplay(int linkID){
    	me.getLinkNumTextField().setText(Integer.toString(linkID));
    	if(links.getLink(linkID).getBackFWD()==1)
    		me.getBackCB_1().setSelected(true);
    	else
    		me.getBackCB_1().setSelected(false);
    	if(links.getLink(linkID).getBackBWD()==1)
    		me.getBackCB_2().setSelected(true);
    	else
    		me.getBackCB_2().setSelected(false);
    	me.getLinkOrigNumText_1().setText(Integer.toString(links.getLink(linkID).getOrigID()));
    	me.getLinkEndNumText_2().setText(Integer.toString(links.getLink(linkID).getOrigID()));
    	me.getLinkOrigNumText_2().setText(Integer.toString(links.getLink(linkID).getEndID()));
    	me.getLinkEndNumText_1().setText(Integer.toString(links.getLink(linkID).getEndID()));
    }

    // パネルのサイズが変更されたときの処理

    public void componentResized(ComponentEvent ce) {
    	//moveX, moveYはmapPanel中央の座標値．左上隅が原点で右がX+，下がY＋となっている．
    	//mapPanelの論理座標は，
        moveX = moveX - preSize.width * 0.5 + getWidth() * 0.5;
        moveY = moveY - preSize.height * 0.5 + getHeight() * 0.5;
        preSize = getSize();

        // バッファイメージの更新
        bufferImage = createImage(getWidth() , getHeight());
        bufferg = (Graphics2D)bufferImage.getGraphics();

        updateAffineTransform();

    	// 再描画
		repaint();
    }

    public void mouseMoved(MouseEvent e) {}
    public void mouseReleased(MouseEvent e) {}
    public void mouseEntered(MouseEvent e) {}
    public void mouseExited(MouseEvent e) {}
    public void componentMoved(ComponentEvent ce) {}
    public void componentShown(ComponentEvent ce) {}
    public void componentHidden(ComponentEvent ce) {}
	public void keyTyped(KeyEvent e) {}
	public void keyReleased(KeyEvent e) {}

    //キーボードが押されたときの処理
    public void keyPressed(KeyEvent e){
    	char key = e.getKeyChar();
//    	System.out.println(Integer.toString(key));
    	switch(key){
    	case 105: // ' i '：ズームイン
            scale *= 1.1;
        	updateAffineTransform();
            repaint();
            break;
    	case 111: // ' o '：ズームアウト
            scale *= 0.9;
        	updateAffineTransform();
            repaint();
            break;
    	case 127: // ' del '：デリート
    		deleteSelected();
    		repaint();
    		break;
        default:
        	break;
    	}
      }

    // 選択されているノードとリンクを削除する
    private void deleteSelected() {
    	for(int i=links.getArrayListLength()-1; i>=0; i--){
    		if(links.getLink(i).getIsSelected())
    			links.deleteLink(i);
    	}
    	for(int i=nodes.getArrayListLength()-1; i>=0; i--){
    		if(nodes.getNode(i).getIsSelected()){
    			links.deleteRelatedLinks(nodes.getNode(i).getSerial());
    			nodes.deleteNode(i);
    		}
    	}
    }

    // リンクを設定する．
    public void setLinks(){
    	int max = nodes.getArrayListLength();
    	for(int i=0; i<max; i++){
    		if(nodes.getNode(i).getIsSelected()){
    			for(int j=i+1; j<max; j++){
    				if(nodes.getNode(j).getIsSelected()){
    					if(checkLinkExistence(nodes.getNode(i).getSerial(), nodes.getNode(j).getSerial())==-1)
    						links.addLink(nodes.getNode(i).getSerial(), nodes.getNode(j).getSerial(), nodes.getNode(i).getCenterPosition(), nodes.getNode(j).getCenterPosition());
    				}
    			}
    		}
    	}
    	linkNodeID();
    }

    // すでに同じリンクが存在するかどうかをチェックする．
    // 同じものがあればそのリンク番号が返る．なければ-1が返る．
    private int checkLinkExistence(int origSerial, int endSerial){
    	for(int i=0; i<links.getArrayListLength(); i++){
    		if(links.getLink(i).getOrigSerial()==origSerial
    				&& links.getLink(i).getEndSerial()==endSerial)
    			return i;
    	}
    	return -1;
    }

    //ノードのIDとリンクを対応付ける（ファイル書き出し用）
    public void linkNodeID(){
    	int serial = -1;
    	for(int i=0; i<nodes.getArrayListLength(); i++){
    		serial = nodes.getNode(i).getSerial();
    		for(int j=0; j<links.getArrayListLength(); j++){
    			if(links.getLink(j).getOrigSerial()==serial)
    				links.getLink(j).setOrigID(i);
    			else if(links.getLink(j).getEndSerial()==serial)
    				links.getLink(j).setEndID(i);
    		}
    	}
    }

    // ノードとリンクの全部を選択状態にする．
    public void selectAll(){
    	for(int i=0; i<links.getArrayListLength(); i++)
    		links.getLink(i).setSelected(true);
    	for(int i=0; i<nodes.getArrayListLength(); i++ )
    		nodes.getNode(i).setSelected(true);
    }

    // ノードとリンクの全部を選択されていない状態にする
    public void releaseAll(){
    	for(int i=0; i<links.getArrayListLength(); i++)
    		links.getLink(i).setSelected(false);
    	for(int i=0; i<nodes.getArrayListLength(); i++ )
    		nodes.getNode(i).setSelected(false);
    }

    // 原点を設定する
    public void setOrigin(double x, double y){ //論理座標で設定
    	origin.x = x;
    	origin.y = y;
    }

    // 論理座標値を実環境の座標値に変換する．解像度は1cmにする．
    public Point2D.Double getRealPositionFromLogicalPosition( Point2D.Double lp){
    	//実環境の座標は右がX+，上がY+とする．
    	double rx = ( lp.x - origin.x )*mPerPix;
    	double ry = - ( lp.y - origin.y)*mPerPix;
    	//1㎝刻みの値に修正
    	int irx = (int) (rx*100.0);
    	int iry = (int) (ry*100.0);
    	rx = irx/100.0;
    	ry = iry/100.0;
    	return new Point2D.Double(rx, ry);

    }

    // 実環境の座標値を論理座標値に変換する
    public Point2D.Double getLogicalPositionFromRealPosition( Point2D.Double rp){
    	//論理座標は右がX+，下がY+である．原点はスクリーンの右下．
    	double lx = rp.x/mPerPix + origin.x;
    	double ly =  - rp.y/mPerPix + origin.y;
    	return new Point2D.Double( lx,ly );
    }

    // 経路マップデータファイルを解析してグラフィカルなオブジェクトを生成する
    // エラー処理がほとんどないので，データファイル自体をちゃんと作っておくべし．
    public void parsePathDataFile(File file){
    	//新しいデータを読み込む前に表示中のノードとリンクのデータを全消去
    	for(int i=nodes.getArrayListLength()-1; i>=0; i--)
    		nodes.deleteNode(i);
    	for(int i=links.getArrayListLength()-1; i>=0; i--)
        	links.deleteLink(i);
    	try {
			FileReader fr = new FileReader(file);
			BufferedReader br = new BufferedReader(fr);
			String line;
			String split[];
			Point2D.Double point = new Point2D.Double(0, 0);
			int orig, end;
			while ( (line=br.readLine()) != null){
				if( line.length() > 6 &&
						! line.substring(0, 2).equalsIgnoreCase("//") &&
						! line.substring(0, 1).equalsIgnoreCase(" ")){
					split = line.split(" ");
					if(split[0].equals("mPerPix")){
						mPerPix = Double.parseDouble(split[1]);
					}
					else if (split[0].equalsIgnoreCase("originX")){
						origin.x = Double.parseDouble(split[1]);
					}
					else if (split[0].equalsIgnoreCase("originY")){
						origin.y = Double.parseDouble(split[1]);
					}
					else if (split[0].equalsIgnoreCase("n")){
						point = getLogicalPositionFromRealPosition(
								new Point2D.Double(Double.parseDouble(split[1]), Double.parseDouble(split[2])));
						nodes.addNode(point.x, point.y, Double.parseDouble(split[3]));
						if(split.length == 5)
							nodes.getNode(nodes.getArrayListLength()-1).setName(split[4]);
						else
							nodes.getNode(nodes.getArrayListLength()-1).setName("-");
					}
					else if (split[0].equalsIgnoreCase("l")){
						orig = Integer.parseInt(split[1]);
						end = Integer.parseInt(split[2]);
						links.addLink(nodes.getNode(orig).getSerial(), nodes.getNode(end).getSerial(),
								nodes.getNode(orig).getCenterPosition(), nodes.getNode(end).getCenterPosition());
						links.getLink(links.getArrayListLength()-1).setBackFWD(Integer.parseInt(split[4]));
						links.getLink(links.getArrayListLength()-1).setBackBWD(Integer.parseInt(split[5]));
						links.getLink(links.getArrayListLength()-1).setOrigID(orig);
						links.getLink(links.getArrayListLength()-1).setEndID(end);
					}
				}
			}
			br.close();
			fr.close();
			repaint();
		} catch (Exception e) {
			e.printStackTrace();
		}
    }

    //ノードとリンクのデータから経路マップデータをファイルに書き出す．
    public void writePathDataFile(File file, boolean isRoutemap){
    	try {
			FileWriter fw = new FileWriter(file);
			BufferedWriter bw = new BufferedWriter(fw);
			if(isRoutemap == false){ // PathPlanningモジュールに渡すroutemapの場合は以下のデータは書かない
				bw.write("mPerPix "+Double.toString(mPerPix)+"\n");
				bw.write("originX "+origin.x+"\n");
				bw.write("originY "+origin.y+"\n");
			}
			for(int i=0; i<nodes.getArrayListLength(); i++){
				if (isRoutemap == false){
					bw.write("n "+getRealPositionFromLogicalPosition(nodes.getNode(i).getCenterPosition()).x +" "
						+getRealPositionFromLogicalPosition(nodes.getNode(i).getCenterPosition()).y +" "
						+nodes.getNode(i).getTheta() +" "+ nodes.getNode(i).getName() + "\n" );
				}
				else {
					bw.write("n "+getRealPositionFromLogicalPosition(nodes.getNode(i).getCenterPosition()).x +" "
							+getRealPositionFromLogicalPosition(nodes.getNode(i).getCenterPosition()).y +" "
							+nodes.getNode(i).getTheta() +"\n" );
				}
			}
			linkNodeID();
			if(isRoutemap == false){
				for(int i=0; i<links.getArrayListLength(); i++)
					bw.write("l "+links.getLink(i).getOrigID() +" "+links.getLink(i).getEndID()+" 0 "
							+links.getLink(i).getBackFWD()+" "+links.getLink(i).getBackBWD()+"\n");
			}
			else{
				for(int i=0; i<links.getArrayListLength(); i++)
					bw.write("l "+links.getLink(i).getOrigID() +" "+links.getLink(i).getEndID()+" 0\n");
			}
			bw.close();
			fw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
    }

    public Nodes getNodes(){
    	return nodes;
    }

    public Links getLinks(){
    	return links;
    }
}