/*!
@file polygon.c
@author d金魚
@note
このソースコードは
http://www.geocities.co.jp/SiliconValley-Oakland/9582/renders/prg02.htm
を参考にして作りました。

サイト管理者のYutaka Yoshisaka氏に感謝します。
*/

//一回だけ読み込むように仕向けるための印
#define DKUTIL_GRAPHICS_2D_POLYGON_C

#include <dkutil/graphics/2d/polygon.h>
#include <dkutil_c/dkcOSIndependent.h>
#include <math.h>

//#include <dkutil/graphics/2d/polygon.hpp>

typedef struct _MinMaxData{
	int min;
	int max;
	size_t min_offset;
}MinMaxData;

typedef struct _State{
	///頂点カウント
	int vertex_count;
	///増加X
	double inc_x;
	///カウント
	double x;
}State;

typedef DKC_POINT2D point_type;


static void CalcMinMaxX(point_type *lpVer,size_t size,MinMaxData *result){
	size_t i,toffset = 0;
	int min,max,t;

	min = max = lpVer->x;

	for(i=1;i<size;i++){
		t = lpVer[i].x;
		if(t < min){
			toffset = i;
			min = t;
		}
		if(t > max){
			max = t;
		}

	}
	result->max = max;
	result->min = min;
	result->min_offset = toffset;

}

/*static double getXIncrease(int x,int x2,int y,int y2){
	double t = (y-y2);
  return (x-x2) / t;
}*/
static double getXIncrease(point_type *a,point_type *b){
	double t = a->y - b->y;
	return (a->x - b->x) / t;
}

///Y座標値で最大・最小のものを求める
static BOOL CalcMinMaxY(point_type *lpVer,size_t size,MinMaxData *result)
{
	//double ty,min,max;
	int ty,min,max;
	//int imin,imax;
	size_t i;
	int r = FALSE;

	size_t toffset = 0;
	
	max = min = lpVer->y;
	//lpVer++;
	for(i=1;i<size;i++)
	{
    //ty = lpVer->y;
		ty = lpVer[i].y;
    if(min>ty){
			toffset = i;
			min=ty;
    }
    if(max<ty){
			max=ty;
		}
		//lpVer++;
  }
  //imin=(int)floor(min+0.5);
  //imax=(int)floor(max+0.5);
	//imin = min;
	//imax = max;

	//set result
	result->min = min;
	result->max = max;
	result->min_offset = toffset;

  if(min!=max){
		r = TRUE;
	}
Exit:
	return r;
}
/*!
@param lpVertex[in] 頂点データ
@param size[in] 頂点の数
@param state[in][out] 状態保存データへのポインタ
@param y[in] y座標
*/
static void CalcLeftX(point_type *lpVertex,const size_t size,int y,State *state){
	//頂点巡回のカウンタ
	size_t i = state->vertex_count;
	//保存用
	size_t t;
	do{
		i++;
		if(i>size - 1)
		{//限界を超えたら最初に戻す
			i = 0;
		}
	}while(lpVertex[i].y < y + 0.5f);

	
	if(0 == i){
		t = size - 1;
	}else{
		//ここがイマイチヨクワカラン
		t = i - 1;
	}
		
	//update
	
	//次の頂点とで増加量を求める
	state->inc_x =  getXIncrease(lpVertex + i,lpVertex + t);
	
	state->x = lpVertex[t].x;
	state->vertex_count = i;

}

///@see CalcLeftY
static void CalcRightX(point_type *lpVertex,const size_t size,int y,State *state){
	size_t i = state->vertex_count;
	size_t t;
	//右側にある頂点とX方向増加値の計算
  do{
      i--;
      //if(i<0) i=size-1;
			if(i + 1 == 0){
				i = size - 1;
			}
  } while(lpVertex[i].y < y + 0.5);
  t=i+1;
	if(t > size-1){
		t=0;
	}

  state->inc_x = getXIncrease(lpVertex + i,lpVertex + t);


  state-> x =lpVertex[t].x;

	
	state->vertex_count = i;

}

static DKC_INLINE void next(State *a){
	a->x += a->inc_x;
}

