
var FXEDITOR = Class.create();
FXEDITOR.prototype = {
	
	
	//==========================================================================
	// variables
	//==========================================================================
	_ckconfig: null,
	_container: null,
	_textarea: null,
	_editor: null,
	_preview: null,
	_contentChanged: false,
	
	
	//==========================================================================
	// properties
	//==========================================================================
	id: null,
	content: null,
	forms: null,
	diaries: null,
	career: 'd',
	header: "",
	footer: "",
	
	
	//==========================================================================
	// constractor
	//==========================================================================
	initialize: function () {
		this.id = "fxeditor";
		this._ckconfig = '../../config/config.js';
	},
	
	
	//==========================================================================
	// public method
	//==========================================================================
	/**
	 * エディタを作成します。
	 */
	create: function () {
		this._container = this._createContainer();
		this._textarea = this._createTextArea();
		this._container.appendChild(this._textarea);
		this._preview = this._createPreview();
		this._preview.create();

		// CKEditorの初期化
		this._createEditor();
	},
	
	/**
	 * エディタを削除します。
	 */
	remove: function () {
		if (this._editor) {
			this._preview.remove();
			this._editor.destroy();
			this._editor = null;
			Element.remove(this._textarea);
			Element.remove(this._container);
		}
	},
	
	/**
	 * フォーカスを当てる
	 */
	focusIn: function () {
		this._editor.focus();
	},
	
	/**
	 * 位置／幅高さの調整を行います。
	 * @param left
	 * @param top
	 * @param width
	 * @param height
	 */
	updateBounds: function(left, top, width, height) {
		if (this._container) {
			this._container.style.top = top + "px";
			this._container.style.left = left + "px";
			if (!isNaN(width) && !isNaN(height)) {
				this._editor.resize(width, height);
			}
		}
	},
	
	/**
	 * プレビューの位置／幅高さの調整を行います。
	 * @param left
	 * @param top
	 * @param width
	 * @param height
	 */
	updatePreviewBounds: function(left, top, width, height) {
		this._preview.updateBounds(left, top, width, height);
	},
	
	/**
	 * エディタ内容が変更された時の処理です。
	 * this = CKEditorAdaptorインスタンスなので注意
	 * @param isNotifyFlex 更新をFxに通知するかどうか
	 */
	notifyChange: function(isNotifyFlex, interval) {
		var fxeditor = this;
		if (isNotifyFlex == null) {
			isNotifyFlex = true;
		}
		if (interval == null) {
			interval = 100;
		}
		
		if (this._editor != null && this._contentChanged) {
			this._editor.setData(this.content, function() {
				fxeditor.notifyChange(false, 1000);
			});
			this._contentChanged = false;
		}
		
		setTimeout(function() {
				var html = ""
				
				if (fxeditor.header != "") {
					// header追加
					html += fxeditor.header + "<BR>";
				}
				
				// body追加
				html += fxeditor.getContent();
				
				
				if (fxeditor.forms) {
					// form追加
					html += "<BR>" + fxeditor.forms;
				}
				if (fxeditor.diaries) {
					// diary追加
					html += "<BR>" + fxeditor.diaries;
				}
				
				if (fxeditor.footer != "") {
					// footer追加
					html += "<BR>" + fxeditor.footer;
				}
				
				var htmlSize = FXEDITOR.getContentSize(html, fxeditor.career);
				
				// プレビューに変更を通知します。
				fxeditor._preview.updateContent(
					FXEDITOR.replaceImage(html, fxeditor.career));
				
				if (isNotifyFlex) {
					// swfに変更を通知します。
					$("main").updateContentHandler(fxeditor.getContent());
				}
				
				$("main").updateSize(htmlSize);
				
		}, interval);
	},
	
	/**
	 * HTMLの内容を取得します。
	 * @return
	 */
	getContent: function () {
		if (this._editor != null) {
			this._editor.updateElement();
			var html = this._editor.getData();
			return FXEDITOR.decodeHtml(html);
		} else {
			return "";
		}
	},
	
	/**
	 * HTMLの内容をセットします。
	 * @param value
	 */
	setContent: function (value) {
		this._contentChanged = true;
		this.content = value;
		this.notifyChange(false);
	},
	
	/**
	 * Headerの内容をセットします。
	 * @param value
	 */
	setHeaderContent: function (value) {
		this.header = value;
		this.notifyChange(false);
	},
	
	/**
	 * Footerの内容をセットします。
	 * @param value
	 */
	setFooterContent: function (value) {
		this.footer = value;
		this.notifyChange(false);
	},
		
	/**
	 * Formの内容をセットします。
	 * @param value
	 */
	setForms: function (value) {
		this.forms = value;
		this.notifyChange();
	},
	
	/**
	 * 日記の内容をセットします。
	 * @param value
	 */
	setDiaries: function (value) {
		this.diaries = value;
		this.notifyChange(false);
	},
	
	/**
	 * キャリアをセットします。
	 * @param value
	 */
	setCareer: function (value) {
		this.career = value;
		this.notifyChange(false);
	},
	
	/**
	 * 表示／非表示をセットします。
	 * @param value
	 */ 
	setVisible: function (value) {
		if (value) {
			this._container.style.display = "block";
		} else {
			this._container.style.display = "none";
		}
		this._preview.setVisible(value);
	},
	
	/**
	 * 現在選択されているSelectionを取得します。
	 * @return
	 */
	getRange: function () {
		return this._editor.document.getSelection().getRanges()[0];
	},

	/**
	 * 背景色アップデート
	 * @param color
	 */
	changeBgcolor: function (color) {
		this._preview.changeBgcolor(color);
	},
	
	/**
	 * テキスト色アップデート
	 * @param color
	 */
	changeTextcolor: function (color) {
		this._preview.changeTextcolor(color);
	},

	/**
	 * リンク色アップデート
	 * @param color
	 */
	changeLinkcolor: function (color) {
		this._preview.changeLinkcolor(color);
	},
	
	/**
	 * アクティブリンク色アップデート
	 * @param color
	 */
	changeAlinkcolor: function (color) {
		this._preview.changeAlinkcolor(color);
	},
	
	/**
	 * 訪問済み色アップデート
	 * @param color
	 */
	changeVlinkcolor: function (color) {
		this._preview.changeVlinkcolor(color);
	},
	
	/**
	 * リンクを挿入します。
	 * @param uri
	 */
	insertLink:function (uri) {
		try {
			var instance = this;
			setTimeout(function(){
				// selectionがまたないとセットされていない
				var aTag = '<a' + ' href="' + uri + '"' + '>';
				aTag += FXEDITOR.getSelectedHtml(instance._editor) + '</a>';
				instance.insertHtml(aTag);
			}, 300);
		} catch (e) {
		}
	},
	
	/**
	 * 画像を挿入します。
	 * @param src
	 * @param alt
	 * @param titl
	 */
	insertImage:function (src, alt, title) {
		var imgTag = '<img' +
			' src="' + src + '"' +
			' alt="' + alt + '"' +
			' title="' + title + '"' +
			'>';
		this.insertHtml(imgTag);
	},
	
	/**
	 * HTMLを挿入します。
	 * @param html
	 */
	insertHtml: function (html) {
		this._editor.insertHtml(html);
		this.notifyChange(true);
	},
	
	/**
	 * mailToを挿入します。
	 * @param address
	 * @param text
	 */
	insertMailTo: function (address, text) {
		var mailtoTag = '<a' + ' href="mailto:' + address + '"' + '>';
		mailtoTag += text;
		mailtoTag += '</a>';
		this.insertHtml(mailtoTag);
	},
	
	/**
	 * 選択をロックする（flexとの連携をしたときに必要かもー）
	 */
	lock: function () {
		var selection = new CKEDITOR.dom.selection(this._editor.document);
		
		if (CKEDITOR.env.ie)
			selection.lock();
		else
			// ffだとロックできていない
			this._savedRanges = selection.getRanges();
	},
	
	/**
	 * 選択ロックを解除する（flexとの連携をしたときに必要かもー）
	 */
	unlock: function () {
		var selection = new CKEDITOR.dom.selection(currentEditor._editor.document);
		
		if (CKEDITOR.env.ie)
			selection.unlock();
		else {
			var instance = this;
			setTimeout(function(){
				// 待たないとselectできない
				selection.selectRanges(instance._savedRanges);
			}, 100);
		}
	},
	
	
	//==========================================================================
	// private method
	//==========================================================================
	
	_createEditor: function() {
		this._editor = CKEDITOR.replace(this._textarea, 
			{customConfig: CKEDITOR.getUrl(this._ckconfig)});
		
	    var adaptor = this;
	    
	    
		this._editor.on("instanceReady", function(e) {
			$("main").editorCreated();
			adaptor.notifyChange(false);
		});
	    
		this._editor.on('key', function(event) {
			if (event.data.keyCode == 9 ||　event.data.keyCode == 2009) {
				// tabが押された時はフォーカスを外す
				event.editor._lastKeyCode = event.data.keyCode;
				event.editor.focusManager.blur();
			}
			adaptor.notifyChange(true);
		});
		
		this._editor.on('selectionChange', function(event) {
			adaptor.notifyChange(true);
		});
		
		this._editor.on('blur', function(event) {
			if (event.editor._lastKeyCode == 9) {
				// tabの監視
				$("main").focus();
				$("main").editorFocusOut("forward");
			} else if (event.editor._lastKeyCode == 2009) {
				// shift+tabの監視
				$("main").focus();
				$("main").editorFocusOut("backward");
			}
			adaptor.notifyChange(true);
		});
		
		this._editor.on('focus', function(event){
			$("main").editorFocusIn();
		});
		
		// window.eventオブジェクトが無いブラウザ対応
		if (!window.event) {
			(function(){
				// 全てのマウスイベントを登録
				for (var property in Event.prototype){
					if(property.match(/MOUSE|CLICK/)){
						window.addEventListener(property.toLowerCase(), function(e){
							window.event = e;
						}, true);
					}
				}
			}());
		}
		
	},
	
	/**
	 * エディター領域を作成します。
	 * @private
	 * @return
	 */
	_createContainer: function () {
		var bbody = document.getElementsByTagName("body").item(0);
		var element = document.createElement("div");
		element.style.position = "absolute";
		element.style.display = "none";
		element.style.zIndex = 100;
		bbody.appendChild(element);
		return element;
	},
	
	/**
	 * テキストエリアを作成します。
	 * @private
	 */
	_createTextArea: function () {
		var element = document.createElement("textarea");
		element.setAttribute("id", this.id);
		element.setAttribute("name", this.id);
		return element;
	},

	_createPreview: function () {
		return new FlexPreviewAdaptor();
	}
};

