/
com/planet_ink/coffee_mud/Abilities/Common/
com/planet_ink/coffee_mud/Abilities/Diseases/
com/planet_ink/coffee_mud/Abilities/Druid/
com/planet_ink/coffee_mud/Abilities/Fighter/
com/planet_ink/coffee_mud/Abilities/Languages/
com/planet_ink/coffee_mud/Abilities/Misc/
com/planet_ink/coffee_mud/Abilities/Prayers/
com/planet_ink/coffee_mud/Abilities/Properties/
com/planet_ink/coffee_mud/Abilities/Skills/
com/planet_ink/coffee_mud/Abilities/Songs/
com/planet_ink/coffee_mud/Abilities/Spells/
com/planet_ink/coffee_mud/Abilities/Thief/
com/planet_ink/coffee_mud/Abilities/Traps/
com/planet_ink/coffee_mud/Behaviors/
com/planet_ink/coffee_mud/CharClasses/interfaces/
com/planet_ink/coffee_mud/Commands/
com/planet_ink/coffee_mud/Commands/interfaces/
com/planet_ink/coffee_mud/Common/
com/planet_ink/coffee_mud/Common/interfaces/
com/planet_ink/coffee_mud/Exits/interfaces/
com/planet_ink/coffee_mud/Items/Armor/
com/planet_ink/coffee_mud/Items/Basic/
com/planet_ink/coffee_mud/Items/CompTech/
com/planet_ink/coffee_mud/Items/MiscMagic/
com/planet_ink/coffee_mud/Items/Weapons/
com/planet_ink/coffee_mud/Items/interfaces/
com/planet_ink/coffee_mud/Libraries/
com/planet_ink/coffee_mud/Libraries/interfaces/
com/planet_ink/coffee_mud/Locales/
com/planet_ink/coffee_mud/MOBS/
com/planet_ink/coffee_mud/Races/
com/planet_ink/coffee_mud/Races/interfaces/
com/planet_ink/coffee_mud/WebMacros/
com/planet_ink/coffee_mud/WebMacros/interfaces/
com/planet_ink/coffee_mud/core/
com/planet_ink/coffee_mud/core/collections/
com/planet_ink/coffee_mud/core/interfaces/
com/planet_ink/coffee_mud/core/intermud/
com/planet_ink/coffee_mud/core/intermud/i3/
com/planet_ink/coffee_web/server/
com/planet_ink/siplet/applet/
lib/
resources/factions/
resources/fakedb/
resources/progs/autoplayer/
resources/quests/holidays/
web/
web/admin.templates/
web/admin/grinder/
web/admin/images/
web/clan.templates/
web/pub.templates/
web/pub/images/mxp/
web/pub/sounds/
web/pub/textedit/
package com.planet_ink.coffee_mud.Libraries;
import com.planet_ink.coffee_mud.core.interfaces.*;
import com.planet_ink.coffee_mud.core.*;
import com.planet_ink.coffee_mud.core.collections.*;
import com.planet_ink.coffee_mud.Abilities.interfaces.*;
import com.planet_ink.coffee_mud.Areas.interfaces.*;
import com.planet_ink.coffee_mud.Behaviors.interfaces.*;
import com.planet_ink.coffee_mud.CharClasses.interfaces.*;
import com.planet_ink.coffee_mud.Commands.interfaces.*;
import com.planet_ink.coffee_mud.Common.interfaces.*;
import com.planet_ink.coffee_mud.Exits.interfaces.*;
import com.planet_ink.coffee_mud.Items.interfaces.*;
import com.planet_ink.coffee_mud.Locales.interfaces.*;
import com.planet_ink.coffee_mud.MOBS.interfaces.*;
import com.planet_ink.coffee_mud.Races.interfaces.*;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.*;

import com.planet_ink.coffee_mud.Libraries.interfaces.*;

