:- dynamic(haunter/2).
:- discontiguous(ghostDo/2).
:- discontiguous(ghostDoContinued/2).

/**
 @descr execute round-action for every ghost related to a player
 @form actionGhosts(PlayerID)
 */
phaseGhosts :-
	currentPlayer(Player),
	forall(
	       (ghostField(_,GhostID,Player),
	        ghost(GhostID,_,_,_,_,Action,_,_)),
	       ghostDo(GhostID,Action)).

/**
 @descr Ghost action
 @form ghostDo(GhostID,Action)
 */	
% No Action
ghostDo(_,none).

% Haunter
ghostDo(GhostID,haunt) :-
	ghostField(FieldID,GhostID,_),
	haunt(FieldID).    

% Haunt village immediatly
ghostDo(GhostID,hauntVillage) :-
	ghostField(FieldID,GhostID,_),
	neighbourRow(FieldID,TileID),
	hauntVillage(TileID).

% No dice allowed against ghost
ghostDo(_,noDice).

% Add ghost
ghostDo(_,addGhost) :-
	addGhost.

% Add ghost and block power
ghostDo(_,addGhostNoPower) :-
	addGhost.

% Add ghost and steal a die
ghostDo(_,addGhostStealDie):-
	ghostDo(_,stealDie),
	addGhost.

% roll cursedie
ghostDo(GhostID,curseDie):-
	ghostField(FieldID,GhostID,_),
	neighbourRow(FieldID,TileID),	
	curseDie(TileID).
      
% select any taomarker
ghostDo(_,anyTao):-
	setBuffer(anyTao),
	playerSelectGhostTaoMarker(anyTao).

ghostDoContinued([TaoMarkerList]):-
	%check the given input
	buffer(anyTao),
	sumlist(TaoMarkerList,1),
	addTaoMarkers(TaoMarkerList).
	
% select any two taomarkers
ghostDo(_,anyTwoTao):-
	setBuffer(anyTwoTao),
	playerSelectGhostTaoMarker(anyTwoTao).

ghostDoContinued([TaoMarkerList]):-
	buffer(anyTwoTao),
	%check the given input
	sumlist(TaoMarkerList,1),
	addTaoMarkers(TaoMarkerList),
	ghostDo(_,anyTao).

% select qi or yingyang
ghostDo(_,qiYY):-
	currentPlayer(Player),
	taoist(Player,_,_,_,YYMarker,_),
	setBuffer(qiYY),	
	checkYYCount(YYMarker).
	
checkYYCount(0):-
	%if the player has no YY-Marker	
	playerSelectQiOrYY.

checkYYCount(1):-
	%if the player still have his YY-Marker
	ghostDoContinued([qi]).
	
ghostDoContinued([qi]):-
	buffer(qiYY),
	addQi(1).

ghostDoContinued([yy]):-
	buffer(qiYY),
	currentPlayer(Player),
	NewYYMarker is 1,
	retract(taoist(Player,P2,P3,P4,_,P6)),
	assert(taoist(Player,P2,P3,P4,NewYYMarker,P6)),
	broadcastYY(Player).

% roll cursedie and select qi or yingyang
ghostDo(GhostID,curseDieQiYY):-
	currentPlayer(Player),
	taoist(Player,_,_,_,YYMarker,_),
	setBuffer((curseDieQiYY,GhostID)),	
	checkYYCount(YYMarker).
	
ghostDoContinued(Args) :-
	buffer((curseDieQiYY,GhostID)),
	setBuffer(qiYY),
	ghostDoContinued(Args),       	
	ghostField(FieldID,GhostID,_),
	neighbourRow(FieldID,TileID),	
	curseDie(TileID).

% steal a die
ghostDo(_,stealDie).

% steal one tao
% don't have one
ghostDo(_,stealTao) :-
	currentPlayer(Player),
	taoist(Player,_,Markers,_,_,_),
	sumlist(Markers,0).

% have only one tao
ghostDo(_,stealTao) :-
	currentPlayer(Player),
	taoist(Player,_,Markers,_,_,_),
	sumlist(Markers,1),
	setTaoMarkers([0,0,0,0,0]).

% have more than one tao
ghostDo(_,stealTao) :-
	setBuffer(stealTao),
	playerSelectGhostTaoMarker(stealTao).

