function amdoc_editor(canvbg, subject_o, content_o, doc_o) {
    var self = this;
    
    this.canvbg = canvbg;
    this.subject_o = subject_o;
    this.content_o = content_o;
    this.doc_o = doc_o;
    
    this.am = new ajaxmind(canvbg);
    this.am.addEventListener("select_subject",
			     function(evt) { return self.load_subject(evt);},
			     false);
    this.am.addEventListener("add_subject",
			     function(evt) { return self.add_subject_hdlr(evt);},
			     false);
    this.am.addEventListener("remove_subject",
			     function(evt) { return self.remove_subject_hdlr(evt);},
			     false);
    this.am.addEventListener("subject_change",
			     function(evt) { return self.subject_change_hdlr(evt);},
			     false);
    this.am.addEventListener("reorder",
			     function(evt) { return self.reorder_hdlr(evt); },
			     false);
    this.am.addEventListener("change_parent",
			     function(evt) { return self.change_parent_hdlr(evt); },
			     false);
    canvbg.addEventListener("keydown",
			    function(evt) { return self.keydown_handler(evt);},
			    false);
    canvbg.addEventListener("keypress",
			    function(evt) { return self.keypress_handler(evt); },
			    false);
    subject_o.addEventListener("change",
			       function(evt) { return self.subject_change(evt); },
			       false);
    content_o.addEventListener("change",
			       function(evt) { return self.content_change(evt); },
			       false);
    this.sub_ctxs = {};
}

amdoc_editor.prototype = {
    get_ctx: function(subject) {
	return this.sub_ctxs[subject.get_text()];
    },
    
    insert_div: function(subject, order) {
	var parent, ctx;
	var prev, next, next_div;
	var doc_o = get_elm("doc");
	
	ctx = this.get_ctx(subject);
	parent = subject.parent;
	
	next = this.am.get_child(parent, order + 1);
	if(next) {
	    next_div = this.get_ctx(next).div;
	} else {
	    prev = this.am.get_child(parent, order - 1);
	    if(!prev) {
		prev = parent;
		if(!prev) {
		    doc_o.appendChild(ctx.div);
		    this.reinsert_child(subject);
		    return;
		}
	    } else {
		while(prev.children.length) {
		    prev = this.am.get_child(prev, prev.children.length - 1);
		}
	    }
	    next_div = this.get_ctx(prev).div.nextSibling;
	}
	
	if(next_div)
	    doc_o.insertBefore(ctx.div, next_div);
	else
	    doc_o.appendChild(ctx.div);
	
	this.reinsert_child(subject);
    },
    
    reinsert_child: function(subject) {
	var next_div;
	var child, child_div;
	var i;
	var doc_o = get_elm("doc");
	
	next_div = this.get_ctx(subject).div.nextSibling;
	for(i = subject.children.length - 1; i >= 0; i--) {
	    child = this.am.get_child(subject, i);
	    child_div = this.get_ctx(child).div;
	    if(next_div) doc_o.insertBefore(child_div, next_div);
	    else doc_o.appendChild(child_div);
	    this.reinsert_child(child);
	    next_div = child_div;
	}
    },
    
    load_subject: function(evt) {
	var subject_o = get_elm("subject");
	var content_o = get_elm("content");
	
	subject_o.value = evt.subject.get_text();
	content_o.value = this.sub_ctxs[subject_o.value].text;
    },
    
    subject_change: function() {
	var subject_o = get_elm("subject");
	
	subject_o.value = this.am.change_subject_text(subject_o.value);
    },
    
    content_change: function() {
	var content_o = get_elm("content");
	var ctx;
	
	ctx = this.sub_ctxs[this.am.get_subject_text()];
	if(ctx == null) return;
	ctx.text = content_o.value;
	ctx.content.innerHTML = parser.show_msg(content_o.value);
    },
    
    add_subject_hdlr: function(evt) {
	var ctx;
	var doc_o = get_elm("doc");
	var order;
	
	ctx = this.sub_ctxs[evt.subject.get_text()] = new Object();
	ctx.text = "";
	ctx.div = document.createElement("div");
	ctx.title = document.createElement("h1");
	ctx.title.innerHTML = evt.subject.get_text();
	ctx.content = document.createElement("div");
	
	ctx.div.appendChild(ctx.title);
	ctx.div.appendChild(ctx.content);
	order = this.am.find_child_order(evt.subject);
	ctx.div.className = "deep_" + evt.subject.deep;
	this.insert_div(evt.subject, order);
    },
    
    remove_subject_hdlr: function(evt) {
	var ctx;
	
	ctx = this.sub_ctxs[evt.subject.get_text()];
	ctx.div.parentNode.removeChild(ctx.div);
	delete this.sub_ctxs[evt.subject.get_text()];
    },
    
    subject_change_hdlr: function(evt) {
	if(evt.from == evt.to) return;
	this.sub_ctxs[evt.to] = this.sub_ctxs[evt.from];
	delete this.sub_ctxs[evt.from];
	this.sub_ctxs[evt.to].title.innerHTML = evt.to;
    },
    
    reorder_hdlr: function(evt) {
	var subject;
	
	subject = evt.subject;
	this.insert_div(subject, evt.to);
    },
    
    change_parent_hdlr: function(evt) {
	var subject, child, parent;
	var i, order;
	var children = [];
	
	subject = evt.subject;
	order = this.am.find_child_order(subject);
	this.insert_div(subject, order);
	for(order = 0; order < subject.children.length; order++) {
	    child = this.am.get_child(subject, order);
	    children.push(child);
	    this.insert_div(child, order);
	}
	for(i = 0; i < children.length; i++) {
	    parent = children[i];
	    for(order = 0; order < parent.children; order++) {
		child = this.am.get_child(parent, order);
		children.push(child);
		this.insert_div(child, order);
	    }
	}
	this.get_ctx(subject).div.className = "deep_" + subject.deep;
    },
    
    keydown_handler: function(evt) {
	var i;
	var c;
	
	switch(evt.keyCode) {
	case 46: // del
	    this.am.del_subject();
	    break;
	case 16: // shift
	case 17: // ctrl
	case 18: // alt
	}
    },
    
    keypress_handler: function(evt) {
	var c;
	
	c = String.fromCharCode(evt.charCode || evt.keyCode);
	switch(c) {
	case 'o': // O
	    if(this.am.showing_order)
		this.am.hide_order();
	    else
		this.am.show_order();
	    break;
	case '+': // +
	    this.am.subject_order_inc();
	    break;
	case '-': // -
	    this.am.subject_order_dec();
	    break;
	case 'x':
	    this.am.cut();
	    break;
	case 'v':
	    this.am.paste();
	    break;
	case '<':
	    this.am.dec_subject_fontsz();
	    break;
	case '>':
	    this.am.inc_subject_fontsz();
	    break;
	}
    },
    
    change_subject_color: function(color) {
	return this.am.change_subject_color(color);
    },
    
    export_doc: function() {
	var doc = new Object;
	var key;
	
	doc.sub_ctxs = new Object();
	for(key in this.sub_ctxs) {
	    if(this.sub_ctxs[key].div) {
		doc.sub_ctxs[key] = this.sub_ctxs[key].text;
	    }
	}
	doc.ajaxmind = this.am.export_map();
	return doc;
    },
    
    export_doc_JSON: function() {
	var doc = this.export_doc();
	return "var _amdoc = " + doc.toJSONString() + ";";
    },
    
    import_doc: function(doc) {
	var key;
	var text;
	
	this.am.import_map(doc.ajaxmind);
	for(key in doc.sub_ctxs) {
	    text = doc.sub_ctxs[key];
	    if(this.sub_ctxs[key].div) {
		this.sub_ctxs[key].content.innerHTML = parser.show_msg(text);
		this.sub_ctxs[key].text = text;
	    }
	}
    },
    
    import_doc_JSON: function(doc) {
	doc = doc.substring("var _amdoc = ".length, doc.length - 1);
	doc = doc.parseJSON(function(key, value) {return value;});
	this.import_doc(doc);
    },
    
    reset: function() {
	this.am.reset();
    }
};