/*
   Copyright 2006-2016 Bo Zimmerman

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

	   http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
public class MUDLaw extends StdLibrary implements LegalLibrary
{
	@Override
	public String ID()
	{
		return "MUDLaw";
	}

	@Override
	public Law getTheLaw(Room R, MOB mob)
	{
		final LegalBehavior B=getLegalBehavior(R);
		if(B!=null)
		{
			final Area A2=getLegalObject(R.getArea());
			return B.legalInfo(A2);
		}
		return null;
	}

	@Override
	public LegalBehavior getLegalBehavior(Area A)
	{
		if(A==null)
			return null;
		final List<Behavior> V=CMLib.flags().flaggedBehaviors(A,Behavior.FLAG_LEGALBEHAVIOR);
		if(V.size()>0)
			return (LegalBehavior)V.get(0);
		LegalBehavior B=null;
		for(final Enumeration<Area> e=A.getParents();e.hasMoreElements();)
		{
			B=getLegalBehavior(e.nextElement());
			if(B!=null)
				break;
		}
		return B;
	}

	@Override
	public LegalBehavior getLegalBehavior(Room R)
	{
		if(R==null)
			return null;
		final List<Behavior> V=CMLib.flags().flaggedBehaviors(R,Behavior.FLAG_LEGALBEHAVIOR);
		if(V.size()>0)
			return (LegalBehavior)V.get(0);
		return getLegalBehavior(R.getArea());
	}

	@Override
	public Area getLegalObject(Area A)
	{
		if(A==null)
			return null;
		final List<Behavior> V=CMLib.flags().flaggedBehaviors(A,Behavior.FLAG_LEGALBEHAVIOR);
		if(V.size()>0)
			return A;
		Area A2=null;
		Area A3=null;
		for(final Enumeration<Area> e=A.getParents();e.hasMoreElements();)
		{
			A2=e.nextElement();
			A3=getLegalObject(A2);
			if(A3!=null)
				return A3;
		}
		return A3;
	}

	@Override
	public Area getLegalObject(Room R)
	{
		if(R==null)
			return null;
		return getLegalObject(R.getArea());
	}

	@Override
	public boolean isACity(Area A)
	{
		int other=0;
		int streets=0;
		int buildings=0;
		Room R=null;
		for(final Enumeration<Room> e=A.getCompleteMap();e.hasMoreElements();)
		{
			R=e.nextElement();
			if((R==null)||(R.roomID()==null)||(R.roomID().length()==0))
				continue;
			if(R.domainType()==Room.DOMAIN_OUTDOORS_CITY)
				streets++;
			else
			if((R.domainType()==Room.DOMAIN_INDOORS_METAL)
			||(R.domainType()==Room.DOMAIN_INDOORS_STONE)
			||(R.domainType()==Room.DOMAIN_INDOORS_WOOD))
				buildings++;
			else
				other++;
		}
		if((streets<(other/2))||((streets+buildings)<other))
			return false;
		return true;
	}

	@Override
	public List<LandTitle> getAllUniqueLandTitles(Enumeration<Room> e, String owner, boolean includeRentals)
	{
		final Vector<LandTitle> V=new Vector<LandTitle>();
		final HashSet<Room> roomsDone=new HashSet<Room>();
		Room R=null;
		for(;e.hasMoreElements();)
		{
			R=e.nextElement();
			final LandTitle T=getLandTitle(R);
			if((T!=null)
			&&(!V.contains(T))
			&&(includeRentals||(!T.rentalProperty()))
			&&((owner==null)
				||(owner.length()==0)
				||(owner.equals("*")&&(T.getOwnerName().length()>0))
				||(T.getOwnerName().equals(owner))))
			{
				final List<Room> V2=T.getAllTitledRooms();
				boolean proceed=true;
				for(int v=0;v<V2.size();v++)
				{
					final Room R2=V2.get(v);
					if(!roomsDone.contains(R2))
						roomsDone.add(R2);
					else
						proceed=false;
				}
				if(proceed)
					V.addElement(T);

			}
		}
		return V;
	}

	@Override
	public LandTitle getLandTitle(Area area)
	{
		if(area==null)
			return null;
		if(area instanceof LandTitle)
			return (LandTitle)area;
		for(final Enumeration<Ability> a=area.effects();a.hasMoreElements();)
		{
			final Ability A=a.nextElement();
			if((A!=null)&&(A instanceof LandTitle))
				return (LandTitle)A;
		}
		return null;
	}

	@Override
	public PrivateProperty getPropertyRecord(Area area)
	{
		if(area==null)
			return null;
		if(area instanceof PrivateProperty)
			return (PrivateProperty)area;
		for(final Enumeration<Ability> a=area.effects();a.hasMoreElements();)
		{
			final Ability A=a.nextElement();
			if((A!=null)&&(A instanceof PrivateProperty))
				return (PrivateProperty)A;
		}
		return null;
	}
	
	@Override
	public LandTitle getLandTitle(Room room)
	{
		if(room==null)
			return null;
		final LandTitle title=getLandTitle(room.getArea());
		if(title!=null)
			return title;
		for(final Enumeration<Ability> a=room.effects();a.hasMoreElements();)
		{
			final Ability A=a.nextElement();
			if((A!=null)&&(A instanceof LandTitle))
				return (LandTitle)A;
		}
		return null;
	}

	@Override
	public PrivateProperty getPropertyRecord(Room room)
	{
		if(room==null)
			return null;
		if(room instanceof PrivateProperty)
			return (PrivateProperty)room;
		final PrivateProperty record=getPropertyRecord(room.getArea());
		if(record!=null)
			return record;
		for(final Enumeration<Ability> a=room.effects();a.hasMoreElements();)
		{
			final Ability A=a.nextElement();
			if((A!=null)&&(A instanceof PrivateProperty))
				return (PrivateProperty)A;
		}
		return null;
	}

	@Override
	public boolean isHomeRoomUpstairs(Room room)
	{
		final Room dR=room.getRoomInDir(Directions.DOWN);
		if((dR!=null)&&(dR.domainType() != Room.DOMAIN_INDOORS_CAVE))
		{
			if(isHomePeerRoom(dR))
				return true;
			final Set<Room> peerRooms=getHomePeersOnThisFloor(room,new HashSet<Room>());
			for(final Room R : peerRooms)
			{
				final Room pdR=R.getRoomInDir(Directions.DOWN);
				if((pdR!=null)
				&&(pdR.domainType() != Room.DOMAIN_INDOORS_CAVE)
				&&(isHomePeerRoom(pdR)))
					return true;
			}
		}
		return false;
	}

	public boolean isHomePeerRoom(Room R)
	{
		return ifHomePeerLandTitle(R)!=null;
	}

	public LandTitle ifHomePeerLandTitle(Room R)
	{
		if((R!=null)
		&&(R.ID().length()>0)
		&&(CMath.bset(R.domainType(),Room.INDOORS)))
			return CMLib.law().getLandTitle(R);
		return null;
	}

	public LandTitle ifLandTitle(Room R)
	{
		if((R!=null)
		&&(R.ID().length()>0))
			return CMLib.law().getLandTitle(R);
		return null;
	}

	@Override
	public boolean isRoomSimilarlyTitled(LandTitle title, Room R)
	{
		final LandTitle ptitle = ifLandTitle(R);
		if(ptitle ==null)
			return false;
		if(ptitle.getOwnerName().length()==0)
		{
			for(int d=0;d<Directions.NUM_DIRECTIONS();d++)
			{
				final Room sideRoom=R.getRoomInDir(d);
				final LandTitle psTitle=ifLandTitle(sideRoom);
				if(psTitle.getOwnerName().equals(title.getOwnerName()))
					return true;
			}
			return false;
		}
		else
			return ptitle.getOwnerName().equals(title.getOwnerName());
	}

	@Override
	public Set<Room> getHomePeersOnThisFloor(Room room, Set<Room> doneRooms)
	{
		for(int d=0;d<Directions.NUM_DIRECTIONS();d++)
		{
			if((d!=Directions.UP)&&(d!=Directions.DOWN)&&(d!=Directions.GATE))
			{
				final Room sideRoom=room.getRoomInDir(d);
				if(isHomePeerRoom(sideRoom)  &&(!doneRooms.contains(sideRoom)))
				{
					doneRooms.add(sideRoom);
					doneRooms.addAll(getHomePeersOnThisFloor(sideRoom, doneRooms));
				}
			}
		}
		return doneRooms;
	}

	@Override
	public boolean isHomeRoomDownstairs(Room room)
	{
		if(isHomePeerRoom(room.getRoomInDir(Directions.UP)))
			return true;
		final Set<Room> peerRooms=getHomePeersOnThisFloor(room,new HashSet<Room>());
		for(final Room R : peerRooms)
		{
			if(isHomePeerRoom(R.getRoomInDir(Directions.UP)))
				return true;
		}
		return false;
	}

	@Override
	public boolean doesHavePriviledgesInThisDirection(MOB mob, Room room, Exit exit)
	{
		final int dirCode=CMLib.map().getExitDir(room,exit);
		if(dirCode<0)
			return false;
		final Room otherRoom=room.getRoomInDir(dirCode);
		if(otherRoom==null)
			return false;
		return doesHavePriviledgesHere(mob,otherRoom);
	}

	@Override
	public boolean doesHavePriviledgesHere(MOB mob, Room room)
	{
		final PrivateProperty record=getPropertyRecord(room);
		if(record==null)
			return false;
		if(record.getOwnerName()==null)
			return false;
		if(record.getOwnerName().length()==0)
			return false;
		if(record.getOwnerName().equals(mob.Name()))
			return true;
		if((record.getOwnerName().equals(mob.getLiegeID())&&(mob.isMarriedToLiege())))
			return true;
		final Pair<Clan,Integer> clanRole=mob.getClanRole(record.getOwnerName());
		if((clanRole!=null)&&(clanRole.first.getAuthority(clanRole.second.intValue(), Clan.Function.HOME_PRIVS)!=Clan.Authority.CAN_NOT_DO))
			return true;
		if(mob.amFollowing()!=null)
			return doesHavePriviledgesHere(mob.amFollowing(),room);
		return false;
	}

	@Override
	public boolean doesAnyoneHavePrivilegesHere(MOB mob, String overrideID, Room R)
	{
		if((CMLib.law().doesHavePriviledgesHere(mob,R))||((overrideID.length()>0)&&(mob.Name().equals(overrideID))))
			return true;
		if(overrideID.length()>0)
		{
			final Pair<Clan,Integer> clanPair=mob.getClanRole(overrideID);
			if((clanPair!=null)&&(clanPair.first.getAuthority(clanPair.second.intValue(), Clan.Function.HOME_PRIVS)!=Clan.Authority.CAN_NOT_DO))
				return true;
		}
		for(int i=0;i<R.numInhabitants();i++)
		{
			final MOB M=R.fetchInhabitant(i);
			if(CMLib.law().doesHavePriviledgesHere(M,R))
				return true;
			if(overrideID.length()>0)
			{
				if(M.Name().equals(overrideID))
					return true;
				final Pair<Clan,Integer> clanPair=M.getClanRole(overrideID);
				if((clanPair!=null)&&(clanPair.first.getAuthority(clanPair.second.intValue(), Clan.Function.HOME_PRIVS)!=Clan.Authority.CAN_NOT_DO))
					return true;
			}
		}
		return false;
	}

	@Override
	public String getPropertyOwnerName(Room room)
	{
		final PrivateProperty record=getPropertyRecord(room);
		if(record==null)
			return "";
		if(record.getOwnerName()==null)
			return "";
		return record.getOwnerName();
	}

	@Override
	public String getLandOwnerName(Room room)
	{
		final LandTitle title=getLandTitle(room);
		if(title==null)
			return "";
		if(title.getOwnerName()==null)
			return "";
		return title.getOwnerName();
	}

	@Override
	public boolean doesOwnThisLand(String name, Room room)
	{
		final LandTitle title=getLandTitle(room);
		if(title==null)
			return false;
		if(title.getOwnerName()==null)
			return false;
		if(title.getOwnerName().length()==0)
			return false;
		if(title.getOwnerName().equals(name))
			return true;
		return false;
	}

	@Override
	public boolean doesOwnThisProperty(String name, Room room)
	{
		final PrivateProperty record=getPropertyRecord(room);
		if(record==null)
			return false;
		if(record.getOwnerName()==null)
			return false;
		if(record.getOwnerName().length()==0)
			return false;
		if(record.getOwnerName().equals(name))
			return true;
		return false;
	}

	@Override
	public boolean doesOwnThisProperty(MOB mob, Room room)
	{
		final PrivateProperty record=getPropertyRecord(room);
		if(record==null)
			return false;
		return doesOwnThisProperty(mob,record);
	}

	@Override
	public boolean doesOwnThisProperty(MOB mob, PrivateProperty record)
	{
		if(record.getOwnerName()==null)
			return false;
		if(record.getOwnerName().length()==0)
			return false;
		if(record.getOwnerName().equals(mob.Name()))
			return true;
		if((record.getOwnerName().equals(mob.getLiegeID())&&(mob.isMarriedToLiege())))
			return true;
		final Pair<Clan,Integer> clanRole=mob.getClanRole(record.getOwnerName());
		if((clanRole!=null)&&(clanRole.first.getAuthority(clanRole.second.intValue(),Clan.Function.PROPERTY_OWNER)!=Clan.Authority.CAN_NOT_DO))
			return true;
		if(mob.amFollowing()!=null)
			return doesOwnThisProperty(mob.amFollowing(),record);
		return false;
	}

	@Override
	public MOB getPropertyOwner(PrivateProperty record)
	{
		if(record.getOwnerName()==null)
			return null;
		if(record.getOwnerName().length()==0)
			return null;
		final Clan clan = CMLib.clans().getClan(record.getOwnerName());
		if(clan != null)
			return clan.getResponsibleMember();
		return CMLib.players().getLoadPlayer(record.getOwnerName());
	}
	
	@Override
	public boolean canAttackThisProperty(MOB mob, PrivateProperty record)
	{
		if(record.getOwnerName()==null)
			return true;
		if(record.getOwnerName().length()==0)
			return true;
		if(record.getOwnerName().equals(mob.Name()))
			return true;
		if((record.getOwnerName().equals(mob.getLiegeID())&&(mob.isMarriedToLiege())))
			return true;
		final Clan clan = CMLib.clans().getClan(record.getOwnerName());
		if(clan != null)
		{
			final Pair<Clan,Integer> clanRole=mob.getClanRole(record.getOwnerName());
			if((clanRole!=null)&&(clanRole.first.getAuthority(clanRole.second.intValue(),Clan.Function.PROPERTY_OWNER)!=Clan.Authority.CAN_NOT_DO))
				return true;
			final MOB M=clan.getResponsibleMember();
			if(M!=null)
				return mob.mayIFight(M);
		}
		else
		{
			final MOB M=CMLib.players().getLoadPlayer(record.getOwnerName());
			if(M!=null)
				return mob.mayIFight(M);
		}
		return false;
	}

	@Override
	public Ability getClericInfusion(Physical room)
	{
		if(room==null)
			return null;
		for(final Enumeration<Ability> a=room.effects();a.hasMoreElements();)
		{
			final Ability A=a.nextElement();
			if((A!=null)&&(A.ID().startsWith("Prayer_Infuse")))
				return A;
		}
		return null;
	}

	@Override
	public Deity getClericInfused(Room room)
	{
		final Ability A=getClericInfusion(room);
		if(A==null)
			return null;
		return CMLib.map().getDeity(A.text());
	}

	@Override
	public boolean doesOwnThisLand(MOB mob, Room room)
	{
		final LandTitle title=getLandTitle(room);
		if(title==null)
			return false;
		if(title.getOwnerName()==null)
			return false;
		if(title.getOwnerName().length()==0)
			return false;
		if(title.getOwnerName().equals(mob.Name()))
			return true;
		if((title.getOwnerName().equals(mob.getLiegeID())&&(mob.isMarriedToLiege())))
			return true;
		final Pair<Clan,Integer> clanRole=mob.getClanRole(title.getOwnerName());
		if((clanRole!=null)&&(clanRole.first.getAuthority(clanRole.second.intValue(),Clan.Function.PROPERTY_OWNER)!=Clan.Authority.CAN_NOT_DO))
			return true;
		if(mob.amFollowing()!=null)
			return doesOwnThisLand(mob.amFollowing(),room);
		return false;
	}

	@Override
	public boolean isLegalOfficerHere(MOB mob)
	{
		if((mob==null)||(mob.location()==null))
			return false;
		final Area A=this.getLegalObject(mob.location());
		if(A==null)
			return false;
		final LegalBehavior B=this.getLegalBehavior(A);
		if(B==null)
			return false;
		return B.isAnyOfficer(A, mob);
	}

	@Override
	public boolean isLegalJudgeHere(MOB mob)
	{
		if((mob==null)||(mob.location()==null))
			return false;
		final Area A=this.getLegalObject(mob.location());
		if(A==null)
			return false;
		final LegalBehavior B=this.getLegalBehavior(A);
		if(B==null)
			return false;
		return B.isJudge(A, mob);
	}

	@Override
	public boolean isLegalOfficialHere(MOB mob)
	{
		return isLegalOfficerHere(mob) || isLegalJudgeHere(mob);
	}
	

	@Override
	public void colorRoomForSale(Room R, boolean rental, boolean reset)
	{
		synchronized(("SYNC"+R.roomID()).intern())
		{
			R=CMLib.map().getRoom(R);
			final String theStr=CMLib.lang().L(rental?RENTSTR:SALESTR);
			final String otherStr=CMLib.lang().L(rental?SALESTR:RENTSTR);
			int x=R.description().indexOf(otherStr);
			while(x>=0)
			{
				R.setDescription(R.description().substring(0,x));
				CMLib.database().DBUpdateRoom(R);
				x=R.description().indexOf(otherStr);
			}
			final String oldDescription=R.description();
			x=R.description().indexOf(theStr.trim());
			final String displayStr =  CMLib.lang().L(CMath.bset(R.domainType(), Room.INDOORS)?INDOORSTR:OUTDOORSTR);
			if((x<0)||(reset&&(!R.displayText().equals(displayStr))))
			{
				if(reset)
				{
					R.setDescription("");
					R.setDisplayText(displayStr);
					x=-1;
				}
				if(x<0)
					R.setDescription(R.description()+theStr);
				else
				if(!reset)
					R.setDescription(R.description().substring(0,x+theStr.trim().length()));
				if(!R.description().equals(oldDescription))
					CMLib.database().DBUpdateRoom(R);
			}
			else
			{
				R.setDescription(R.description().substring(0,x+theStr.trim().length()));
				if(!R.description().equals(oldDescription))
					CMLib.database().DBUpdateRoom(R);
			}
			Item I=R.findItem(null,"$id$");
			if((I==null)||(!I.ID().equals("GenWallpaper")))
			{
				I=CMClass.getItem("GenWallpaper");
				CMLib.flags().setReadable(I,true);
				I.setName(("id"));
				I.setReadableText(CMLib.lang().L("This room is "+CMLib.map().getExtendedRoomID(R)));
				I.setDescription(CMLib.lang().L("This room is @x1",CMLib.map().getExtendedRoomID(R)));
				R.addItem(I);
				CMLib.database().DBUpdateItems(R);
			}
		}
	}
}