ghostDoContinued([TaoMarkerList]) :-
	%check given Input
	buffer(stealTao),
	sumlist(TaoMarkerList,1),
	currentPlayer(Player),
	taoist(Player,_,Markers,_,_,_),
	vectorSub(Markers,TaoMarkerList,Diff),
	normRes(Diff,Markers),
	!,
	playerSelectGhostTaoMarker(stealTao).
	
ghostDoContinued([TaoMarkerList]) :-
	buffer(stealTao),
	sumlist(TaoMarkerList,1),
	vectorinvers(TaoMarkerList,InversList),
	addTaoMarkers(InversList).

% steal one qi
ghostDo(_,stealQi):-
	addQi(-1).

% lock taomarkers
ghostDo(_,lockTao).

% select Qi and take one Yin Yang 
ghostDo(_,addQiAndYY) :-
	setBuffer(addQiAndYY),
	yySelect.
	
yySelect :-
	%every taoist still has his YYmarker
	forall(	taoist(_,_,_,_,YY,_), YY = 1 ),
	qiSelect.

yySelect :-
	findall( Player,
		 taoist(Player,_,_,_,0,_),
		 PlayerList ),
	playerSelectTaoist( PlayerList ).

ghostDoContinued([Player]) :-
	buffer(addQiAndYY),
	addYY(Player),
	qiSelect.

qiSelect :-
	setBuffer( addQiAndYY2 ),
	findall( Player,
		 taoist(Player,_,_,X,_,_), X>0,
		 PlayerList ),
	playerSelectTaoist( PlayerList ).

ghostDoContinued([Player]) :-
	buffer(addQiAndYY2),
	addQi(Player,1).


% roll curseDie select Qi and take Yin Yang
ghostDo(GhostID,curseDieAndAddQiAndYY) :-
	setBuffer((curseDieAndAddQiAndYY,GhostID)),
	yySelect.

qiSelect :-
	buffer( (curseDieAndAddQiAndYY,GhostID) ),
	setBuffer( (curseDieAndAddQiAndYY2,GhostID) ),
	findall( Player,
		 taoist(Player,_,_,X,_,_), X>0,
		 PlayerList ),
	playerSelectTaoist( PlayerList ).

ghostDoContinued([Player]) :-
	buffer((curseDieAndAddQiAndYY2,GhostID)),
	addQi(Player,1),
	ghostDo(GhostID,curseDie).


% auf Dem Feld Gegenueber Darf Kein Geist Stehen Beim Austreiben
ghostDo(_,noGhostTowards).

% auf Dem Feld Mit Buddha Austreiben
ghostDo(_,placeOnBuddha):-
	retractall(uncatchable(_)),
	assert(uncatchable(uncatched)).

%spiel Tafeln Und Taoisten Verlieren Ihre Spezial Faehigkeiten
ghostDo(_,lockPowerAll).

% alle Taoisten Verlieren Einen TaoMarker
ghostDo(_,alleTaoistenVerlierenEinenTaoMarker).

% lock TaoMarker
ghostDo(_,lockTao).

% gebetskreis Entfernen
ghostDo(_,gebetskreisEntfernen).

%white Dice No Joker

ghostDo(_,whiteDiceNoJoker).

/**
 @descr draw a card and ask player to place the ghost
 @form addGhost
 */
phaseDrawGhost :-
	currentPlayer(PlayerID),
	getFreeFields(PlayerID,[]),
	addQi(-1).

phaseDrawGhost :-
	currentPlayer(PlayerID),
	getFreeFields(PlayerID,L),
	not(L = []),
	setLock(true),
	cards([GhostID|_]),
	ghost(GhostID,_,Color,_,_,_,_,_),
        selectField(PlayerID,Color,FieldList),
	broadcastGhostDraw(GhostID),
	playerGhostPlace(GhostID,FieldList).

addGhost :-	
	getFreeFields([]),
	addQi(-1).

addGhost :-
	currentPlayer(PlayerID),
	getFreeFields(L),
	not(L = []),
	setLock(true),
	cards([GhostID|_]),
	ghost(GhostID,_,Color,_,_,_,_,_),
        selectField(PlayerID,Color,FieldList),
	broadcastGhostDraw(GhostID),
	playerGhostPlace(GhostID,FieldList).

/**
 @descr place a ghost
 @form addGhost(FieldID)
 */
