#include <stdio.h>
#include <concorde.h>
#include <tornado.h>
#include <ysmaze2.h>


extern int MazeLinearC(int x,int x1,int y1,int x2,int y2);
extern int MazeScanLineClip(int bl,int *sx,int *sy,int vx,int vy);




static void MazeGetOneLine
       (MAZEZBUF *buf,MAZEMAP *mz,MAZESCREEN *ms,
            int cx,int cy,int h,int lng,int sx);
static void MazeDrawOneLine
       (PAGE *wp,MAZESCREEN *ms,int col,int x,int y0,int y1);
static void MazeScanLineFloorCeiling
  (int fc,int bl,int x,int y,int tx,int ty,int ux,int uy,MAZEZBUF *xbuf,int h);
static void MazeScanLineCross
     (MAZEMAP *mz,int *bx,int *by,int *sx,int *sy,int vx,int vy);
static int MazeGetLineWall(MAZEMAP *mz,int x,int y);
static void MazeScanLine
       (MAZEMAP *mz,int bx,int by,int sx,int sy,
        int vx,int vy,int length,MAZEZBUF *xbuf,int h);



void MazeGetLines
     (MAZEZBUF *buf,MAZEMAP *mz,MAZESCREEN *ms,int bx,int by,int h,int lng)
{
	int sx;

	for(sx=((*ms).x0); sx<=((*ms).x1); sx++)
	{
		MazeGetOneLine(buf,mz,ms,bx,by,h,lng,sx);
	}
}

void MazeDrawWalls(PAGE *pg,MAZEMAP *mz,MAZESCREEN *ms,MAZEZBUF *buf)
{
	int bl=(*mz).bl;
	int sx,sy,sz,wp,wl,wc,dc,bb,rr,gg,y0,y1;
	int i,fl,fc,fy0,fy1;
	int cy=(*ms).cy;
	int mag=(*ms).mag;

	y0=(*ms).y0;
	y1=(*ms).y1;

	if(mag==0)mag=128;

	for(sx=((*ms).x0); sx<=((*ms).x1); sx++)
	{
		int r,br;

		r=sx-((*ms).x0);
		sz = buf[r].viewz;
		wl = buf[r].door;
		wp =(buf[r].edgedist*100/bl);
		if(sz>0)sy=((*mz).bh/2)*mag/sz;
		   else sy=((*ms).y1-(*ms).y0)/2;

		wc =(*ms).wc;
		dc =(*ms).dc;

		br=(100-((*ms).cont)*(sz/100));  if(br<10)br=10;

		gg =((wc / 1024) & 31)*br/100;
		rr =((wc / 32)   & 31)*br/100;
		bb =( wc         & 31)*br/100;
		wc = gg*1024+rr*32+bb;

		gg =((dc / 1024) & 31)*br/100;
		rr =((dc / 32)   & 31)*br/100;
		bb =( dc         & 31)*br/100;
		dc = gg*1024+rr*32+bb;

		MazeDrawOneLine(pg,ms,0,sx,y0,cy-sy);
		MazeDrawOneLine(pg,ms,0,sx,cy+sy,y1);
		if(wl==2 && 20<=wp && wp<=80)
		{
			MazeDrawOneLine(pg,ms,wc,sx,cy-sy,cy-sy*7/10);
			MazeDrawOneLine(pg,ms,dc,sx,cy-sy*7/10,cy+sy);
		}
		else if(wl==1 || wl==2)
		{
			MazeDrawOneLine(pg,ms,wc,sx,cy-sy,cy+sy);
		}
		else
		{
			MazeDrawOneLine(pg,ms,0,sx,cy-sy,cy+sy);
		}

		for(i=0; i<2; i++)
		{
			if( (fl=buf[r].floor[i].mark)==0)continue;
			if( (fy0=buf[r].floor[i].z1)<=0 )fy0=1;
			if( (fy1=buf[r].floor[i].z2)<=0 )fy1=1;
			br=(100-((*ms).cont)*((fy0+fy1)/2/100)); if(br<10)br=10;
			br=31*br/100;  fc=br*1024+br*32+br;
			fy0=((*mz).bh/2) * mag /fy0;
			fy1=((*mz).bh/2) * mag /fy1;
			if(fl & 1)MazeDrawOneLine(pg,ms,fc,sx,cy+fy1,cy+fy0);
			if(fl & 2)MazeDrawOneLine(pg,ms,fc,sx,cy-fy0,cy-fy1);
		}
	}
}



