#include <stdio.h>
#include <snd.h>
#include <egb.h>

#include <\src\ysuty2\ysuty2.h>


#define XBUF 16
#define LNG 480


char EGB_work[EgbWorkSize];
char SND_work[16384];


int vx1=64;
int vy1=64;
int vx2=447;
int vy2=415;


#define clipPoint(x1,y1,x2,y2,x) (x-x1)*(y2-y1)/(x2-x1)+y1

/* Polygon Parameters */
int fstx,fsty,fstvy;    /* 最初に画面に入った点,その時のvy */
int lstvy;              /* 最後の直線の終点,その時のvy */
int max,may,mnx,mny;    /* x,yの最大値,最小値 */


void polygon_addPoint(int *plg,int x,int y)
{
	int i;
	plg+=y*XBUF;
	for(i=*plg; i>0; i--)
	{
		if(plg[i]<=x)break;
		plg[i+1]=plg[i];
	}
	plg[i+1]=x;
	(*plg)++;
}

void polygon_vertical(int *plg,int x,int y1,int y2)
{
	int vy,c;
	if     (y2-y1 >0)vy= 1;
	else if(y2-y1==0)vy= 0;
	else if(y2-y1< 0)vy=-1;

	c=_abs(y2-y1);
	while(c>0)
	{
		y1+=vy;
		polygon_addPoint(plg,x,y1);
		c--;
	}
}


void polygon_normal(int *plg,int x1,int y1,int x2,int y2)
{
	int c,vx,vy,a,b,Rf;
	if     (x2-x1 >0)vx= 1;
	else if(x2-x1==0)vx= 0;
	else if(x2-x1< 0)vx=-1;

	if     (y2-y1 >0)vy= 1;
	else if(y2-y1==0)vy= 0;
	else if(y2-y1< 0)vy=-1;

	a=_abs(x2-x1);
	b=_abs(y2-y1);

	c=_abs(y2-y1);
	Rf=0;
	if(a<b)
	{
		while(c>0)
		{
			y1+=vy;
			Rf+=a;
			polygon_addPoint(plg,x1,y1);
			if(Rf>0)
			{
				x1+=vx;
				Rf-=b;
			}
			c--;
		}
	}
	else if(a>b)
	{
		while(c>0)
		{
			x1+=(Rf/b+1)*vx;
			y1+=vy;
			Rf=(Rf%b)-b+a;
			polygon_addPoint(plg,x1,y1);
			c--;
		}
	}
	else if(a==b)
	{
		while(c>0)
		{
			x1+=vx;
			y1+=vy;
			polygon_addPoint(plg,x1,y1);
			c--;
		}
	}
}


/* polygon_line  戻り値 0:画面外  1:画面内の点が存在 */
int polygon_line(int *plg,int x1,int y1,int x2,int y2)
{
	int sp;            /* 書き出し点を追加するかしないかのフラッグ */
	int ax,ay,bx,by;   /* 直線の変換用 */
	int vx,vy;         /* 直線の方向ベクトル SGN(x2-x1),SGN(y2-y1) */

	/* ↓ポリゴンの最大値/最小値のチェック (水平/垂直直線になる場合の備え) */
	if(x2>max)max=x2;
	if(x2<mnx)mnx=x2;
	if(y2>may)may=y2;
	if(y2<mny)mny=y2;


	/* ↓両方の点が画面の上か下ならば何もしない */
	if((y1<vy1 && y2<vy1) || (y1>vy2 && y2>vy2))return 0;


	/* ↓ 上下にはみ出している部分を切る */
	if(y1<vy1)
	{
		ax=clipPoint(y1,x1,y2,x2,vy1);
		ay=vy1;
	}
	else if(y1>vy2)
	{
		ax=clipPoint(y1,x1,y2,x2,vy2);
		ay=vy2;
	}
	else
	{
		ax=x1;
		ay=y1;
	}

	if(y2<vy1)
	{
		bx=clipPoint(y1,x1,y2,x2,vy1);
		by=vy1;
	}
	else if(y2>vy2)
	{
		bx=clipPoint(y1,x1,y2,x2,vy2);
		by=vy2;
	}
	else
	{
		bx=x2;
		by=y2;
	}

	x1=ax; y1=ay; x2=bx; y2=by;
	/* ↑ 上下にはみ出している部分を切る */


	if     (x2-x1 >0)vx= 1;
	else if(x2-x1==0)vx= 0;
	else if(x2-x1< 0)vx=-1;

	if     (y2-y1 >0)vy= 1;
	else if(y2-y1==0)vy= 0;
	else if(y2-y1< 0)vy=-1;

	if(lstvy*vy < 0)sp=1; else sp=0;


	/* 水平でない直線ならばvyを記録 */
	if(vy!=0)
	{
		lstvy=vy;
		/* それが最初の縦の幅を持った直線ならば記録 */
		if(fstvy==0)
		{
			if     (x1<vx1)fstx=vx1;
			else if(x1>vx2)fstx=vx2;
			else           fstx= x1;
			fsty=y1;
			fstvy=vy;
		}
	}

	/* 両方とも左右にはみ出していないかのチェック */
	if(x1<vx1 && x2<vx1)
	{
		if(sp)polygon_addPoint(plg,vx1,y1);
		polygon_vertical(plg,vx1,y1,y2);
		return 0;
	}
	else if(x1>vx2 && x2>vx2)
	{
		if(sp)polygon_addPoint(plg,vx2,y1);
		polygon_vertical(plg,vx2,y1,y2);
		return 0;
	}


	/* 水平な直線ならば何もしない */
	if(vy==0)return 1;


	/* 最初の点を追加する場合 */
	if(sp)
	{
		if     (x1<vx1)polygon_addPoint(plg,vx1,y1);
		else if(x1>vx2)polygon_addPoint(plg,vx2,y1);
		else           polygon_addPoint(plg, x1,y1);
	}


	/* 垂直な直線の処理 */
	if(x1==x2)
	{
		polygon_vertical(plg,x1,y1,y2);
		return 1;
	}


	/* P1が左右にはみ出している場合 */
	if(x1<vx1)
	{
		by=clipPoint(x1,y1,x2,y2,vx1);
		polygon_vertical(plg,vx1,y1,by);
		x1=vx1;
		y1=by;
	}
	else if(x1>vx2)
	{
		by=clipPoint(x1,y1,x2,y2,vx2);
		polygon_vertical(plg,vx2,y1,by);
		x1=vx2;
		y1=by;
	}
	

	/* 画面内部の処理 */
	if(vx1<=x2 && x2<=vx2)
	{
		bx=x2;
		by=y2;
	}
	else if(x2<vx1)
	{
		bx=vx1;
		by=clipPoint(x1,y1,x2,y2,vx1);
	}
	else if(vx2<x2)
	{
		bx=vx2;
		by=clipPoint(x1,y1,x2,y2,vx2);
	}
	polygon_normal(plg,x1,y1,bx,by);


	/* P2がはみ出していた場合の処理 */
	if(by!=y2)
	{
		polygon_vertical(plg,bx,by,y2);
	}

	return 1;
}