static int DrawPoly2D(point_type *lpVertex,int count,int color,DKC_2D_FIGURE_DRAW_INTERFACE *p)
{
	MinMaxData data,xdata;
	State leftx,rightx;
	int i;
	double x1,x2,t;
	int ix1,ix2;
	//point_type *lpVer = 	lpVertex + 1;




	
	if(FALSE==CalcMinMaxY(lpVertex,count,&data))
	{
		CalcMinMaxX(lpVertex,count,&xdata);

		(p->mLine)(xdata.min,data.min,xdata.max,data.max,color);
		return edk_SUCCEEDED;
			//return edk_FAILED;
	}
	leftx.vertex_count = data.min_offset;
	rightx.vertex_count = data.min_offset;
	
	//とりあえず、増加量とか求めてみる
	CalcLeftX(lpVertex,count,data.min,&leftx);
	CalcRightX(lpVertex,count,data.min,&rightx);

	//i は y座標
	for(i = data.min;i < data.max;i++)
	{
		if(lpVertex[leftx.vertex_count].y < i + 0.5)
		//if(lpVertex[leftx.vertex_count].y < i + 1)
		{
			CalcLeftX(lpVertex,count,i,&leftx);
		}
		if(lpVertex[rightx.vertex_count].y < i + 0.5)
		//if(lpVertex[rightx.vertex_count].y < i + 1)
		{
			CalcRightX(lpVertex,count,i,&rightx);
		}
		
		{
			x1 = leftx.x;
			x2 = rightx.x;
			
			
			
			if(x2 == x1){
				(p->mPixel)(x1,i,color);
				goto Update;
			}else if(x2<x1)
			{//swap
				t=x1;
				x1=x2;
				x2=t;
      }


			//ix1 = x1;
			//ix2 = x2;
			ix1 = (int)floor(x1 + 0.5);
			ix2 = (int)floor(x2 + 0.5);
			
			//ライン描画
			(p->mLine)(ix1,i,ix2,i,color);
Update:			
			next(&leftx);
			next(&rightx);
			continue;
		}
		
		
		
	}//end of for
		
	return edk_SUCCEEDED;
}

int WINAPI dkcDrawPolygon2D(point_type *lpVertex,size_t count,int color,DKC_2D_FIGURE_DRAW_INTERFACE *p){

	switch(count){
	case 0:
		return edk_FAILED;
	case 1:
		(p->mPixel)(lpVertex->x,lpVertex->y,color);
		break;
	case 2:
		(p->mLine)(
			lpVertex[0].x,
			lpVertex[0].y,
			lpVertex[1].x,
			lpVertex[1].y,
			color
		);
		break;
	case 3:
		(p->mTriangle)(
			lpVertex[0].x,
			lpVertex[0].y,
			lpVertex[1].x,
			lpVertex[1].y,
			lpVertex[2].x,
			lpVertex[2].y,
			color
		);
		break;
	default:
		return DrawPoly2D(lpVertex,count,color,p);
		//break;
	}
	return edk_SUCCEEDED;
}


BOOL WINAPI dkcIsSquare(DKC_POINT2D *p,size_t size,DKC_RECT *rect){
	BOOL badflag = FALSE;
	RECT r={0,0,0,0};
	size_t i;
	if(size < 4 && NULL==p){
		return FALSE;
	}
	for(i=0;i<3;i++){
		if(p[i].x == p[i+1].x || p[i].y == p[i+1].y){
			
		}else{
			badflag = TRUE;
			break;
		}

		if( p[i+1].x < p[r.left].x){
			r.left = i + 1;
		}
		if( p[i+1].y < p[r.top].y){
			r.top = i + 1;
		}
		if ( p[i+1].x > p[r.right].x){
			r.right = i + 1;
		}
		if(p[i+1].y > p[r.bottom].y){
			r.bottom = i + 1;
		}
		
			
	}//end of for
	if((p[0].x == p[3].x || p[0].y == p[3].y) && FALSE==badflag)
	{//とりあえず、四角と断定（本当は違うかもしれないけど･･･ あと、冗長な処理だけどね･･･
		//DrawFillBox(p[0].x,p[0].y,p[3].x,p[3].y,gFillColor);
		r.left = p[r.left].x;
		r.top = p[r.top].y;
		r.right = p[r.right].x;
		r.bottom = p[r.bottom].y;
		if(rect){
			memcpy(rect,&r,sizeof(DKC_RECT));
		}
		return TRUE;
	}
	return FALSE;

}

