/*FreeMind - A Program for creating and viewing Mindmaps
 *Copyright (C) 2000-2001  Joerg Mueller <joergmueller@bigfoot.com>
 *See COPYING for Details
 *
 *This program is free software; you can redistribute it and/or
 *modify it under the terms of the GNU General Public License
 *as published by the Free Software Foundation; either version 2
 *of the License, or (at your option) any later version.
 *
 *This program is distributed in the hope that it will be useful,
 *but WITHOUT ANY WARRANTY; without even the implied warranty of
 *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *GNU General Public License for more details.
 *
 *You should have received a copy of the GNU General Public License
 *along with this program; if not, write to the Free Software
 *Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
/*$Id: MindMapNodeModel.java,v 1.21.14.4.4.11 2008/05/26 19:25:09 christianfoltin Exp $*/

package freemind.modes.mindmapmode;

import java.io.IOException;
import java.io.Writer;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.ListIterator;

import freemind.main.HtmlTools;
import freemind.modes.MindMap;
import freemind.modes.NodeAdapter;

/**
 * This class represents a single Node of a Tree. It contains direct handles to
 * its parent and children and to its view.
 */
public class MindMapNodeModel extends NodeAdapter {

	//
	// Constructors
	//

	public MindMapNodeModel(MindMap pMap) {
		this(null,pMap);
	}

	public MindMapNodeModel(Object userObject, MindMap pMap) {
		super(userObject, pMap);
		children = new LinkedList();
		setEdge(new MindMapEdgeModel(this, getMapFeedback()));
	}

	//
	// The mandatory load and save methods
	//

	public String getPlainTextContent() {
		return HtmlTools.htmlToPlain(toString());
	}

	public void saveTXT(Writer fileout, int depth) throws IOException {
		String plainTextContent = getPlainTextContent();
		for (int i = 0; i < depth; ++i) {
			fileout.write("    ");
		}
		if (plainTextContent.matches(" *")) {
			fileout.write("o");
		} else {
			if (getLink() != null) {
				String link = getLink();
				if (!link.equals(plainTextContent)) {
					fileout.write(plainTextContent + " ");
				}
				fileout.write("<" + link + ">");
			} else {
				fileout.write(plainTextContent);
			}
		}

		fileout.write("\n");
		// fileout.write(System.getProperty("line.separator"));
		// fileout.newLine();

		// ^ One would rather expect here one of the above commands
		// commented out. However, it does not work as expected on
		// Windows. My unchecked hypothesis is, that the String Java stores
		// in Clipboard carries information that it actually is \n
		// separated string. The current coding works fine with pasting on
		// Windows (and I expect, that on Unix too, because \n is a Unix
		// separator). This method is actually used only for pasting
		// purposes, it is never used for writing to file. As a result, the
		// writing to file is not tested.

		// Another hypothesis is, that something goes astray when creating
		// StringWriter.

		saveChildrenText(fileout, depth);
	}

	private void saveChildrenText(Writer fileout, int depth) throws IOException {
		for (ListIterator e = sortedChildrenUnfolded(); e.hasNext();) {
			final MindMapNodeModel child = (MindMapNodeModel) e.next();
			if (child.isVisible()) {
				child.saveTXT(fileout, depth + 1);
			} else {
				child.saveChildrenText(fileout, depth);
			}
		}
	}

	public void collectColors(HashSet colors) {
		if (color != null) {
			colors.add(getColor());
		}
		for (ListIterator e = childrenUnfolded(); e.hasNext();) {
			((MindMapNodeModel) e.next()).collectColors(colors);
		}
	}

	private String saveRFT_escapeUnicodeAndSpecialCharacters(String text) {
		int len = text.length();
		StringBuilder result = new StringBuilder(len);
		int intValue;
		char myChar;
		for (int i = 0; i < len; ++i) {
			myChar = text.charAt(i);
			intValue = (int) text.charAt(i);
			if (intValue > 128) {
				result.append("\\u").append(intValue).append("?");
			} else {
				switch (myChar) {
				case '\\':
					result.append("\\\\");
					break;
				case '{':
					result.append("\\{");
					break;
				case '}':
					result.append("\\}");
					break;
				case '\n':
					result.append(" \\line ");
					break;
				default:
					result.append(myChar);
				}
			}
		}
		return result.toString();
	}

	public void saveRTF(Writer fileout, int depth, HashMap colorTable)
			throws IOException {
		String pre = "{" + "\\li" + depth * 350;
		String level;
		if (depth <= 8) {
			level = "\\outlinelevel" + depth;
		} else {
			level = "";
		}
		String fontsize = "";
		if (color != null) {
			pre += "\\cf" + (Integer) colorTable.get(getColor());
		}

		if (isItalic()) {
			pre += "\\i ";
		}
		if (isBold()) {
			pre += "\\b ";
		}
		if (isStrikethrough()) {
			pre += "\\strike ";
		}
		if (font != null && font.getSize() != 0) {
			fontsize = "\\fs" + Math.round(1.5 * getFont().getSize());
			pre += fontsize;
		}

		pre += "{}"; // make sure setting of properties is separated from the
						// text itself

		fileout.write("\\li" + depth * 350 + level + "{}");
		if (this.toString().matches(" *")) {
			fileout.write("o");
		} else {
			String text = saveRFT_escapeUnicodeAndSpecialCharacters(this
					.getPlainTextContent());
			if (getLink() != null) {
				String link = saveRFT_escapeUnicodeAndSpecialCharacters(getLink());
				if (link.equals(this.toString())) {
					fileout.write(pre + "<{\\ul\\cf1 " + link + "}>" + "}");
				} else {
					fileout.write("{" + fontsize + pre + text + "} ");
					fileout.write("<{\\ul\\cf1 " + link + "}}>");
				}
			} else {
				fileout.write(pre + text + "}");
			}
		}

		fileout.write("\\par");
		fileout.write("\n");

		saveChildrenRTF(fileout, depth, colorTable);
	}

	private void saveChildrenRTF(Writer fileout, int depth, HashMap colorTable)
			throws IOException {
		for (ListIterator e = sortedChildrenUnfolded(); e.hasNext();) {
			final MindMapNodeModel child = (MindMapNodeModel) e.next();
			if (child.isVisible()) {
				child.saveRTF(fileout, depth + 1, colorTable);
			} else {
				child.saveChildrenRTF(fileout, depth, colorTable);
			}
		}
	}

	public boolean isWriteable() {
		return true;
	}

}