/* cx,cy 自分の位置(単位cm)  vx,vy 視線ベクトル  lng 視野
   *x,*y 自分に対して視線の壁との交点  *p 壁のどのへんか */
static void MazeGetOneLine(MAZEZBUF *buf,MAZEMAP *mz,MAZESCREEN *ms,
            int cx,int cy,int h,int lng,int sx)
{
	int vx,vz;
	int bl;
	int mag;
	MAZEZBUF *xbuf;

	xbuf = &buf[sx-(ms->x0)];
	bl = mz->bl;
	if((mag=ms->mag)==0)mag=128;

	vz=10000;            /* sx= x*mag/z  →  x/z=sx/mag */
	vx=(sx-(*ms).cx)*vz/mag;
	Rot2(&vx,&vz,h);
	MazeScanLine(mz,cx/bl,cy/bl,cx%bl,cy%bl,vx,-vz,lng*lng,xbuf,h);
}





static void MazeDrawOneLine
       (PAGE *wp,MAZESCREEN *ms,int col,int x,int y0,int y1)
{
	if(y1<(ms->y0) || (ms->y1)<y0)return;
	if(y0<(ms->y0))y0=(ms->y0);
	if(y1>(ms->y1))y1=(ms->y1);
	if(y0>y1)return;
	YGH_color(wp,col);
	YGH_line(wp,x,y0,x,y1);
}


/*
static int MazeScanLineClip(int bl,int *sx,int *sy,int vx,int vy)
{
	int tx,ty;

	if     (vx==0)
	{
		if     (vy<0){*sy=   0;return  0;}
		else if(vy>0){*sy=bl-1;return  0;}
		else                   return -1;
	}
	else if(vy==0)
	{
		if     (vx<0){*sx=   0;return  0;}
		else if(vx>0){*sx=bl-1;return  0;}
		else                   return -1;
	}

	tx=*sx;
	ty=*sy;
	if(vx<0)
	{
		ty=MazeLinearC(0,tx,ty,tx+vx,ty+vy);
		if(0<=ty && ty<bl){*sx=0; *sy=ty; return 0;}
	}
	else if(vx>0)
	{
		ty=MazeLinearC(bl-1,tx,ty,tx+vx,ty+vy);
		if(0<=ty && ty<bl){*sx=bl-1; *sy=ty; return 0;}
	}

	tx=*sx;
	ty=*sy;
	if(vy<0)
	{
		tx=MazeLinearC(0,ty,tx,ty+vy,tx+vx);
		*sx=tx; *sy=0; return 0;
	}
	else if(vy>0)
	{
		tx=MazeLinearC(bl-1,ty,tx,ty+vy,tx+vx);
		*sx=tx; *sy=bl-1; return 0;
	}

	return -1;
}
*/


