%%基于六边形地图的slg后端需要的相关算法
-module(hexMap).
-include("utComMisc.hrl").

-compile([export_all, nowarn_export_all]).

%%六边形画线算法 算法说明 http://zvold.blogspot.com/2010/01/bresenhams-line-drawing-algorithm-on_26.html
-define(samePoint(SX, SY, TX, TY), SX == TX andalso SY == TY).
lineDraw({SX, SY}, {TX, TY}) -> %% 11
	case ?samePoint(SX, SY, TX, TY) of
		true ->
			[{SX, SY}];
		_ ->
			X_2old = 2 * (TX - SX) + abs(TY rem 2) - abs(SY rem 2),
			Y_old = TY - SY,
			case abs(Y_old) < abs(X_2old) of
				true ->
					X_sign = ?CASE(X_2old >= 0, 1, -1),
					Y_sign = ?CASE(Y_old >= 0, 1, -1),
					X_diff = 3 * abs(X_2old),
					Y_diff = 3 * abs(Y_old),
					S = 0,
					lineDrawH(SX, SY, TX, TY, X_sign, Y_sign, X_diff, Y_diff, abs(X_2old), S, [{SX, SY}]);
				_ ->
					X_sign = ?CASE(X_2old >= 0, 1, -1),
					Y_sign = ?CASE(Y_old >= 0, 1, -1),
					X_diff = abs(X_2old),
					Y_diff = abs(Y_old),
					S = 0,
					lineDrawV(SX, SY, TX, TY, X_sign, Y_sign, X_diff, Y_diff, S, [{SX, SY}])
			end
	end.

lineDrawH(SX, SY, TX, TY, X_sign, Y_sign, X_diff, Y_diff, AX_2old, S, Acc) ->
	case ?samePoint(SX, SY, TX, TY) of
		true ->
			Acc;
		_ ->
			TS = S + Y_diff,
			case TS > AX_2old of
				true ->
					{NX, NY} = dir_point(X_sign, Y_sign, SX, SY),
					NS = TS - X_diff,
					lineDrawH(NX, NY, TX, TY, X_sign, Y_sign, X_diff, Y_diff, AX_2old, NS, [{NX, NY} | Acc]);
				_ ->
					{NX, NY} = dir_point(0, X_sign, SX, SY),
					NS = TS + Y_diff,
					lineDrawH(NX, NY, TX, TY, X_sign, Y_sign, X_diff, Y_diff, AX_2old, NS, [{NX, NY} | Acc])
			end
	end.

lineDrawV(SX, SY, TX, TY, X_sign, Y_sign, X_diff, Y_diff, S, Acc) ->
	case ?samePoint(SX, SY, TX, TY) of
		true ->
			Acc;
		_ ->
			TS = S + X_diff,
			case TS > 0 of
				true ->
					{NX, NY} = dir_point(X_sign, Y_sign, SX, SY),
					NS = TS - Y_diff,
					lineDrawV(NX, NY, TX, TY, X_sign, Y_sign, X_diff, Y_diff, NS, [{NX, NY} | Acc]);
				_ ->
					{NX, NY} = dir_point(-X_sign, Y_sign, SX, SY),
					NS = TS + Y_diff,
					lineDrawV(NX, NY, TX, TY, X_sign, Y_sign, X_diff, Y_diff, NS, [{NX, NY} | Acc])
			end
	end.

%%六边形视线算法 算法说明 http://zvold.blogspot.com/2010/02/line-of-sight-on-hexagonal-grid.html
lineSight(SX, SY, TX, TY) ->
	case ?samePoint(SX, SY, TX, TY) of
		true ->
			[{SX, SY}];
		_ ->
			TwoXOld = 2 * (TX - SX) + abs(TY rem 2) - abs(SY rem 2),
			YOld = TY - SY,
			SignX = ?CASE(TwoXOld >= 0, 1, -1),
			SignY = ?CASE(YOld >= 0, 1, -1),
			ADiffX = abs(TwoXOld),
			ADiffY = abs(YOld),
			S = -2 * ADiffX,
			lineSight(SX, SY, TX, TY, SignX, SignY, ADiffX, ADiffY, S, [{SX, SY}])
	end.

