/* $Id: GSHTMLEditorTextPane.java 512 2012-06-22 02:50:48Z ohura $ */
package sml_editor.swingui;

import java.awt.Event;
import java.awt.Font;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.AbstractAction;
import javax.swing.JTextPane;
import javax.swing.KeyStroke;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.DocumentListener;
import javax.swing.text.Caret;
import javax.swing.text.Keymap;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.hexidec.ekit.PasteListener;
import com.hexidec.ekit.UnicodeDecode;
import com.hexidec.ekit.action.RedoAction;
import com.hexidec.ekit.action.UndoAction;
import com.hexidec.ekit.component.ExtendedHTMLDocument;
import com.hexidec.ekit.component.ExtendedHTMLEditorKit;
import com.hexidec.ekit.component.HTMLUtilities;




@SuppressWarnings("serial")
public class GSHTMLEditorTextPane extends JTextPane 
	implements MouseListener, FocusListener, CaretListener{
	
	private static int n = 0;
	
	//private int offset = 0;
	
	private DocumentListener docListener;
	private EditorMouseListener mouseListener;
	private PasteListener pasteListener;	//20080922 North Grid
	
	private int count = 0;
	
//	private AtomicInteger linkCounter = new AtomicInteger(0);
	
	private List<String> tagNames;
	
	private String emphasizeId;
	
	public static int DEFAULT_FONT_SIZE = 20;
	
	protected UndoAction undoAction;
	protected RedoAction redoAction;
	
	protected static String DUMMY_CHAR = "\1";
	protected static String DUMMY_CHAR_HTML = "&#1;";
	
	public GSHTMLEditorTextPane(UndoAction undoAction,RedoAction redoAction){
		super();

		setContentType("text/html");

		this.undoAction = undoAction;
		this.redoAction = redoAction;
		this.tagNames = this.createTagList();
		Style defStyle = StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE);
		StyleConstants.setFontSize(defStyle,DEFAULT_FONT_SIZE);
		this.setFont(new Font(StyleContext.DEFAULT_STYLE,Font.PLAIN,20));
		// ADD NorthGird 存在しないタグを復活させる
//		checkTagName(this.controller.getDocument().getDocumentElement());
//		this.controller.getDocument().getElementsByTagName("segment");
		this.updateText();
		this.addFocusListener(this);
		this.addCaretListener(this);
	}
	private void checkTagName(Element element){
		NodeList nodes = element.getChildNodes();
		for(int i=0;i<nodes.getLength();i++){
			Node node = nodes.item(i);
			if(node.getNodeType() == Node.ELEMENT_NODE){
				Element elem = (Element)node;
				String tagName = elem.getNodeName();
				if(!tagName.equals("br")){
					addTagList(tagName);
					/*
					if(!elem.getAttribute("id").equals("")){
						incrementLinkid();
					} */
				}
				this.checkTagName(elem);
			}
		}
	}

    public void setText(String t) {
    	t = t.replaceAll(DUMMY_CHAR_HTML, "");
    	super.setText(t+DUMMY_CHAR);
    }
	
    public String getText() {
    	String rtn = super.getText();
    	return rtn.replaceAll(DUMMY_CHAR_HTML, "");
    }

    public String getSelectedText() {
        String src = null;
        Caret caret = getCaret();
        int p0 = Math.min(caret.getDot(), caret.getMark());
        int p1 = Math.max(caret.getDot(), caret.getMark());
        if (p0 != p1) {
            String body = HTMLUtilities.getBodyText(super.getText());
            src = body;

    		src = src.replaceAll(" ", "");
    		src = src.replaceAll("\r", "");
    		src = src.replaceAll("\n", "");
    		src = src.replaceAll("\r\n", "");
    		src = src.replaceAll("\\</p\\>", "\n");
    		src = src.replaceAll("\\<br\\>", "\n");
    		Pattern pattern = Pattern.compile("<.+?>", Pattern.DOTALL);
    		Matcher matcher = pattern.matcher(src);
    		src = matcher.replaceAll("");
    		src = src.replaceAll("&amp;", "&");
    		src = src.replaceAll("&quot;", "\"");
    		src = src.replaceAll("&gt;", "<");
    		src = src.replaceAll("&lt;", ">");
    		src = src.replaceAll("&#160;", " ");
    		src = src.replaceAll(DUMMY_CHAR_HTML, "");
    		src = UnicodeDecode.decode(src);
        
            src = src.substring(Math.max(0,p0-1), Math.min(src.length(),p1-1));
        }

        
        return src;
    }
    
	/*	
	public int incrementLinkid(){
		return linkCounter.incrementAndGet();
	}
	*/
	
	public List<String> createTagList(){
		List<String> tagNames = new ArrayList<String>();
		String line;
		try{
			BufferedReader reader = new BufferedReader(new FileReader("tags.txt"));
			while((line = reader.readLine()) != null){
			   tagNames.add(line.trim());
			}
		}catch(IOException e){
		   e.printStackTrace();
		}
		return tagNames;
	}
	public void addTagList(String tagname){
		if(!tagNames.contains(tagname)){
			tagNames.add(tagname);
		}
	}
	public List getTagNames(){
		return tagNames;
	}
	/*
	 * markupを行ったとき等に呼ばれる
	 */
	public void updateText(){
/*		this.getStyledDocument().removeDocumentListener(this.docListener);
		try {
			this.getStyledDocument().remove(0,this.getStyledDocument().getLength());
		} catch (BadLocationException e) {
			e.printStackTrace();
		}		
		Style defaultStyle = this.getStyledDocument().getStyle("default");
	    this.offset = 0;
		this.showText(this.controller.getDocument().getDocumentElement(),defaultStyle);
		this.offset = 0;
		this.getStyledDocument().addDocumentListener(this.docListener);
		//2007/10/15 kazuhiro kobayashi
		this.setCaretPosition(0);*/
	}
	
	public void emphasize(String id){
		this.emphasizeId = id;
	}
	

	private Node getNode(Node node,int index){
		if(node.hasChildNodes() == false){
			return null;
		}
		NodeList list = node.getChildNodes();
		for(int i=0;i<list.getLength();i++){
			Node n = list.item(i);
			if(n.getNodeType() == Node.TEXT_NODE){
				count += n.getNodeValue().length();
				if(count >= index){
					count = 0;
					return n;
				}
			}else if(n.getNodeName().equals("br")){
				count++;
			}
			
			n = getNode(n,index);
			if(n != null){
				return n;
			}
		}
		return null;
	}

	public void caretUpdate(CaretEvent e) {
		try{
			ExtendedHTMLDocument htmlDoc = (ExtendedHTMLDocument)getDocument();
			if( e.getDot() == htmlDoc.getLength() && htmlDoc.getLength() > 1 ){
				String body = HTMLUtilities.getBodyText(super.getText());
				if( body.endsWith(DUMMY_CHAR_HTML+"\n")){
					if(e.getDot()==e.getMark()){
						setCaretPosition(htmlDoc.getLength() -1);
					}else{
						moveCaretPosition(htmlDoc.getLength() -1);
						new DelayCaret(e.getMark(), htmlDoc.getLength() -1).start();
					}
				}
			}
			
			//System.out.println(e.getDot()+":"+element.toString()+":"+htmlDoc.getLength());
		}catch(Exception ex){
			ex.printStackTrace();
		}
	}
	
	public class DelayCaret extends Thread{
		int pos = 0;
		int mark = 0;
		public DelayCaret(int mark, int pos){
			this.mark = mark;
			this.pos = pos;
		}
		public void run(){
			setCaretPosition(mark);
			moveCaretPosition(pos);
		}
	}
	
	public void setDocumentListener(DocumentListener docListener){
		this.getStyledDocument().removeDocumentListener(this.docListener);
		this.docListener = docListener;
		this.getStyledDocument().addDocumentListener(this.docListener);
	}
	public void setMouseListener(EditorMouseListener mouseListener){
		this.removeMouseListener(this.mouseListener);
		this.mouseListener = mouseListener;
		this.addMouseListener(this.mouseListener);
		this.addMouseMotionListener(this.mouseListener);
	}

	public void mouseClicked(MouseEvent arg0) {
//		this.getHighlighter().removeAllHighlights();
		this.validate();
	}

	public void mouseEntered(MouseEvent arg0) {
		
	}

	public void mouseExited(MouseEvent arg0) {
		
	}

	public void mousePressed(MouseEvent arg0) {
		this.emphasizeId = null;

		
	}

	public void mouseReleased(MouseEvent arg0) {
		
	}