addGhost(FieldID) :-
	currentPlayer(PlayerID),
	cards([GhostID|_]),
	ghost(GhostID,_,Color,_,_,_,_,_),
	selectField(PlayerID,Color,FieldList),
	member(FieldID,FieldList),
	takeCard(GhostID),
	placeGhost(GhostID,FieldID).
	
/**
 @descr places a ghost at a given field
 @form placeGhost(GhostID,FieldID)
 */	
placeGhost(GhostID,FieldID) :-
	placeGhostNow(GhostID,FieldID),
	setLock(false).

% No buddah at field 
placeGhostNow(GhostID,FieldID) :-
	not(buddha(_,FieldID)),
	!,
	retract(ghostField(FieldID,0,Color)),
	assert(ghostField(FieldID,GhostID,Color)),
	broadcastGhostPlace(GhostID,FieldID),
	ghost(GhostID,_,_,_,InitAction,RoundAction,_,_),
	placeGhostAction(GhostID,InitAction,RoundAction).

% Place Uncatchable on Buddha Field
placeGhostNow(57,FieldID) :-
	!,
	buddha(BuddhaID,FieldID),
	village(6,_,_,_,Pos),
	placeBuddha(BuddhaID,Pos),	
	retract(uncatchable(_)),
	assert(uncatchable(catched)),
	retract(ghostField(FieldID,0,Color)),
	assert(ghostField(FieldID,57,Color)),
	broadcastGhostPlace(57,FieldID),
	ghost(GhostID,_,_,_,InitAction,RoundAction,_,_),
	placeGhostAction(GhostID,InitAction,RoundAction).
	
% Place Wu Feng on Buddha Field
placeGhostNow(GhostID,FieldID) :-
	GhostID >= 56,
	GhostID =< 65,
	!,
	buddha(BuddhaID,FieldID),
	village(6,_,_,_,Pos),
	placeBuddha(BuddhaID,Pos),		
	retract(ghostField(FieldID,0,Color)),
	assert(ghostField(FieldID,GhostID,Color)),
	broadcastGhostPlace(GhostID,FieldID),
	ghost(GhostID,_,_,_,InitAction,RoundAction,_,_),
	placeGhostAction(GhostID,InitAction,RoundAction).

% Place everyone else on Buddah field
placeGhostNow(_,FieldID) :-
	buddha(BuddhaID,FieldID),
	village(6,_,_,_,Pos),
	placeBuddha(BuddhaID,Pos),
	broadcastHidePreviewGhost.

placeGhostAction(GhostID,InitAction,haunt) :-
	!,
	ghostDo(GhostID,haunt),
	ghostDo(GhostID,InitAction).

placeGhostAction(GhostID,InitAction,_) :-
	ghostDo(GhostID,InitAction).

/**
 @descr asks to place a buddha if player have one
 @form addBuddha
 */
phaseBuddha :- 
	getFreeBuddhaFields([]),
	!,
	nextPhase.

phaseBuddha :-
	currentPlayer(PlayerID),
	not(buddha(_,PlayerID)),
	!,
	nextPhase.

phaseBuddha :-
	buffer(buddhaTaken),
	% Player have only one buddha
	currentPlayer(PlayerID),	
	buddha(_,ID),
	not(PlayerID = ID),
	setBuffer(none),
	!,
	nextPhase.

phaseBuddha :-
	buffer(buddhaTaken),
	!,
	getFreeBuddhaFields(FieldList),	
	playerBuddhaPlace(FieldList,1).
	

phaseBuddha :-
	getFreeBuddhaFields(FieldList),
	currentPlayer(PlayerID),
	buddha(BuddhaID,PlayerID),
	playerBuddhaPlace(FieldList,BuddhaID).

/**
 @descr places a buddha if FieldID is free, else do nothing
 @form addBuddha(FieldID)
 */
addBuddha(FieldID) :-
	getFreeBuddhaFields(FieldList),
	member(FieldID,FieldList),
	currentPlayer(PlayerID),
	buddha(BuddhaID,PlayerID),
	placeBuddha(BuddhaID,FieldID),
	phaseBuddha.

/**
 @descr save new buddhafield
 @form placeBuddha(BuddhaID,FieldID)
 */
placeBuddha(BuddhaID,FieldID) :-
	retract(buddha(BuddhaID,_)),
	assert(buddha(BuddhaID,FieldID)),
	broadcastBuddhaPlace(BuddhaID,FieldID).