/**
 * CKEditorによってHTML encoding された物を元に戻す.
 * TODO 現状全ての物をデコードしていません
 */
FXEDITOR.decodeHtml = function(html) {
	var result = html;
	if (result == null) return result;
	result = result.replace(/&quot;/g, '"');
	result = result.replace(/[\t\n]/g, '');
	// CKEditorから取得できるContentは何故か数字がURLEncodeされてるのでdecodeしたげる
	result = result.replace(/&#48;/g, '0');
	result = result.replace(/&#49;/g, '1');
	result = result.replace(/&#50;/g, '2');
	result = result.replace(/&#51;/g, '3');
	result = result.replace(/&#52;/g, '4');
	result = result.replace(/&#53;/g, '5');
	result = result.replace(/&#54;/g, '6');
	result = result.replace(/&#55;/g, '7');
	result = result.replace(/&#56;/g, '8');
	result = result.replace(/&#57;/g, '9');
	
	result = result.replace(/&amp;/g, '&');
	return result;
};


/**
 * 絵文字を<img>タグに置き換えます。
 * @param {Object} html
 */
FXEDITOR.replaceImage = function(html, career) {
	if (career == null) career = 'd';
	var images = html.match(/\{\"[0-9]+\"\|emoji\}/g);
	if (!images || !emojiMap) return html;
	images.each(function(image) {
		if (emojiMap[image.match(/[0-9]+/)]) {
			var src = emojiMap[image.match(/[0-9]+/)][career];
			html = html.replace(image, '<img src="' + src + '" width="5" height="5" brder="0" style="width: 16px; height: 16px;" />');
		}
	});

	// 店舗用の置き換え文字列
	var replaceString = {
		"{$signPageUrl}":"http://xxx.your-site.jp/p/x.html&g=xxxxx",
		"{$resignPageUrl}":"http://xxx.your-site.jp/resign/resign.html&g=xxxx",
		"{$userName}":"山田太郎",
		"{$shopName}":"新宿店",
		"{$shopZip}":"1010001",
		"{$shopAddress}":"東京都新宿区○○○1-1-1 ○○○ビル1F",
		"{$shopTel}":"0312345678",
		"{$shopFax}":"0312345679",
		"{$shopMail}":"info@your-site.jp"
	}

	for (var key in replaceString) {
		html = html.replace(key, replaceString[key]);
	}

	return html;
};

FXEDITOR.htmlSize = 0;
FXEDITOR.getContentSize = function(html, career) {
	//TotalSize
	FXEDITOR.htmlSize = 0;
	
	//絵文字のサイズ
	var images = html.match(/\{\"[0-9]+\"\|emoji\}/g);
	if (images){
		images.each(function(image) {
			if(career == 'a'){//AU
				FXEDITOR.htmlSize += 20;
			}else{//docomo,SoftBank
				FXEDITOR.htmlSize += 2;
			}
			//絵文字Tagは削除
			html = html.replace(image, '');
		});
	}

	//画像サイズ
	var imgSrcs = html.match(/src=\"\S+\"/g);
	if (imgSrcs){
		FXEDITOR.htmlSize += FXEDITOR.getImageSize(imgSrcs);
	}
	
	//実際に生成されるＨＴＭＬのHeader(Meta)のサイズ（全ページ共通）<HTML><HEAD><meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS"><meta name="Expires-Of-Contents" content="2008/08/18-2009/08/18" /><TITLE></TITLE></HEAD><BODY BGCOLOR="#ffffff"></BODY></HTML>
	FXEDITOR.htmlSize += 210;
	
	//<TITLE>XXXX</TITLE>のXXXXのサイズも
//	html = html + $('pageName').value;
	
	//残ったTag,Textのサイズ
	FXEDITOR.htmlSize += FXEDITOR.getHtmlTagLength(html);
	
	//その他
	FXEDITOR.htmlSize += FXEDITOR.plusContentSize();
	
	return FXEDITOR.htmlSize;
}

var backUpImgTag = new Array();
/**
* 指定されたImage（URLから）のサイズ（Byte）を取得する。
* @param {Object} imgSrcs
*/
FXEDITOR.getImageSize = function (imgSrcs) {
	
	var size = "0";
	//ImageTagをBackupして、変更がなかった場合は、ImageSize取得Ajaxをよばない。
	if(backUpImgTag.toString() == imgSrcs.toString()){
		return Number(size);
	}else{

		backUpImgTag = imgSrcs;

		var count = 0;
		var param = new Object();
		imgSrcs.each(function(src) {
			src = src.replace('src=','');
			src = src.replace(/\"/g,'');
			count++;
			param['url' + count] = encodeURIComponent(src);
		});
		param['count'] = count;
		
		Kumu.Ajax.executeTeedaAjax(
			ajaxPage_ajaxImgSize,
			[],
			Kumu.Ajax.RESPONSE_TYPE_JSON
		);
		
		return Number(size);
	}
}

function ajaxPage_ajaxImgSize(response) {
	FXEDITOR.htmlSize += Number(response);
}



/**
* HtmlTag（String）のサイズを計算する。
*/
FXEDITOR.getHtmlTagLength = function (html) {
	var cSize = 0;
	var temp = html;
	var escTemp;
	if(temp != "" & temp != null){
		for(var i = 0;i < temp.length;i++){
			if (temp.charAt(i)!= escape(temp.charAt(i))){
				escTemp = escape(temp.charAt(i));//日本語をescapeすると、6文字以上
				if (escTemp.length >= 6){
					cSize += 2;
				}else{
					cSize += 1;
				}
			}else{
				cSize += 1;
			}
		}
	}
	return cSize;
}

/**
* ページ追加されTagサイズ（Defaultは0）
* 必要な時は、Overrideすること
*/
FXEDITOR.plusContentSize = function () {
	return 0;
}

FXEDITOR.getSelectedHtml = function(editor)
{
   var selection = editor.getSelection();
   if( selection )
   {
      var bookmarks = selection.createBookmarks();
	     range = selection.getRanges()[ 0 ];
	
	     fragment = range.clone().cloneContents();
	
      selection.selectBookmarks( bookmarks );

      var retval = "",
         childList = fragment.getChildren(),
         childCount = childList.count();
      
      for ( var i = 0; i < childCount; i++ )
      {
         var child = childList.getItem( i );
         retval += ( child.getOuterHtml?
            child.getOuterHtml() : child.getText() );
      }
      return retval;
   }
};



var logger = function(SomeClass){
    var NewClass = function(){
        throw new Error("This is Singleton-Pattern Class. Use self.getInstance().");
    };
    NewClass.__instance__ = null;
    NewClass.getInstance = function(){
        if (this.__instance__ === null)
            this.__instance__ = applyNew(SomeClass, arguments);
        return this.__instance__;
    };
    
    NewClass.info = function (mes) {
        if (this.__instance__ === null)
            this.__instance__ = applyNew(SomeClass, arguments);
    	this.__instance__._logger.info(mes);
    };
    
    NewClass.error = function (mes) {
        if (this.__instance__ === null)
            this.__instance__ = applyNew(SomeClass, arguments);
    	this.__instance__._logger.error(mes);
    };
    
    NewClass.debug = function (mes) {
        if (this.__instance__ === null)
            this.__instance__ = applyNew(SomeClass, arguments);
    	this.__instance__._logger.debug(mes);
    };
    
    function applyNew(cls, args){
        var Tmp = function(){};
        Tmp.prototype = cls.prototype;
        var instance = new Tmp;
        cls.apply(instance, args || []);
        return instance;
    };
    return NewClass;
};