/*    public void copy() {
    	Action copyAction = TransferHandler.getCopyAction();
    	super.copy();
    	TransferHandler.getCopyAction();
//        invokeAction("copy", TransferHandler.getCopyAction());
    } */
	public void focusGained(FocusEvent e) {
		Keymap km = this.getKeymap();
		KeyStroke ksctrlz = KeyStroke.getKeyStroke(KeyEvent.VK_Z,
				Event.CTRL_MASK);
		km.addActionForKeyStroke(ksctrlz, undoAction);

		KeyStroke ksctrly = KeyStroke.getKeyStroke(KeyEvent.VK_Y,
				Event.CTRL_MASK);
		km.addActionForKeyStroke(ksctrly, redoAction);

		KeyStroke ksctrlv = KeyStroke.getKeyStroke(KeyEvent.VK_V,
				Event.CTRL_MASK);
		km.addActionForKeyStroke(ksctrlv, new AbstractAction(){
			public void actionPerformed(ActionEvent e) {
				paste(true);
			}
		});
	}
	
	public void focusLost(FocusEvent e) {
	}

	public void setPasteListener(PasteListener pasteListener){
		this.pasteListener = pasteListener;
	}
	
	public void  paste(boolean allowHtml){
		Clipboard clp = getToolkit().getSystemClipboard();
		Transferable data = clp.getContents(this);
		try{
			pasteListener.stopDocumentListener();
			if(data.isDataFlavorSupported(new DataFlavor("text/html;class=java.lang.String"))&&allowHtml){
				boolean isFirst = getCaretPosition() <= 1 ? true : false;
				
				ExtendedHTMLEditorKit kit = (ExtendedHTMLEditorKit)getEditorKit();
				kit.setReadWithoutHeader(true);
				super.paste();
				kit.setReadWithoutHeader(false);
				int pos = getCaretPosition();
				setText(HTMLUtilities.getBodyText(super.getText()));
				if(isFirst){
					setCaretPosition(pos-1);
				}else{
					setCaretPosition(pos-2);
				}
			}else{
				boolean isFirst = getCaretPosition() <= 1 ? true : false;
				pasteListener.paste();
				int pos = getCaretPosition();
				setText(HTMLUtilities.getBodyText(super.getText()));
				if(isFirst&&pos+1==getDocument().getLength()-1){
					setCaretPosition(pos+1);
				}else{
					setCaretPosition(pos);
				}
			}
			pasteListener.learn();
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			pasteListener.startDocumentListener();
		}
	}

	public void copy(){
		super.copy();
	}
}