/**
 @descr move haunter with id
 @form haunt(FieldID)
 */
% Init haunter
haunt(FieldID) :-
	not(haunter(FieldID,_)),
	assert(haunter(FieldID,0)),
	broadcastGhostHaunt(FieldID,0),
	!.

% Move haunter
haunt(FieldID) :-
	haunter(FieldID,Z),
	Z < 1,                    % Only haunt one time
	ZN is Z + 1,
	retract(haunter(FieldID,_)),
	assert(haunter(FieldID,ZN)),
	broadcastGhostHaunt(FieldID,ZN),
	!.

% Haunt village and reset haunter
haunt(FieldID) :-
	haunter(FieldID,Z),
	Z >= 1,
	neighbourRow(FieldID,TileID),
	hauntVillage(TileID),
	retract(haunter(FieldID,_)),
	assert(haunter(FieldID,0)),
	broadcastGhostHaunt(FieldID,0),
	!.

/**
 @descr returns ghosts who are attackable from the VillageTile
 @form neighbourDirect(TileID,GhostID).
 */
neighbourDirect(TileID,GhostList) :-
        findall((TileID,A),
		(   ghostField((TileID,A),X,_),
		    X > 0),
		GhostList).
/**
 @descr returns next unhaunted tile, 
	returns 0 if every Tile in the row is already haunted
 @form neighbourRow(FieldID,TileID)
 */
neighbourRow(FieldID,TileID) :-
	neighbourRow(FieldID,3,TileID).

% no unhaunted tile found
neighbourRow(_,0,0).

% unhaunted tile found.
neighbourRow((Start,_),Acc,TileID) :- 
	Acc > 0,
	village(TileID,_,_,none,Start).

% haunted tile found
neighbourRow((Start,Add),Acc,TileID) :-
	Acc > 0,
	AccN is Acc - 1,
	village(_,_,_,haunted,Start),
	StartN is Start + Add,
       	neighbourRow((StartN,Add),AccN,TileID).

/**
@descr Returns a list of free ghostfielfIDs  
@form ghostfield_is_full(?Color of player,?List of free ghostFieldIDs)
*/
getFreeFields(PlayerID,L) :-  
	findall(FieldID,
		ghostField(FieldID,0,PlayerID),		 
		L).

getFreeFields(FreeList) :-  
	findall(FieldID,
		ghostField(FieldID,0,_),
		FreeList).

getFreeBuddhaFields(FreeList) :-
	%get the players position
	currentPlayer(Player),
	taoist(Player,_,_,_,_,Pos),
	%get all neighbour ghostfields, where are no buddhas
	findall((Pos,A),
		(   ghostField((Pos,A),_,_),
		    not(buddha(_,(Pos,A))),
		    ghostField(_,0,_)
		),
		FreeList).

getOccupiedFields(OccupiedList) :-  
	findall(FieldID,
		(   ghostField(FieldID,Z,_),
		    Z > 0),
		OccupiedList).

/**
 @descr returns the field, at which the ghost shell be placed.
 @form selectField(PlayerID,GhostColor,FieldID)
 */
% Ghost's color = aktive player's color without free space
selectField(PlayerID,Color,FieldList) :-
	PlayerID = Color,
	getFreeFields(PlayerID,[]),
	getFreeField(FieldList),
	!.

% Ghost's color = aktive player's color with free space
selectField(PlayerID,Color,FieldList) :-
	PlayerID = Color,
	getFreeFields(PlayerID,FieldList),
	!.

% Ghost's color = black with free space
selectField(PlayerID,Color,FieldList) :-
	Color = black,
	getFreeFields(PlayerID,FieldList),
	not(FieldList  = []).

% Ghost's color = black, with no free space
selectField(PlayerID,Color,FieldList) :-
	Color = black,
	getFreeFields(PlayerID,[]),
	getFreeFields(FieldList).	

% Ghost's color = inaktive player's color with free space
selectField(PlayerID,Color,FieldList) :-
	not(PlayerID = Color),
	getFreeFields(Color,FieldList),
	not(FieldList = []).

% Ghost's color = inaktive Player's color without free space
selectField(PlayerID,Color,FieldList) :-
	not(PlayerID = Color),
	not(Color = black),
	getFreeFields(Color,[]),
	getFreeFields(FieldList).

