coding UTF-8;

import Math;
import Text;
import GUI;
import File;
import tool.Graph3D;


/** グラフに描画する数式 x(t) のデフォルト入力値です。 */
const string DEFAULT_X_EXPRESSION = "cos(t*4*PI)";

/** グラフに描画する数式 y(t) のデフォルト入力値です。 */
const string DEFAULT_Y_EXPRESSION = "sin(t*2*PI)";

/** グラフに描画する数式 z(t) のデフォルト入力値です。 */
const string DEFAULT_Z_EXPRESSION = "sin(t*PI)";

/** 媒介変数 t(時刻)の最大値(終了値)のデフォルト入力値です。 */
const string DEFAULT_T_MAX = "1.0";

/** 媒介変数 t(時刻)の最小値(開始値)のデフォルト入力値です。 */
const string DEFAULT_T_MIN = "-1.0";

/** 媒介変数 t の範囲を分割する点数(プロット点数)のデフォルト入力値です。 */
const string DEFAULT_T_N = "300";


// 以下、グラフやGUIコンポーネントIDを格納する変数

/** グラフのIDを格納します。 **/
int graph;

/** 入力画面のウィンドウのIDを格納します。 */
int window;

/** x(t) の数式を入力するテキストフィールドのIDを格納します。 */
int xExpressionField;

/** y(t) の数式を入力するテキストフィールドのIDを格納します。 */
int yExpressionField;

/** z(t) の数式を入力するテキストフィールドのIDを格納します。 */
int zExpressionField;

/** 時刻 t の最大値を入力するテキストフィールドのIDを格納します。 */
int tMaxField;

/** 時刻 t の最小値を入力するテキストフィールドのIDを格納します。 */
int tMinField;

/** 時刻の刻み数(=グラフのプロット点数)を入力するテキストフィールドのIDを格納します。 */
int tNField;

/** PLOTボタンのIDを格納します。 */
int plotButton;

/** CLEARボタンのIDを格納します。 */
int clearButton;

/** ANIMATIONボタンのIDを格納します。 */
int animationButton;

/** EXPORTボタンのIDを格納します。 */
int exportButton;

/** EXITボタンのIDを格納します。 */
int exitButton;



/**
 * プログラムの開始時に自動で実行されます。
 */
void main(){

	// GUIを備えたツールではコンソール画面は邪魔なので非表示化
	hide();

	// システム側で標準接続されている3DグラフソフトがあればIDを取得(無ければnewGraph3D関数同様、新規に立ち上がる)
	graph = getGraph3D();

	// ※ 普通に newGraph3D 関数で新規生成しないのは、このプログラムがリニアングラフ3Dにも同梱されるツールであり、
	//	リニアングラフ3D上でメニューからこのプログラムを実行した際に、そのグラフ自身を制御対象とするためです。

	// グラフ画面の位置とサイズを設定
	setGraph3DLocation(graph, 350, 0);
	setGraph3DSize(graph, 720, 600);

	// グラフのプロットオプションを設定(点プロットと線プロットをONに)
	setGraph3DOption( graph, "WITH_POINTS", true );
	setGraph3DOption( graph, "WITH_LINES", true );
	setGraph3DOption( graph, "WITH_MEMBRANES", false );
	setGraph3DOption( graph, "WITH_MESHES", false );

	// 設定画面を起動
	createSettingWindow();
}


/**
 * 引数に指定された数式をグラフにプロットします。
 * 
 * @param xExpression プロットするxの数式(時刻変数tの関数)
 * @param yExpression プロットするyの数式(時刻変数tの関数)
 * @param zExpression プロットするzの数式(時刻変数tの関数)
 * @param yMin 時刻変数tの最小値(開始値)
 * @param yMax 時刻変数tの最大値(終了値)
 * @param n プロット点数
 */
void plotGraph( string xExpression, string yExpression, string zExpression, double tMax, double tMin, int n ){

	// グラフに転送するデータ配列
	double xArray[n];
	double yArray[n];
	double zArray[n];

	// グラフを構成する点の x, y, z 座標および時刻 t を格納する変数
	//(入力された x, y の数式の中でも変数 t は参照するので、スコープの都合上ここで宣言)
	double x;
	double y;
	double z;
	double t;

	//  x, y, z の数式が文法的に正しいか検査
	if( !evaluable(xExpression, 0.0) ){
		alert("x(t)の式に誤りがあります。");
		return;
	}
	if( !evaluable(yExpression, 0.0) ){
		alert("y(t)の式に誤りがあります。");
		return;
	}
	if( !evaluable(zExpression, 0.0) ){
		alert("z(t)の式に誤りがあります。");
		return;
	}

	// 時間範囲をn分割した時刻 t において、数式から x, y の値を求め、配列に格納
	float dt = (tMax-tMin)/(n-1);
	for( int i=0; i<n; i++ ){
		t = tMin + i * dt;
		
		// 文字列の式を解釈してfloatの値を得る(第2引数は式内の整数を浮動小数点数と見なすオプション)
		x = feval( xExpression, 0.0 );
		y = feval( yExpression, 0.0 );
		z = feval( zExpression, 0.0 );
		
		xArray[ i ] = x;
		yArray[ i ] = y;
		zArray[ i ] = z;
	}

	// データ配列をグラフソフトウェアに転送し、追加プロット
	addGraph3DData( graph, xArray, yArray, zArray );
}