lineSight(SX, SY, TX, TY, SignX, SignY, ADiffX, ADiffY, S, Acc) ->
	case ?samePoint(SX, SY, TX, TY) of
		true ->
			Acc;
		_ ->
			case S >= 0 of
				true ->
					{NX, NY} = dir_point(-SignX, SignY, SX, SY),
					NS = S - 3 * ADiffY - 3 * ADiffX,
					lineSight(NX, NY, TX, TY, SignX, SignY, ADiffX, ADiffY, NS, [{NX, NY} | Acc]);
				_ ->
					TS = S + 3 * ADiffY,
					case TS > -ADiffX of
						true ->
							{NX, NY} = dir_point(SignX, SignY, SX, SY),
							NS = TS - 3 * ADiffX,
							lineSight(NX, NY, TX, TY, SignX, SignY, ADiffX, ADiffY, NS, [{NX, NY} | Acc]);
						_ ->
							case TS < -3 * ADiffX of
								true ->
									{NX, NY} = dir_point(SignX, -SignY, SX, SY),
									NS = TS + 3 * ADiffX,
									lineSight(NX, NY, TX, TY, SignX, SignY, ADiffX, ADiffY, NS, [{NX, NY} | Acc]);
								_ ->
									{NX, NY} = dir_point(0, SignX, SX, SY),
									NS = TS + 3 * ADiffY,
									lineSight(NX, NY, TX, TY, SignX, SignY, ADiffX, ADiffY, NS, [{NX, NY} | Acc])
							end
					end
			end
	end.


-define(HEXAGON_X, {0, 1, -1, -1, 0, 0, 0, -1}).%%六边形格子偶数行
-define(HEXAGON_X1, {1, 1, 0, -1, 0, 1, 0, 0}).%%六边形格子基数行
-define(HEXAGON_Y, {-1, 0, 1, 0, 0, 1, 0, -1}).%%六边形格子列
-define(HEXAGON_DIRECTION, [2, 5, 3, 1, 7, 0]).%%六边形格子的方向

-define(HEXAGON_DIRECTION_RIGHT_DOWN, 0). %% 右下
-define(HEXAGON_DIRECTION_RIGHT, 1). %% 右
-define(HEXAGON_DIRECTION_LEFT_TOP, 2). %% 左上
-define(HEXAGON_DIRECTION_LEFT, 3). %% 左
-define(HEXAGON_DIRECTION_RIGHT_TOP, 5). %% 右上
-define(HEXAGON_DIRECTION_LEFT_DOWN, 7). %% 左下

-define(HexDirRightUp, 1).
-define(HexDirRight, 2).
-define(HexDirRightDown, 3).
-define(HexDirLeftDown, 4).
-define(HexDirLeft, 5).
-define(HexDirLeftUp, 6).

dir_point(-1, -1, SX, SY) ->
	get_point_xy(SX, SY, ?HexDirLeftDown);
dir_point(-1, 1, SX, SY) ->
	get_point_xy(SX, SY, ?HexDirLeftUp);
dir_point(1, -1, SX, SY) ->
	get_point_xy(SX, SY, ?HexDirRightDown);
dir_point(1, 1, SX, SY) ->
	get_point_xy(SX, SY, ?HexDirRightUp);
dir_point(0, -1, SX, SY) ->
	get_point_xy(SX, SY, ?HexDirLeft);
dir_point(0, 1, SX, SY) ->
	get_point_xy(SX, SY, ?HexDirRight).

%% ----------------------------------------------------
%% @doc
%%      根据方向获取坐标
%% @end
%% ----------------------------------------------------
get_point_xy(X, Y, D) ->
	Bool = (Y rem 2) =:= 0,%% D band 1 等效
	if
		Bool ->%%偶数列
			NX = element(D + 1, ?HEXAGON_X) + X,
			NY = element(D + 1, ?HEXAGON_Y) + Y,
			{NX, NY};
		true ->
			NX = element(D + 1, ?HEXAGON_X1) + X,
			NY = element(D + 1, ?HEXAGON_Y) + Y,
			{NX, NY}
	end.


getDirXY(X, Y, Dir) ->
	case (Y rem 2) of
		0 ->
			NX = element(Dir + 1, ?HEXAGON_X) + X,
			NY = element(Dir + 1, ?HEXAGON_Y) + Y,
			{NX, NY};
		_ ->
			NX = element(Dir + 1, ?HEXAGON_X1) + X,
			NY = element(Dir + 1, ?HEXAGON_Y) + Y,
			{NX, NY}
	end.