static void MazeScanLineFloorCeiling
  (int fc,int bl,int x,int y,int tx,int ty,int ux,int uy,MAZEZBUF *xbuf,int h)
{
	int x1,y1,x2,y2,cp,ps,p[4],flrn;
	if(fc==0)return;
	if(tx==ux && ty==uy)return;

	if(xbuf->floor[0].mark==0)
	{
		flrn=0;
	}
	else if(xbuf->floor[1].mark==0)
	{
		flrn=1;
	}
	else
	{
		return;
	}

	x1=bl/6;
	y1=bl/6;
	x2=bl*5/6;
	y2=bl*5/6;

	if((tx<x1 && ux<x1) || (tx>x2 && ux>x2))return;
	if((ty<y1 && uy<y1) || (ty>y2 && uy>y2))return;

	if     (tx==ux)
	{
		ps=2;
		p[0]=tx;
		p[1]=y1;
		p[2]=ux;
		p[3]=y2;
	}
	else if(ty==uy)
	{
		ps=2;
		p[0]=x1;
		p[1]=ty;
		p[2]=x2;
		p[3]=uy;
	}
	else
	{
		ps=0;
		cp=MazeLinearC(x1,tx,ty,ux,uy);
		if(ps<2 && y1<=cp && cp<=y2)
		{
			p[ps*2]=x1;
			p[ps*2+1]=cp;
			ps++;
		}
		cp=MazeLinearC(x2,tx,ty,ux,uy);
		if(ps<2 && y1<=cp && cp<=y2)
		{
			p[ps*2]=x2;
			p[ps*2+1]=cp;
			ps++;
		}
		cp=MazeLinearC(y1,ty,tx,uy,ux);
		if(ps<2 && x1<=cp && cp<=x2)
		{
			p[ps*2]=cp;
			p[ps*2+1]=y1;
			ps++;
		}
		cp=MazeLinearC(y2,ty,tx,uy,ux);
		if(ps<2 && x1<=cp && cp<=x2)
		{
			p[ps*2]=cp;
			p[ps*2+1]=y2;
			ps++;
		}
	}
	if(ps<2)return;

	p[0]=  x+(p[0]-tx);
	p[1]=-(y+(p[1]-ty));
	p[2]=  x+(p[2]-tx);
	p[3]=-(y+(p[3]-ty));
	Rot2(p,p+1,-h);
	Rot2(p+2,p+3,-h);
	xbuf->floor[flrn].mark=fc;
	if(p[1]<p[3])
	{
		xbuf->floor[flrn].z1=p[1];
		xbuf->floor[flrn].z2=p[3];
	}
	else
	{
		xbuf->floor[flrn].z1=p[3];
		xbuf->floor[flrn].z2=p[1];
	}

	if(xbuf->floor[flrn].z1<=0)
	{
		xbuf->floor[flrn].z1=1;
	}
}


static void MazeScanLineCross(
     MAZEMAP *mz,int *bx,int *by,int *sx,int *sy,int vx,int vy)
{
	int bl=(*mz).bl;

	if(*bx>=(*mz).lx)(*bx)-=(*mz).lx;
	if(*bx <       0)(*bx)+=(*mz).lx;
	if(*by>=(*mz).ly)(*by)-=(*mz).ly;
	if(*by <       0)(*by)+=(*mz).ly;

	if     (vx==0 && vy> 0){                    *sx=   0; *sy=   0;}
	else if(vx==0 && vy< 0){          (*by)--;  *sx=   0; *sy=bl-1;}
	else if(vx> 0 && vy==0){                    *sx=   0; *sy=   0;}
	else if(vx< 0 && vy==0){(*bx)--;            *sx=bl-1; *sy=   0;}
	else if(vx> 0 && vy> 0){                    *sx=   0; *sy=   0;}
	else if(vx< 0 && vy> 0){(*bx)--;            *sx=bl-1; *sy=   0;}
	else if(vx> 0 && vy< 0){          (*by)--;  *sx=   0; *sy=bl-1;}
	else if(vx< 0 && vy< 0){(*bx)--;  (*by)--;  *sx=bl-1; *sy=bl-1;}

	if(*bx>=(*mz).lx)(*bx)-=(*mz).lx;
	if(*bx <       0)(*bx)+=(*mz).lx;
	if(*by>=(*mz).ly)(*by)-=(*mz).ly;
	if(*by <       0)(*by)+=(*mz).ly;
}