/**
 * 設定画面を起動します。
 */
void createSettingWindow(){

	int fontSize = 20;
	int leftWidth = 120;
	int rightX = leftWidth + 10;
	int rightWidth = 160;
	int buttonWidth = 270;

	window = newWindow( 0, 0, 320, 580, "入力画面" );

	int xExpressionLabel = newTextLabel( 10, 10, leftWidth, 25, "x( t ) =" );
	setComponentFontSize( xExpressionLabel, fontSize );
	mountComponent( xExpressionLabel, window );

	xExpressionField = newTextField( rightX, 10, rightWidth, 25, DEFAULT_X_EXPRESSION );
	setComponentFontSize( xExpressionField, fontSize );
	mountComponent( xExpressionField, window );

	int yExpressionLabel = newTextLabel( 10, 50, leftWidth, 25, "y( t ) =" );
	setComponentFontSize( yExpressionLabel, fontSize );
	mountComponent( yExpressionLabel, window );

	yExpressionField = newTextField( rightX, 50, rightWidth, 25, DEFAULT_Y_EXPRESSION );
	setComponentFontSize( yExpressionField, fontSize );
	mountComponent( yExpressionField, window );

	int zExpressionLabel = newTextLabel( 10, 90, leftWidth, 25, "z( t ) =" );
	setComponentFontSize( zExpressionLabel, fontSize );
	mountComponent( zExpressionLabel, window );

	zExpressionField = newTextField( rightX, 90, rightWidth, 25, DEFAULT_Z_EXPRESSION );
	setComponentFontSize( zExpressionField, fontSize );
	mountComponent( zExpressionField, window );


	int tMaxLabel = newTextLabel( 10, 140, leftWidth, 25, "t-max =" );
	setComponentFontSize( tMaxLabel, fontSize );
	mountComponent( tMaxLabel, window );

	tMaxField = newTextField( rightX, 140, rightWidth, 25, DEFAULT_T_MAX );
	setComponentFontSize( tMaxField, fontSize );
	mountComponent( tMaxField, window );

	int tMinLabel = newTextLabel( 10, 170, leftWidth, 25, "t-min =" );
	setComponentFontSize( tMinLabel, fontSize );
	mountComponent( tMinLabel, window );

	tMinField = newTextField( rightX, 170, rightWidth, 25, DEFAULT_T_MIN );
	setComponentFontSize( tMinField, fontSize );
	mountComponent( tMinField, window );


	int tNLabel = newTextLabel( 10, 200, leftWidth, 25, "t-N =" );
	setComponentFontSize( tNLabel, fontSize );
	mountComponent( tNLabel, window );

	tNField = newTextField( rightX, 200, rightWidth, 25, DEFAULT_T_N );
	setComponentFontSize( tNField, fontSize );
	mountComponent( tNField, window );


	int buttonGridPanel = newGridPanel( 10, 240, buttonWidth, 280, "", 5, 1 );
	mountComponent( buttonGridPanel, window );

	plotButton = newButton( 0, 0, 0, 0, "プロット" );
	setComponentFontSize( plotButton, fontSize );
	mountComponent( plotButton, buttonGridPanel );

	animationButton = newButton( 0, 0, 0, 0, "アニメーション" );
	setComponentFontSize( animationButton, fontSize );
	mountComponent( animationButton, buttonGridPanel );

	clearButton = newButton( 0, 0, 0, 0, "クリア" );
	setComponentFontSize( clearButton, fontSize );
	mountComponent( clearButton, buttonGridPanel );

	exportButton = newButton( 0, 0, 0, 0, "データ出力" );
	setComponentFontSize( exportButton, fontSize );
	mountComponent( exportButton, buttonGridPanel );

	exitButton = newButton( 0, 0, 0, 0, "終了" );
	setComponentFontSize( exitButton, fontSize );
	mountComponent( exitButton, buttonGridPanel );
}


