/*
	lib_AEK.js
	AEキーフレームからXPSオブジェクトに対する変換ライブラリ

	XPS2AEKは特に重要

AEK2XDS(dataStream)
	AEキーフレームデータを挿入可能なデータストリームに変換する。
	AEのキーフレームテキストを、複数連ねたデータを受け入れる。
	その場合最初のデータから順にA,B,C,…の順に積む
	コンバートに失敗した場合は""ヌルストリングを返す。

XPS2AEK(myXps,myOptions)
	XPSオブジェクト又はソースストリームからAEのキーフレームテキストに変換する。
	オプションは、オブジェクト
	変換するレイヤの指定　（単独又は全て）
	エクスプレッションの有無
	操作スクリプトの有無などを指定可能…にしたいね
*/
/*
	XPS ファイルから オブジェクトの初期化
	AEKeyデータ読み込み
		メソッドか?それとも関数か?
	2005/03/20
 */
/*
結局は、XPSのメソッドがよかろうと思うのであったよ。
でも、出力部分は分けないと汎用性が下がりそうだよね、と
AEKey 読み取り部分追加何とか動く 05/04/26
AEKey イロイロ補正 おおかた大丈夫そう? 12/11 次は6.5対応だけどー…
AEKey bTimeLine 参照違い バグ修正 1/31
AERemap/T-Sheet読み込み部分コーディング でも 泥縄 5/12
readIN メソッドはメッセージ分離してXpsオブジェクトのメソッドに移送済み
ssUnit と ckBlank も引越先を考慮中 (7/7/9)

Xps.readINメソッドは、フォーマットをXpsに限定
判定部分はXPSの同名オブジェクトでラップ
AEKey関連関数は、lib_AEK.js　として分離　2013.04.06

 */
/*
	ssUnit(UpS)
	サブユニット長を自動設定して戻す
	引数 UpS は、Units Per Second・秒あたりのフレーム数 または キーワード
	戻り値は、フレームレートにしたがって自動設定されるサブユニットの長さ
	サブセパレータの値とは別。
 */
function ssUnit(UpS){
if (isNaN(UpS))
{
	switch (UpS)
	{
	case "NTSC"	:	return 6;break;
	case "PAL"	:	return 5;break;
	case "drop"	:	return 6;break;
	default	:	return UpS;
	}
}else{
	UpS=Math.round(UpS);//	ドロップフレーム系の処置・どのみち整数でないとイヤだけど、暫定で
	for (ssu=4;ssu>1;ssu--){if(UpS%ssu==0)return UpS/ssu;}
	return UpS;
}
//	4から1へ順に約数をあたる。マッチした時点で返す。
//	すべて失敗した場合は、元の数値を返す。
}
/*
	ckBlank(timeLine)
	制御レイヤ(現在カラセル制御のみ)の判定
	判定するtimelineオブジェクトを与える。
	すべての値が 0 || 100 	ならばカラセルレイヤであると判定

	現在はブーリアンで返しているが、要調整か?
 */
function ckBlank(timeLine){
	for(xid=0;xid<timeLine.length;xid ++){
	if(timeLine[xid].value[0]%100 != 0){return false}
	}
return true;
}

//
var thisComp=null;
var thisLayer=null;
var thisTimeLine=null;
//