static int MazeGetLineWall(MAZEMAP *mz,int x,int y)
{
	int r,bx,by;

	if(x>=(*mz).lx)x-=(*mz).lx;
	if(x <       0)x+=(*mz).lx;
	if(y>=(*mz).ly)y-=(*mz).ly;
	if(y <       0)y+=(*mz).ly;

	if( (r=MazeWall(mz,x,y,0))!=0 )return r;
	if( (r=MazeWall(mz,x,y,1))!=0 )return r;

	bx=x-1; by=y-1;
	if(bx<0)bx+=(*mz).lx;
	if(by<0)by+=(*mz).ly;
	if( (r=MazeWall(mz,bx,by,2))!=0 )return r;
	if( (r=MazeWall(mz,bx,by,3))!=0 )return r;

	return 0;
}


static void MazeScanLine(MAZEMAP *mz,
                       int bx,int by,int sx,int sy,
                       int vx,int vy,int length,MAZEZBUF *xbuf,int h)
{
	int tx,ty,r,bl,x,y,p,flt,fc;
	bl=(*mz).bl;x=0;y=0;p=0;r=0;flt=0;

	xbuf->viewz=1;
	xbuf->edgedist=0;
	xbuf->door=0;
	xbuf->floor[0].mark=0;
	xbuf->floor[1].mark=0;

	while(length>x*x+y*y && r==0 && flt<3)
	{
		tx=sx;ty=sy;
		if( (fc=(mz->map[by*((*mz).lx)+bx]& 192))!=0)
		{
			MazeScanLineFloorCeiling
			     (fc/64,bl,x,y,tx,ty,tx+vx,ty+vy,xbuf,h);
		}
		if(MazeScanLineClip((*mz).bl,&tx,&ty,vx,vy))break;
		if(tx==sx && ty==sy)flt++;

		x+=(tx-sx);
		y+=(ty-sy);

		if(tx<=0 && ty<=0)
		{
			r=MazeGetLineWall(mz,bx,by);
			p=0;
			MazeScanLineCross(mz,&bx,&by,&sx,&sy,vx,vy);
		}
		else if(tx>=bl-1 && ty>=bl-1)
		{
			bx++;
			by++;
			r=MazeGetLineWall(mz,bx,by);
			p=0;
			MazeScanLineCross(mz,&bx,&by,&sx,&sy,vx,vy);
		}
		else if(tx<=0    && ty>=bl-1)
		{
			by++;
			r=MazeGetLineWall(mz,bx,by);
			p=0;
			MazeScanLineCross(mz,&bx,&by,&sx,&sy,vx,vy);
		}
		else if(tx>=bl-1 && ty<=0   )
		{
			bx++;
			r=MazeGetLineWall(mz,bx,by);
			p=0;
			MazeScanLineCross(mz,&bx,&by,&sx,&sy,vx,vy);
		}
        else if(tx<=0)
       	{
			r=MazeWall(mz,bx,by,1);
			p=bl-ty;
			bx--; sx=bl-1; sy=ty;
       	}
		else if(tx>=bl-1)
		{
			r=MazeWall(mz,bx,by,3);
			p=ty;
			bx++;
			sx=0;
			sy=ty;
		}
        else if(ty<=0)
        {
			r=MazeWall(mz,bx,by,0);
			p=tx;
			by--;
			sx=tx;
			sy=bl-1;
		}
		else if(ty>=bl-1)
		{
			r=MazeWall(mz,bx,by,2);
			p=bl-tx;
			by++;
			sx=tx;
			sy=0;
		}
		else break;

		while(bx<0)bx+=(*mz).lx;
		while(by<0)by+=(*mz).ly;
		while(bx>=(*mz).lx)bx-=(*mz).lx;
		while(by>=(*mz).ly)by-=(*mz).ly;
	}

	y=-y;
	Rot2(&x,&y,-h);
	xbuf->viewz=y;
	xbuf->edgedist=p;
	xbuf->door=r;
}