/**
 * 現在のデータをファイルに出力します。
 */
void exportFile(){

	// ファイルパスの選択
	string exportFilePath[] = choose();
	if (length(exportFilePath) == 0) {
		return; // 何も選択されなかった場合
	}

	// 拡張子が無ければ付ける
	string name = getFileName( exportFilePath[0] );
	if( indexOf(name,".") < 0 ){
		exportFilePath += ".dat3d";
	}

	// ファイル出力
	exportGraph3DData(graph, exportFilePath[0], "COLUMN_TSV");

	// 保存完了メッセージを表示
	pop( "SAVED: " + exportFilePath[0] );
}



/**
 * 入力画面上の入力項目において、式として評価するのに適さない文字を、適切な文字で置き換えます。
 */
void correctCharactersOnInputWindow() {
	
	setComponentText(
		xExpressionField, 
		correctCharactersInExpression(
			getComponentText(xExpressionField)
		)
	);
	
	setComponentText(
		yExpressionField, 
		correctCharactersInExpression(
			getComponentText(yExpressionField)
		)
	);
	
	setComponentText(
		zExpressionField, 
		correctCharactersInExpression(
			getComponentText(zExpressionField)
		)
	);
	
	setComponentText(
		tMaxField, 
		correctCharactersInExpression(
			getComponentText(tMaxField)
		)
	);
	
	setComponentText(
		tMinField, 
		correctCharactersInExpression(
			getComponentText(tMinField)
		)
	);
	
	setComponentText(
		tNField, 
		correctCharactersInExpression(
			getComponentText(tNField)
		)
	);
}


/**
 * 文字列内で、式として評価するのに適さない文字を、適切な文字で置き換えます。
 * 
 * @param 式の内容を格納する文字列
 * @return 適切に文字を置き換えた文字列
 */
string correctCharactersInExpression(string expression) {
	string result = expression;
	
	result = replaceText(result, "×", "*", Text.ALL);
	result = replaceText(result, "÷", "/", Text.ALL);
	result = replaceText(result, "^", "**", Text.ALL);
	result = replaceText(result, "^",  "**", Text.ALL);
	
	result = replaceText(result, "+", "+", Text.ALL);
	result = replaceText(result, "*", "*", Text.ALL);
	result = replaceText(result, "/", "/", Text.ALL);
	result = replaceText(result, "ー", "-", Text.ALL);
	result = replaceText(result, "-", "-", Text.ALL);
	result = replaceText(result, "―", "-", Text.ALL);
	result = replaceText(result, "‐", "-", Text.ALL);
	result = replaceText(result, "ー", "-", Text.ALL);
	result = replaceText(result, "%", "%", Text.ALL);
	result = replaceText(result, "(", "(", Text.ALL);
	result = replaceText(result, ")", ")", Text.ALL);
	
	result = replaceText(result, "A", "A", Text.ALL);
	result = replaceText(result, "B", "B", Text.ALL);
	result = replaceText(result, "C", "C", Text.ALL);
	result = replaceText(result, "D", "D", Text.ALL);
	result = replaceText(result, "E", "E", Text.ALL);
	result = replaceText(result, "F", "F", Text.ALL);
	result = replaceText(result, "G", "G", Text.ALL);
	result = replaceText(result, "H", "H", Text.ALL);
	result = replaceText(result, "I", "I", Text.ALL);
	result = replaceText(result, "J", "J", Text.ALL);
	result = replaceText(result, "K", "K", Text.ALL);
	result = replaceText(result, "L", "L", Text.ALL);
	result = replaceText(result, "M", "M", Text.ALL);
	result = replaceText(result, "N", "N", Text.ALL);
	result = replaceText(result, "O", "O", Text.ALL);
	result = replaceText(result, "P", "P", Text.ALL);
	result = replaceText(result, "Q", "Q", Text.ALL);
	result = replaceText(result, "R", "R", Text.ALL);
	result = replaceText(result, "S", "S", Text.ALL);
	result = replaceText(result, "T", "T", Text.ALL);
	result = replaceText(result, "U", "U", Text.ALL);
	result = replaceText(result, "V", "V", Text.ALL);
	result = replaceText(result, "W", "W", Text.ALL);
	result = replaceText(result, "X", "X", Text.ALL);
	result = replaceText(result, "Y", "Y", Text.ALL);
	result = replaceText(result, "Z", "Z", Text.ALL);
	
	result = replaceText(result, "a", "a", Text.ALL);
	result = replaceText(result, "b", "b", Text.ALL);
	result = replaceText(result, "c", "c", Text.ALL);
	result = replaceText(result, "d", "d", Text.ALL);
	result = replaceText(result, "e", "e", Text.ALL);
	result = replaceText(result, "f", "f", Text.ALL);
	result = replaceText(result, "g", "g", Text.ALL);
	result = replaceText(result, "h", "h", Text.ALL);
	result = replaceText(result, "i", "i", Text.ALL);
	result = replaceText(result, "j", "j", Text.ALL);
	result = replaceText(result, "k", "k", Text.ALL);
	result = replaceText(result, "l", "l", Text.ALL);
	result = replaceText(result, "m", "m", Text.ALL);
	result = replaceText(result, "n", "n", Text.ALL);
	result = replaceText(result, "o", "o", Text.ALL);
	result = replaceText(result, "p", "p", Text.ALL);
	result = replaceText(result, "q", "q", Text.ALL);
	result = replaceText(result, "r", "r", Text.ALL);
	result = replaceText(result, "s", "s", Text.ALL);
	result = replaceText(result, "t", "t", Text.ALL);
	result = replaceText(result, "u", "u", Text.ALL);
	result = replaceText(result, "v", "v", Text.ALL);
	result = replaceText(result, "w", "w", Text.ALL);
	result = replaceText(result, "x", "x", Text.ALL);
	result = replaceText(result, "y", "y", Text.ALL);
	result = replaceText(result, "z", "z", Text.ALL);
	
	result = replaceText(result, "1", "1", Text.ALL);
	result = replaceText(result, "2", "2", Text.ALL);
	result = replaceText(result, "3", "3", Text.ALL);
	result = replaceText(result, "4", "4", Text.ALL);
	result = replaceText(result, "5", "5", Text.ALL);
	result = replaceText(result, "6", "6", Text.ALL);
	result = replaceText(result, "7", "7", Text.ALL);
	result = replaceText(result, "8", "8", Text.ALL);
	result = replaceText(result, "9", "9", Text.ALL);
	result = replaceText(result, "0", "0", Text.ALL);
	result = replaceText(result, ".", ".", Text.ALL);
	
	return result;
}