int polygon(int *plg,int *pnt)
{
	int drawf,pn;
	int x1,y1,x2,y2;
	int xo,yo;              /* 頂点0番の座標 */
	int i;

	if(*pnt<3)return 0;

	lstvy=0;
	fstvy=0;
	drawf=0;
	mnx=pnt[1];
	max=pnt[1];
	mny=pnt[2];
	may=pnt[2];
	for(i=0; i<LNG; i++)plg[i*XBUF]=0;

	pn=*pnt-1;
	xo=pnt[1];
	yo=pnt[2];
	x2=pnt[1];
	y2=pnt[2];
	pnt+=3;

	while(pn>0)
	{
		x1=x2;
		y1=y2;
		x2=* pnt;
		y2=*(pnt+1);
		drawf |= polygon_line(plg,x1,y1,x2,y2);
		pnt+=2;
		pn--;
	}

	drawf |= polygon_line(plg,x2,y2,xo,yo);

	if(lstvy*fstvy<0)
	{
		polygon_addPoint(plg,fstx,fsty);
	}

	YGB_color(EGB_work,32767-31);
	YGB_box(EGB_work,xo-4,yo-4,xo+4,yo+4);

	return drawf;
}



void paint(int *plg)
{
	int y,y1,y2;
	y1=mny;
	y2=may;

	if(y2<vy1 || y1>vy2)return;

	if(y1<vy1)y1=vy1;
	if(y2>vy2)y2=vy2;

	plg+=y1*XBUF;

	for(y=y1; y<=y2; y++)
	{
		int i,x1,x2;
		if(*plg&1)YGB_color(EGB_work,31*1024);
		     else YGB_color(EGB_work,31*32);

		for(i=0; i<*plg; i+=2)
		{
			x1=plg[1+i];
			x2=plg[2+i];
			if(x2<=vx1 || vx2<=x1)continue;
			YGB_line(EGB_work,x1,y,x2,y);
		}

		plg+=XBUF;
	}
}



void main()
{
	int pnt[16],plg[XBUF*LNG];
	int pn,i;
	int pad,pre,prs;

	srand(clock());

	SND_init(SND_work);
	YGB_init(EGB_work,17,17);  /* 512x480 mode */

	pad=255;

	while((pad & 32)==32)
	{
		YGB_color(EGB_work,32767);
		YGB_boxFul(EGB_work,0,0,511,479);
		YGB_color(EGB_work,0);
		YGB_boxFul(EGB_work,vx1,vy1,vx2,vy2);

		pn=rand()%4+3;
		pnt[0]=pn;
		for(i=0; i<pn; i++)
		{
			pnt[1+i*2]=rand()%512;
			pnt[2+i*2]=rand()%480;
		}
		polygon(plg,pnt);
		paint(plg);

		YGB_color(EGB_work,31);
		YGB_frame(EGB_work,pnt);

		prs=255;
		while((prs & 48)==48)
		{
			pre=pad;
			SND_joy_in_2(0,&pad);
			prs= ~pre | pad;
		}
	}

	SND_end();
}