function save_doc() {
    var save_dlg = get_elm("save_dlg");
    
    dialog(save_dlg);
}

function real_save() {
    var save_dlg = get_elm("save_dlg");
    var save_file_name = get_elm("save_file_name");
    var fn, doc;
    var base;
    
    fn = save_file_name.value;
    base = fn.substring(0, fn.lastIndexOf('/'));
    doc = mm.export_doc_JSON();
    doc = convertUnicodeToUTF8(doc);
    if(!saveFile(fn, doc)) {
	alert("Can not save to " + fn + "!");
	return;
    }
    parser.attach_url = base;
    dialog_close(save_dlg);
}

function load_doc() {
    var load_dlg = get_elm("load_dlg");
    
    dialog(load_dlg);
}

function real_load() {
    var load_dlg = get_elm("load_dlg");
    var load_file_name = get_elm("load_file_name");
    var fn, doc;
    var base;
    
    fn = load_file_name.value;
    base = fn.substring(0, fn.lastIndexOf('/'));
    doc = loadFile(fn);
    if((doc == null) || (doc == false)) {
	alert(fn + " is not exist!!");
	return;
    }
    parser.attach_url = base;
    doc = convertUTF8ToUnicode(doc);
    mm.reset();
    mm.import_doc_JSON(doc);
    dialog_close(load_dlg);
}

function play_doc() {
    alert("Not implement!");
}

function load() {
    var canvbg;
    var subject_o, content_o, doc_o;
    
    canvbg = get_elm("canvbg");
    subject_o = get_elm("subject");
    content_o = get_elm("content");
    doc_o = get_elm("doc");
    subject_o.value = "";
    content_o.value = "";
    
    mm = new amdoc_editor(canvbg, subject_o, content_o, doc_o);
    if(typeof _amdoc != "undefined")
	mm.import_doc(_amdoc);
}