/**
 * ボタンが押された際にコールされます(イベントハンドラ)
 * 
 * @param id 押されたボタンのID
 */
void onButtonClick( int id ){

	// 「PLOT」ボタンが押された場合
	if( id == plotButton ){

		// 画面上に入力されている文字列内で、式として評価するのに適さない文字を、適切な文字で置き換え
		correctCharactersOnInputWindow();

		// 以下、画面から入力内容を取得

		// 入力された x, y, z の数式を取得
		string xExpression = getComponentText( xExpressionField );
		string yExpression = getComponentText( yExpressionField );
		string zExpression = getComponentText( zExpressionField );

		// t の範囲の最大・最小値やプロット点数の入力内容が、文法的に正しいか検査
		if( !evaluable( getComponentText( tMaxField ), 0.0 ) ){
			alert("t-maxの式に誤りがあります。");
			return;
		}
		if( !evaluable( getComponentText( tMinField ), 0.0 ) ){
			alert("t-minの式に誤りがあります。");
			return;
		}
		if( !evaluable( getComponentText( tNField ), 0.0 ) ){
			alert("t-Nの式に誤りがあります。");
			return;
		}

		// t の範囲の最大・最小値やプロット点数の入力内容を解釈し、値を求める
		double tMax = feval( getComponentText( tMaxField ), 0.0 );
		double tMin = feval( getComponentText( tMinField ), 0.0 );
		int tN = feval( getComponentText( tNField ), 0.0 );

		// x, y, z の数式をグラフにプロットする(plotGraph関数はこのファイル内の上の方で定義)
		plotGraph( xExpression, yExpression, zExpression, tMax, tMin, tN );
		return;
	}

	// 「CLEAR」ボタンが押された場合
	if( id == clearButton ){
		clearGraph3D(graph);
		return;
	}

	// 「ANIMATION」ボタンが押された場合
	if( id == animationButton ){
		setGraph3DAnimation( graph, "TRACE", true );
		return;
	}

	// 「SAVE」ボタンが押された場合
	if( id == exportButton ){
		exportFile();
		return;
	}

	// 「EXIT」ボタンが押された場合
	if( id == exitButton ){
		exit();
		return;
	}
}


/**
 * ウィンドウが閉じられた際にコールされます(イベントハンドラ)
 */
void onWindowClose( int id ){
	if( id == window ){
		exit();
		return;
	}
}


/**
 * グラフが閉じられた際にコールされます(イベントハンドラ)
 */
void onGraph3DClose( int id ){
	if( id == graph ){
		exit();
		return;
	}
}