#include <stdio.h>
#include <eggx.h>

// ============================ EGGX

int win;

#define WW (400)
#define HH (600)

// ========================== 二線分の交差チェック
/*
 http://www.hiramine.com/programming/graphics/2d_segmentintersection.html
 を参考に。
 線分 AB (ax,ay)-(bx,by) と CD (cx,cy)-(dx,dy) が交差しているかどうかを調べる
 r, s が共に 0-1 の間にあれば衝突と判定
 ただし、
 戻り値は衝突時は r そのもの。範囲は 0.0 <= r <= 1.0 である。
 非衝突時は 9.0 を返す。
 ただし、二線分が並行な場合は 99.0 であり、
 二線分が同一（重なっている）の場合は 999.0 を返す
 この r を用いて交差座標位置を求める
 */

#define NOCROSS  9.0
#define PARALLEL 99.0
#define DOUBLE   999.0

float colCheck(float ax, float ay,
			   float bx, float by,
			   float cx, float cy,
			   float dx, float dy)
{
	float denom; // 途中計算における分母(denominator)
	float acx, acy; // ベクター AC を表現する
	float r, s;
	
	denom = (bx - ax) * (dy - cy) - (by - ay) * (dx - cx);
	if( denom == 0.0 ) return PARALLEL; // 二線は並行である

	// ベクター AC を得る（C - A にあたる） 
	acx = cx - ax; acy = cy - ay;
	
	r=( ( dy - cy ) * acx - ( dx - cx ) * acy ) / denom;
	s=( ( by - ay ) * acx - ( bx - ax ) * acy ) / denom;
	
	if( (r == 0.0)&&(s == 0.0)) return DOUBLE; // 二線は同一である
	
	if( (0.0 <= r) && (r <= 1.0) && (0.0 <= s) && (s <= 1.0)) {
		return r; // 交差した
	} else {
		return NOCROSS; // 交差しない
	}
		
}

// ========================== 補助サブルーチン群

// 初期設定
void setup(void)
{
	win=gopen(WW, HH);
	winname(win, "Collision test");
	
}

// 終了処理
void closeall(void)
{
	// ggetch(); // quit 関数があるので外す
	gclose(win);
}

// ======================== 座標位置を指定して、交差点を計算させる

// 終了ボタンの位置、幅、高さ
#define QUITX (WW*0.3)
#define QUITY (40.0)
#define QUITW (35.0)
#define QUITH (20.0)

// 終了ボタンの表示
void showQuitButton(void)
{
	newpen(win, 4);
	fillrect(win, QUITX, QUITY, QUITW, QUITH);
	newpen(win, 1);
	drawstr(win, QUITX+2.0, QUITY+2.0, 16, 0.0, "QUIT");
}	

// 座標位置が終了ボタンの中かどうかをチェック
int inQuitButton(float x, float y)
{
	if ((QUITX<x)&&(x<(QUITX+QUITW))
		&&(QUITY<y)&&(y<(QUITY+QUITH))) {
		return 1; // quit ボタン内をクリックすれば 1 を返して return
	} else {
		return 0;
	}
}

#define CLIP 1000
// マウス入力：戻り値は x * 1000 + y 
int clickCheck(void)
{
	int w;
	float x, y;
	int code, type;

	do {
		w=ggetxpress(&type, &code, &x, &y);
	} while((w!=win)||(type!=ButtonPress));

	return (int)x * CLIP + (int)y; // 整数型で戻す
}

// 指定された位置、色にマークを入れる
void markPoint(float x, float y, int color)
{
	newpen(win, color);
	drawcirc(win, x, y, 3.0, 3.0);
}

// A,B,C,D の各点を指定し、交差を判定
int crossPoint(void)
{
	int xy;
	float ax, ay, bx, by, cx, cy, dx, dy;
	float r, isx, isy;

	// 最初に終了ボタンを出しておく
	showQuitButton();

	// まず A, B, C, D の四点の座標位置を指定する
	// A を指定
	xy=clickCheck(); ax=xy/CLIP; ay=xy%CLIP;

	//	A ではなく QUIT ボタンを押したかどうかチェック
	if(inQuitButton(ax, ay)==1) return 1; // 当該枠内をクリックすれば 1 を返して return

	gclr(win); // 問題なければ画面を消去（終了ボタンを消す）
	markPoint(ax, ay, 1); // ポイントした位置にマルを描く
	
	// B を指定
	xy=clickCheck(); bx=xy/CLIP; by=xy%CLIP;
	markPoint(bx, by, 1); // ポイントした位置にマルを描く
	// 線分 AB を描画
	newpen(win, 1); drawline(win, ax, ay, bx, by);

	// C, D を指定して線分 CD を描画
	xy=clickCheck(); cx=xy/CLIP; cy=xy%CLIP;
	markPoint(cx, cy, 2); // ポイントした位置にマルを描く
	xy=clickCheck(); dx=xy/CLIP; dy=xy%CLIP;
	markPoint(dx, dy, 2); // ポイントした位置にマルを描く
	newpen(win, 2); drawline(win, cx, cy, dx, dy);
	
	// ここまでで A,B,C,D の位置が決定できた
	
	// 交差チェック
	r=colCheck(ax, ay, bx, by, cx, cy, dx, dy);
	if(r >= NOCROSS) { // 交差しなかった
		drawstr(win, 10.0, 10.0, 16, 0.0, "NO CROSS");
	} else { // 交差した
		//交点を AB から求める（A から r ぶんだけ AB 方向に進んだ位置）
		isx = ax + r * ( bx - ax );
		isy = ay + r * ( by - ay );
		// 交点を描画
		markPoint(isx, isy, 3);
	}
	return 0;
}

// ============================================ メインルーチン

int main (int argc, const char * argv[]) {
	setup();
	
	gclr(win);
	while(1) {
		if(crossPoint()==1) break;
	}
	
	closeall();
    return 0;
}