function AEK2XDS(datastream){

//AE-Key data encoder
//AEキーの挿入は外部でストリームを組み立ててputする方式に変更する
//	///////////////////////
//データ冒頭の空白文字を削除
	datastream=datastream.replace(/^\s*/,"");
	if(	(! datastream.toString().length )||
		( false )
	){return ""};//不正データ時処理

//alert(datastream);
//ラインで分割して配列に取り込み
	var SrcData=new Array();
if(datastream.match(/\r/)){datastream=datastream.replace(/\r\n?/g,("\n"));};

	SrcData=datastream.split("\n");

//ソースデータのプロパティ
//	SrcData.layerHeader	=0;//レイヤヘッダ開始行
//	SrcData.layerProps	=0;//レイヤプロパティエントリ数
	SrcData.layerCount	=0;//レイヤ数
	SrcData.layers= new Array();//レイヤ情報トレーラー
//	SrcData.layerBodyEnd	=0;//レイヤ情報終了行
	SrcData.frameCount	=0;//読み取りフレーム数

//	仮にデータを取得するコンポを初期化
		thisComp= new FakeComposition();
		thisComp.maxFrame=0;//キーの最大時間を取得するプロパティを初期化
		ly_id	=0;//レイヤID初期化
		tl_id	=0;//タイムラインID初期化
		kf_id	=0;//キーフレームID初期化 いらないか?

//		第一パス開始
//	データをスキャンしてコンポ(オブジェクト)に格納
	for(line=0;line<SrcData.length;line++)
	{
			// キーデータに含まれるレイヤ情報の取得
		if(MSIE){
	var choped=SrcData[line].charCodeAt(SrcData[line].length-1);
	if(choped<=32) SrcData[line] = SrcData[line].slice(0,-1);
		}
		//データ前処理・なぜだかナゾ、なぜに一文字多いのか?

//空白行のスキップ
	if(SrcData[line]=='') continue;

//一番エントリの多いデータ行を最初に処理
	if( SrcData[line].match(/^\t.*/) )
	{
//if(dbg) dbgPut("\tDATALINEs\nLayer No."+ly_id+" TimeLineID :"+tl_id+ " "+line+":"+SrcData[line]);
		var SrcLine = SrcData[line].split("\t");

		if(SrcLine[1]=="Frame") continue;//フィールドタイトル行スキップ

		if (tl_id==0){ //レイヤ内で一度もタイムラインを処理していない。
//if(dbg) dbgPut(SrcLine);

//レイヤヘッダなのでレイヤのプロパティを検証してオブジェクトに登録
switch (SrcLine[1])
{
case	"Units\ Per\ Second"	:			;//コンポフレームレート
	thisComp.frameRate	= SrcLine[2]		; break;
	//この部分をこのまま放置するとコンポのフレームレートが、最後のレイヤで決定されるので注意。

case	"Source\ Width"	:				;//レイヤソース幅
	thisLayer.width	= SrcLine[2]	; break;
case	"Source\ Height"	:			;//レイヤソース高さ
	thisLayer.height	= SrcLine[2]	; break;
case	"Source\ Pixel\ Aspect\ Ratio"	:		;//ソースの縦横比
	thisLayer.pixelAspect	= SrcLine[2]	; break;
case	"Comp\ Pixel\ Aspect\ Ratio"	:		;//コンポの縦横比
	thisComp.pixelAspect	= SrcLine[2]		; break;
default:				;//時間関連以外
	thisLayer[SrcLine[1]]	= SrcLine[2]	; break;
//	判定した値をレイヤのプロパティに控える。
}
		}else{
//タイムラインデータなのでアクティブなタイムラインに登録
//if(dbg) dbgPut("timelinedata line No."+line+":"+SrcData[line]);
	frame=SrcLine[1]*1;
		if(frame > thisComp.maxFrame) thisComp.maxFrame=frame;
		//	キーフレームの最大時間を記録
	value=SrcLine.slice(2,SrcLine.length-1);
//	value=SrcLine.slice(2);

//	タイムラインの最大値を控える 999999 は予約値なのでパス
//	実際問題ここで控えた方が良いのかこれは?
//	if (thisTimeLine.maxValue<value && value < 999999)
//	thisTimeLine.maxValue=value;

//result=thisTimeLine.push(new KeyFrame(frame,value));
//thisComp.layers[ly_id][tl_id][kf_id] = new KeyFrame(frame,value);
//kf_id ++;
//result=thisComp.layers[ly_id][tl_id].setKeyFrame(new KeyFrame(frame,value));

	thisComp.layers[ly_id][tl_id].push(new KeyFrame(frame,value));
	result=thisComp.layers[ly_id][tl_id].length;

//	if(dbg) dbgPut(">>set "+thisComp.layers[ly_id][tl_id].name+
//	" frame:"+frame+"  to value:"+value+"<<"+result+
//	"::and maxFrame is :" + thisComp.maxFrame);

//if(dbg) dbgPut(">>> "+ thisComp.layers[ly_id][tl_id][kf_id].frame +"<<<");
		}

	continue;//次の判定は、当然パスして次の行を処理
	};

//レイヤ開始判定
	if(SrcData[line].match(/^Adobe\ After\ Effects\x20([456]\.[015])\ Keyframe\ Data$/)){
//if(dbg) dbgPut("\n\nNew Layer INIT "+l+":"+SrcData[line]);
		//レイヤ作成
		thisComp.layers[ly_id]=new FakeLayer();
//		thisComp.layers[ly_id].init();
		thisLayer=thisComp.layers[ly_id];//ポインタ設定

		continue;
	}
//		タイムライン開始判定または、レイヤ終了
	if( SrcData[line].match(/^[\S]/))
	{
//　タイムライン終了処理があればここに
// レイヤ終了処理
//if(dbg)	dbgPut(line+" : "+SrcData[line]);
		if(SrcData[line].match(/^End\ of\ Keyframe\ Data/))
		{
//			thisComp.setFrameDuration()
			ly_id ++ ;tl_id	=0;kf_id=0;
		//レイヤIDインクリメント・タイムラインID初期化
		}else{

//	最上位階層はデータブロックのセパレータなので読み取り対象を切り換え	//	タイムラインを判定して作成


//	if(! SrcData[line].match(/^\s*$/)){}

//新規タイムライン設定

	SrcLine=SrcData[line].split("\t");

switch (SrcLine[0])
{
case	"Time\ Remap":	tl_id="timeRemap";
	break;
case	"Anchor\ Point":	tl_id="anchorPoint";
	break;
case	"Position":	tl_id="position";
	break;
case	"Scale":	tl_id="scale";
	break;
case	"Rotation":	tl_id="rotation";
	break;
case	"Opacity":	tl_id="opacity";
	break;
case	"変換終了":	tl_id="wipe";//AE 4.0-5.5 wipe/トランジション
	break;
case "スライダ":	tl_id="slider";//AE 4.0-5.5 スライダ制御
	break;
case	"Effects":	//AE 6.5 (6.0? 要確認) エフェクトヘッダサブ判定が必要
	switch (SrcLine[1].slice("\ ")[0])
	{
	case "変換終了":	tl_id="wipe";break;
	case "スライダ制御":	tl_id="slider";break;
//	case "":	tl_id="";break;
//	case "":	tl_id="";break;
//	case "":	tl_id="";break;
	defaulet:	tlid=SrcLine[1];
	}
	break;
default:
	tlid=SrcLine[0];
}

//	if(! thisComp.layers[ly_id][tl_id]){thisComp.layers[ly_id][tl_id]= new TimeLine(tl_id)}else{if(dbg) dbgPut(tl_id)}

	if(! thisComp.layers[ly_id][tl_id])
	{
		thisComp.layers[ly_id][tl_id]= new Array();
		thisComp.layers[ly_id][tl_id].name=[tl_id];
		thisComp.layers[ly_id][tl_id].maxValue=0;
		thisComp.layers[ly_id][tl_id].valueAtTime=valueAtTime_;
	};//else{	if(dbg) dbgPut(tl_id + " is exist")	}
//			なければ作る＝すでにあるタイムラインならスキップ
		thisTimeLine=thisComp.layers[ly_id][tl_id];
//		if(dbg) dbgPut("set TIMELINE :"+ly_id+":"+tl_id);
		continue;
		}
	continue;
	}


	}

//		all_AEfake();
//	キーの読み込みが終わったのでキーデータを解析
//キーの最後のフレームをみて、カットの継続時間を割り出す。
	thisComp.duration=
	nas.FCT2ms(
		ssUnit(thisComp.frameRate)*
		Math.ceil(thisComp.maxFrame/ssUnit(thisComp.frameRate))
	)/1000;//最小単位はキリの良いところで設定

//
//タイムラインをチェックしてタイミング情報を抽出
//レイヤでループ
for (var lyr=0;lyr < thisComp.layers.length;lyr++){
/*
	コンポジションのレイヤ情報を読んで、変換のパラメータを判定する
	現在認識して読み取るタイムライン
timeRemap	タイミング情報有り
	slider	タイミング情報の可能性有り
opacity	タイミング情報の可能性有り
wipe	タイミング情報の可能性有り
**カメラワーク判定は、現在なし 常にfalse

*/
//ソースデータ用情報トレーラ
		SrcData.layers[lyr]=new Object();
//　初期化
	SrcData.layers[lyr].haveTimingData	=false;
	SrcData.layers[lyr].haveCameraWork	=false;

//メソッド・位置をデフォルトに設定
	SrcData.layers[lyr].blmtd=xUI.blmtd;
	SrcData.layers[lyr].blpos=xUI.blpos;
	SrcData.layers[lyr].blmtd="wipe";
	SrcData.layers[lyr].blpos="first";
	SrcData.layers[lyr].lot="=AUTO=";
		//仮のブランクレイヤ
		SrcData.layers[lyr].bTimeLine=false;
		SrcData.layers[lyr].tBlank=false;
//リマップはある?
	if (thisComp.layers[lyr].timeRemap)
	{
		SrcData.layers[lyr].haveTimingData	=true;

//カラセル制御レイヤはあるか
if (thisComp.layers[lyr].opacity){
		if(ckBlank(thisComp.layers[lyr].opacity)){
	SrcData.layers[lyr].blmtd="opacity";
	SrcData.layers[lyr].blpos="end";
		//仮のブランクレイヤ
		SrcData.layers[lyr].bTimeLine=thisComp.layers[lyr].opacity;
		SrcData.layers[lyr].tBlank=0;
//alert("hasBlankOpacity");
		}
}else{
if (thisComp.layers[lyr].wipe){
		if(ckBlank(thisComp.layers[lyr].wipe)){
	SrcData.layers[lyr].blmtd="wipe";
	SrcData.layers[lyr].blpos="end";
		//仮のブランクレイヤ
		SrcData.layers[lyr].bTimeLine=thisComp.layers[lyr].wipe;
		SrcData.layers[lyr].tBlank=100;
//alert("hasBlankWipe");
		}
}
}
//キーを全数検査
	var isExpression=false	;//エクスプレッションフラグ
	var MaxValue=0	;//最大値を控える変数
	var blAP=false	;//カラセル出現フラグ
	var tmpBlank=(SrcData.layers[lyr].blmtd=="opacity")? 0 : 100 ;//仮のブランク値
for (kid=0;kid<thisComp.layers[lyr].timeRemap.length;kid++){
	if(thisComp.layers[lyr].timeRemap[kid].value[0] >= 999999){
		isExpression=true;
		blAP=true;
	};//これが最優先(最後に判定して上書き)
//最大値を取得
	if(	MaxValue< 1 * thisComp.layers[lyr].timeRemap[kid].value[0] &&
		1 * thisComp.layers[lyr].timeRemap[kid].value[0] < 999999)
	{
	MaxValue=1*thisComp.layers[lyr].timeRemap[kid].value[0];

//最大値が更新されたらキーに対応するカラセル制御をチェック
		if(SrcData.layers[lyr].bTimeLine){
//制御ラインあるか
//キーフレームの位置にブランク指定があれば、そこをブランク値に設定
if(SrcData.layers[lyr].bTimeLine.valueAtTime(thisComp.layers[lyr].timeRemap[kid].frame)==SrcData.layers[lyr].tBlank){
	blAP=true;//カラセル出現
}
		}

	}
}
	if(isExpression){
		SrcData.layers[lyr].blmtd="expression2";
		SrcData.layers[lyr].blpos="end";
	}

	SrcData.layers[lyr].maxValue=MaxValue;

//	フレームレート取り出し
var FrameDuration=(thisComp.layers[lyr].frameDuration)?
	thisComp.layers[lyr].frameDuration :
	thisComp.frameDuration();
//	セル枚数推定
switch(SrcData.layers[lyr].blpos){
case "end":
	SrcData.layers[lyr].lot=(blAP)?
		Math.floor(MaxValue/FrameDuration):
		Math.floor(MaxValue/FrameDuration)+1	;//end
	if(isExpression && blAP)	SrcData.layers[lyr].lot++;
//		SrcData.layers[lyr].hasBlank=blAP;
	break;
case "first":
	SrcData.layers[lyr].lot=
		Math.floor(MaxValue/FrameDuration);//first
	break;
case "none":
default:
//	SrcData.layers[lyr].lot="=AUTO=";//end && MaxValue==0
}
	}else{
//	スライダ制御はある?
		if (thisComp.layers[lyr].slider)
		{
/*	スライダ=エクスプレッションの可能性有り
エクスプレッションだとするとexpression1なので、
同一レイヤにタイムラインが二つ以上あってはならないものとする。
が、二つ目以降のスライダは、現在正常に読めない。混ざる

そのうち何とかする
*/
//キーを全検査する。
	var MaxValue=0;
	var isTiming=true;
for (kid=0;kid<thisComp.layers[lyr].slider.length;kid++){
//	整数か
	if(thisComp.layers[lyr].slider[kid].value[0] %1 != 0) {isTiming=false;break;}

//最大値を取得
	if(MaxValue<1*thisComp.layers[lyr].slider[kid].value[0])
	{MaxValue=thisComp.layers[lyr].slider[kid].value[0]}
}
//すべて整数値ならば一応エクスプレッションによるタイミングと認識
if (isTiming){
	SrcData.layers[lyr].haveTimingData	=true;		
	SrcData.layers[lyr].blmtd="expression1";
	SrcData.layers[lyr].blpos="first";
	SrcData.layers[lyr].lot=MaxValue;
	SrcData.layers[lyr].maxValue=MaxValue;
}
//			
		}
	}
//両方の判定を抜けたならタイミング情報がないのでこのレイヤはただの空レイヤ

/*
//	タイミングだと思われる場合はフラグ立てる。
//case	"slider":
//case	"timeRemap":	;break;
//キーを全数検査する。
//制御レイヤが付属していたらそちらを優先させる。
//制御レイヤの値とリマップの値を比較してカラセルメソッドとポジションを出す

//タイムリマップとスライダの時のみの判定
//値の最大量を控える
if(SrcData.layers[ly_id].maxValue<value) SrcData.layers[ly_id].maxValue= value;

//スライダかつ整数以外の値があるときは削除フラグを立てる
if(tl_id=="slider" && value%1 != 0) SrcData.layers[ly_id].isExpression=false;

//タイムリマップでかつ値に"999999"がある場合はメソッドをexp2に
if(tlid=="timeRemap" && value==999999) SrcData.leyers[ly_id].blmtd="exp2";

*/

}
//	解析したプロパティの転記
// alert(thisComp.layers.length+" : "+thisComp.duration*thisComp.frameRate)
//var myXps=new Xps(thisComp.layers.length,thisComp.duration*thisComp.frameRate);

SrcData.mapfile	="(no file)";
SrcData.title	="";
SrcData.subtitle="";
SrcData.opus	="";
SrcData.scene	="";
SrcData.cut	="";
SrcData.create_user	="";
SrcData.update_user	="";
SrcData.create_time	="";
SrcData.update_time	="";
SrcData.framerate	=thisComp.frameRate;
SrcData.layerCount	=thisComp.layers.length;
SrcData.memo	="";
SrcData.time	=thisComp.duration*thisComp.frameRate;//読み取り
SrcData.trin	=[0,"trin"];
SrcData.trout	=[0,"trout"];//キーフレームからは読まない(ユーザが後で指定)

//	SrcData.frameCount	=;
//	SrcData.	="";
//	SrcData.	="";
//	SrcData.	="";	
//	SrcData.	="";


/*
	タイムリマップとスライダ制御の両方がない場合は、
	レイヤは「camerawork」(保留)
	スライダ制御があって、かつデータエントリーがすべて整数の場合は、
	exp1 それ以外はスライダ制御を破棄
	スライダ制御とタイムリマップが両方ある場合はタイムリマップ優先

	
*/




//読み出したAEオブジェクトから情報を再構成する
	var preValue='';//直前の値を控えておく変数
if(true)	{
//	var AETransStream=new String();//リザルト文字列の初期化
	var AETransStream="";//リザルト文字列の初期化
	var AETransArray=new Array(SrcData.layerCount);//
	for(layer=0;layer<SrcData.layerCount;layer++){
		AETransArray[layer]=new Array();
	}
}

for(layer=0;layer<SrcData.layerCount;layer++){
//レイヤ数回す

	timingTL=(SrcData.layers[layer].blmtd=="expression1")? "slider":"timeRemap";//	タイミング保持タイムラインをblmtdで変更


	BlankValue	=(SrcData.layers[layer].blpos=="first")?
		0	:	(SrcData.layers[layer].lot + 1);
//	レイヤごとのブランク値を出す。999999は、パス

	for(kid=0; kid < thisComp.layers[layer][timingTL].length ;kid++)
	{
//タイミング保持タイムラインのキー数で転送
		if (preValue != thisComp.layers[layer][timingTL][kid].value[0])
		{
	frame=thisComp.layers[layer][timingTL][kid].frame;

//キーフレームの存在するコマのみ時間値からセル番号を取り出して転送

	if(xUI.timeShift){
var diffStep = (Math.abs(thisComp.layers[layer][timingTL][kid].value[0] % thisComp.frameDuration()))/thisComp.frameDuration();
timeShift=(diffStep < 0.1)? thisComp.frameDuration()*0.5 : 0;
	}else{
timeShift=0;
	};
	blank_offset =(SrcData.layers[layer].blpos=="first")? 0 : 1;

//あらかじめセル番号を計算
		cellNo=(timingTL=="timeRemap")?
Math.floor((thisComp.layers[layer][timingTL][kid].value[0]*1+timeShift)/thisComp.frameDuration())+blank_offset:
thisComp.layers[layer][timingTL][kid].value[0];
	if (SrcData.layers[layer].blpos=="first"){
		if(cellNo == BlankValue)	{cellNo="X"}
	}else{
		if(cellNo >= BlankValue)	{cellNo="X"}
	}

//	無条件ブランク
if(
	thisComp.layers[layer][timingTL][kid].value[0]==999999 ||
	thisComp.layers[layer][timingTL][kid].value[0]< 0
){cellNo="X"};
if(SrcData.layers[layer].bTimeLine){
	if(SrcData.layers[layer].bTimeLine.valueAtTime(frame)==SrcData.layers[layer].tBlank){cellNo="X"};
};
//if(dbg) dbgPut(thisComp.layers[layer][timingTL][kid].value);

		if(false){
	myXps.xpsBody[layer+1][frame]=cellNo;
		}else{
	AETransArray[layer].push(cellNo.toString());
	if(kid < thisComp.layers[layer][timingTL].length -1)
	{
		var currentframe = thisComp.layers[layer][timingTL][kid].frame;
		var nextframe = thisComp.layers[layer][timingTL][kid+1].frame;
		for(fr=currentframe+1;fr<nextframe;fr++){
			AETransArray[layer].push("");
		}
	}
		}
	}else{
			AETransArray[layer].push("");
	};
	preValue=thisComp.layers[layer][timingTL][kid].value[0];
	};
	preValue='';//1レイヤ終わったら再度初期化
}
//=================この関数内でput処理はしない　データストリームを作って戻すだけに留める

//	リザルト配列の要素数を比較して最も大きなものに合わせる
//	ブロックデータに加工
		var MaxLength=0;
		for(layer=0;layer<SrcData.layerCount;layer++)
		{
	MaxLength=(MaxLength<AETransArray[layer].length)?
	AETransArray[layer].length:MaxLength;
		}
		for(layer=0;layer<SrcData.layerCount;layer++)
		{
	AETransArray[layer].length=MaxLength;
	AETransStream+=AETransArray[layer].join(",");
	if(layer<SrcData.layerCount-1)AETransStream+="\n";
		};

//	xUI.Select=[1,0];
//	xUI.put(AETransStream);

	return AETransStream
}
//=================================================================

//キーフレーム変換メソッド
/*
	キー変換部分はりまぴん的にはヘソだけど、汎用的にはNG
	この部分は、コンバータとして置き換えが必要
	xUI又は別LIBに引っ越すべき　2013.04.05
*/
//これはAEだと不要か？
//Xps.prototype.mkAEKey=function(layer_id){};

XPS2AEK=function(myXps,layer_id){
//将来、データツリー構造が拡張された場合、機能開始時点でツリーの仮構築必須
//現在は、決め打ち
//内部処理に必要な環境を作成
var	layerDataArray	=myXps.xpsBody[layer_id+1];
		layerDataArray.label=myXps.layers[layer_id].name;
var	blank_method	=myXps.layers[layer_id].blmtd;
var	blank_pos	=myXps.layers[layer_id].blpos;
var	key_method	=xUI.keyMethod;
var	key_max_lot	=(isNaN(myXps.layers[layer_id].lot))?
			0 : myXps.layers[layer_id].lot;

var	bflag=(blank_pos)? false : true ;//ブランク処理フラグ
//
//
var	AE_version	=xUI.aeVersion;
var	compFramerate	=myXps.framerate;
var	footageFramerate=xUI.fpsF;
	if(isNaN(footageFramerate)){footageFramerate=compFramerate}
var	sizeX	=myXps.layers[layer_id].sizeX;
var	sizeY	=myXps.layers[layer_id].sizeY;
var	aspect	=myXps.layers[layer_id].aspect;
//alert("カラセル方式は :"+blank_method+"\n フーテージのフレームレートは :"+footageFramerate);

	var layer_max_lot=0;//レイヤロット変数の初期化

//事前処理 AEVersionが6.5以降でキータイプがexpression3ならばを強制変更 未処理

//前処理 シート配列からキー変換前にフルフレーム有効データの配列を作る
//全フレーム分のバッファ配列を作る
	var bufDataArray=myXps.getNormarizedStream(layer_id);
if(false){
//第一フレーム評価・エントリが無効な場合空フレームを設定
	bufDataArray[0]= (dataCheck(layerDataArray[0],layerDataArray.label,bflag))?
	dataCheck(layerDataArray[0],layerDataArray.label,bflag):"blank";

//2?ラストフレームループ
	for (f=1;f<layerDataArray.length;f++){
//有効データを判定して無効データエントリを直前のコピーで埋める
	bufDataArray[f]=(dataCheck(layerDataArray[f],layerDataArray.label,bflag))?
	dataCheck(layerDataArray[f],layerDataArray.label,bflag):bufDataArray[f-1];

		if (bufDataArray[f]!="blank")
		{
			layer_max_lot=(layer_max_lot>bufDataArray[f]) ?
			layer_max_lot : bufDataArray[f] ;
		}
	}
	max_lot = (layer_max_lot>key_max_lot)?
	layer_max_lot:key_max_lot;

//あらかじめ与えられた最大ロット変数と有効データ中の最大の値を比較して
//大きいほうをとる
//ここで、layer_max_lot が 0 であった場合変換すべきデータが無いので処理中断

	if(layer_max_lot==0){
	xUI.errorCode=4;return	;
// "変換すべきデータがありません。\n処理を中断します。";
	}
}
//前処理第二 (配列には、キーを作成するフレームを積む)
//キースタック配列を宣言
	var keyStackArray=new Array;//キースタックは可変長
	keyStackArray["remap"]= new Array();
	keyStackArray["blank"]= new Array();
		//ふたつ リマップキー/ブランクキー 用

	keyStackArray["remap"].push(0);
	keyStackArray["blank"].push(0);//最初のフレームには無条件でキーを作成

//有効データで埋まった配列を再評価(2?ラスト)
	for (f=1;f<bufDataArray.length;f++){
//キーオプションにしたがって以下の評価でキー配列にスタック(フレームのみ)
switch (key_method){
case	"opt"	:	//	最適化キー(変化点の前後にキー)
			//	○前データと同じで、かつ後ろのデータと
			//	同一のエントリをスキップ
	if (bufDataArray[f]!=bufDataArray[f-1] || bufDataArray[f]!=bufDataArray[f+1])
	{keyStackArray["remap"].push(f)}
	break;
case	"min"	:	//	最少キー(変化点の前後にキー)
			//	○前データと同じエントリをスキップ
	if (bufDataArray[f]!=bufDataArray[f-1])
	{keyStackArray["remap"].push(f)}
	break;
case	"max"	:	//	全フレームキー(スキップ無し)
default:
	keyStackArray["remap"].push(f);
}	
//ブランクメソッドにしたがってブランクキーをスタック(フレームのみ)
	var prevalue	=(bufDataArray[f-1]	=="blank")?	"blank":"cell";
	var currentvalue=(bufDataArray[f]	=="blank")?	"blank":"cell";
	var postvalue	=(bufDataArray[f+1]	=="blank")?	"blank":"cell";
switch (key_method){
case	"opt"	:	//	最適化キー(変化点の前後にキー)
	if (currentvalue!=prevalue || currentvalue!=postvalue)
	{keyStackArray["blank"].push(f)}
	break;
case	"min"	:	//	最少キー(変化点の前後にキー)
	if (currentvalue!=prevalue)
	{keyStackArray["blank"].push(f)}
	break;
case	"max"	:	//	全フレームキー(スキップ無し)
default:
	keyStackArray["blank"].push(f);
}	
	}

//キー文字列を作成
//blankoffsetは、カラセル挿入によるタイミングの遷移量・冒頭挿入以外は基本的に0
switch (blank_pos){
case	"first"	:var blankoffset=1;break;
case	"end"	:var blankoffset=0;break;
case	"none"	:var blankoffset=0;break;
default	:var blankoffset=0;
}
var footage_frame_duration=(1/footageFramerate);
//	リマップキーを作成
	var remapBody=
'Time Remap\n\tFrame\tseconds\t\n';
	for (n=0;n<keyStackArray["remap"].length;n++)
	{
	if(bufDataArray[keyStackArray["remap"][n]]=="blank")
	{	var seedValue=(blank_pos=="first")? 1 : max_lot+1 ;
	}else{	var seedValue=bufDataArray[keyStackArray["remap"][n]]*1 + blankoffset;
	}
	remapBody+="\t";
	remapBody+=keyStackArray["remap"][n].toString(10);
	remapBody+="\t";
if(	blank_method=="expression2" && 
	bufDataArray[keyStackArray["remap"][n]]=="blank"){
	remapBody+=999999;//エクスプレッション2のカラ
}else{
	remapBody+=(seedValue-0.5)*footage_frame_duration;//通常処理
}
	remapBody+="\t\n";
	}

//	エクスプレッション型
	var expBody=(AE_version<6.0)?
'スライダ\tスライダ制御\tEffect\ Parameter\ #1\t\n\tFrame\t\t\n':
'Effects\tスライダ制御\tスライダ\n\tFrame\t\t\n';

	for (n=0;n<keyStackArray["remap"].length;n++)
	{
	expBody+="\t";
	expBody+=keyStackArray["remap"][n].toString(10);
	expBody+="\t";
	if (bufDataArray[keyStackArray["remap"][n]]=="blank")
	{	expBody+=("0");

	}else{	expBody+=(bufDataArray[keyStackArray["remap"][n]]);
	}
	expBody+="\t\n";
	}

//	ブランクキーを作成
//	エクスプレッション型/ブランクセル無し の場合は不要
switch(blank_method){
case "opacity":
//不透明度
	var blankBody=
'Opacity\n\tFrame\tpercent\t\n';
	var blank_='0';
	var cell_='100';
	break;
case "wipe":
//ワイプ
	var blankBody=(AE_version<6.0)?
'変換終了\tリニアワイプ\tEffect\ Parameter\ \#1\t\n\tFrame\tパーセント\t\n':
'Effects\tリニアワイプ\t変換終了\n\tFrame\tpercent\t\n';
	var blank_='100';
	var cell_='0';
	break;
}
//
	for (n=0;n<keyStackArray["blank"].length;n++)
	{

	blankBody+="\t";
	blankBody+=keyStackArray["blank"][n].toString(10);
	blankBody+="\t";
		if(bufDataArray[keyStackArray["blank"][n]]=="blank")
		{blankBody+=blank_}else{blankBody+=cell_};
	blankBody+="\t\n";
	}

//AE6.5以降のエクスプレッションペースト可能な際のエクスプレッション

if(AE_version>=6.5){
//前方キー参照
	var keepPreviewKeyValue='Expression Data\n';
	keepPreviewKeyValue+='if(numKeys){if(nearestKey(time).time<=time){ix=nearestKey(time).index;}else{';
	keepPreviewKeyValue+='ix=(nearestKey(time).index==1)?1:nearestKey(time).index-1;};key(ix).value;}else{myXps.value}';
	keepPreviewKeyValue+='\n';
	keepPreviewKeyValue+='End of Expression Data\n';
//TimeRemap処理 No.1-T(スライダ参照)
	var trExpression='Time Remap\n';
	trExpression+='\tFrame\tseconds\t\n';
	trExpression+='\t0\t0\t\n\n';
	trExpression+='Expression Data\n';
	trExpression+='(effect("スライダ制御")("スライダ")-1)*thisComp.frameDuration;\n';
	trExpression+='End of Expression Data\n';
//blank処理 No.1-B(スライダ参照)
	var blankExpression1='Effects\tチャンネルシフト #2\tアルファを取り込む #2\n';
	blankExpression1+='\tFrame\t\t\n';
	blankExpression1+='\t\t0\t\n\n';
	blankExpression1+='Expression Data\n';
	blankExpression1+='if(effect("スライダ制御")("スライダ").value){1}else{10};\n';
	blankExpression1+='End of Expression Data\n';

//blank処理 No.2(タイムリマップ直接参照)
	var blankExpression2='Effects\tチャンネルシフト #1\tアルファを取り込む #2\n';
	blankExpression2+='\tFrame\t\t\n';
	blankExpression2+='\t\t0\t\n\n';
	blankExpression2+='Expression Data\n';
	blankExpression2+='if(timeRemap.value>=999999){10}else{1};\n';
	blankExpression2+='End of Expression Data\n';

}

//出力
var Result= 'Adobe After Effects ';
Result += AE_version;
Result += ' Keyframe Data\n';
//Result += ;
Result += '\n\tUnits Per Second\t';
Result += compFramerate.toString();
Result += '\n\tSource Width\t';
Result += sizeX.toString();
Result += '\n\tSource Height\t';
Result += sizeY.toString();
Result += '\n\tSource Pixel Aspect Ratio\t1';
Result += '\n\tComp Pixel Aspect Ratio\t1';
Result += '\n';
if (blank_method !="expression1")
{

	if (blank_method =="opacity")
	{
//		ブランク
		Result += '\n';
		Result += blankBody;
	}
//
//	リマップ
	Result += '\n';
	Result += remapBody;
//AE 6.5以上ならキー保持エクスプレッション貼り付け
	if (AE_version >=6.5){Result +=keepPreviewKeyValue+"\n";}

	if (blank_method =="wipe")
	{
//		ブランク
		Result += '\n';
		Result += blankBody;
	}
//AE 6.5以上でメソッドがexpression2ならばブランク処理を貼り付け
if((blank_method=="expression2")&&(AE_version >=6.5)){Result +=blankExpression2+"\n";}
//
} else {
//エクスプレッション1
	Result += '\n';
//AE 6.5以上ならタイムリマップのエクスプレッションを貼り付け
	if (AE_version >=6.5){Result +=trExpression+"\n";}
//スライダボディ
	Result +=expBody+"\n";
//AE 6.5以上ならキープエクスプレッション貼り付け
	if (AE_version >=6.5){Result +=keepPreviewKeyValue+"\n";}
//AE 6.5以上ならカラセルエクスプレッション貼り付け
	if (AE_version >=6.5){Result +=blankExpression1+"\n";}
}

Result += '\n';
Result += 'End of Keyframe Data';

if(xUI.errorCode){xUI.errorCode=0};return Result;
}

