package com.planet_ink.coffee_mud.Common;
import com.planet_ink.coffee_mud.core.interfaces.*;
import com.planet_ink.coffee_mud.core.*;
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.util.*;

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

public class DefaultRoomnumberSet implements RoomnumberSet
{
    public DVector root=new DVector(2);
    public String ID(){return "DefaultRoomnumberSet";}
    public int compareTo(Object o){ return CMClass.classID(this).compareToIgnoreCase(CMClass.classID(o));}
    public CMObject newInstance(){try{return (CMObject)getClass().newInstance();}catch(Exception e){return new DefaultRoomnumberSet();}}
    public void initializeClass(){}
    public CMObject copyOf()
    {
        DefaultRoomnumberSet R=new DefaultRoomnumberSet();
        R.root=new DVector(2);
        CMIntegerGrouper CI=null;
        for(int r=0;r<root.size();r++)
        {
        	CI=((CMIntegerGrouper)root.elementAt(r,2));
            R.root.addElement(root.elementAt(r,1),CI==null?null:CI.copyOf());
        }
        return R;
    }
    public void add(RoomnumberSet set)
    {
    	Vector V=set.getAreaNames();
    	CMIntegerGrouper his=null;
    	CMIntegerGrouper mine=null;
    	String arName=null;
    	for(int v=0;v<V.size();v++)
    	{
    		arName=(String)V.elementAt(v);
    		his=set.getGrouper(arName);
    		mine=set.getGrouper(arName);
    		if(mine==null)
    		{
    			if(his!=null)
	    			mine=(CMIntegerGrouper)his.copyOf();
    			root.addElement(arName.toUpperCase(),mine);
    		}
    		else
    			mine.add(his);
    	}
    }
    public void remove(String str)
    {
        String areaName=str.toUpperCase().trim();
        if(areaName.length()==0) return;
        
        String theRest=null;
        long roomNum=-1;
        int x=areaName.indexOf("#");
        CMIntegerGrouper CI=null;
        if(x<=0)
        	CI=getGrouper(areaName);
        else
        if(x>0)
        {
            theRest=areaName.substring(x+1).trim();
            areaName=areaName.substring(0,x);
        	CI=getGrouper(areaName);
        	if(CI==null) return;
            x=theRest.indexOf("#(");
            if((x>=0)&&(theRest.endsWith(")"))&&(CMath.isInteger(theRest.substring(0,x))))
            {
                int comma=theRest.indexOf(",",x);
                if(comma>0)
                {
                    roomNum=(Long.parseLong(theRest.substring(0,x))<<30);
                    roomNum+=(Long.parseLong(theRest.substring(x+2,comma))<<15);
                    roomNum+=Long.parseLong(theRest.substring(comma+1,theRest.length()-1));
                    if(roomNum<CMIntegerGrouper.NEXT_BITS) roomNum|=CMIntegerGrouper.GRID_FLAGL;
                }
            }
            else
            if(CMath.isInteger(theRest))
                roomNum=Integer.parseInt(theRest.substring(x+1).trim());
        }
        if(CI==null) return;
        CI.remove(roomNum);
        if(CI.roomCount()==0)
        	root.removeElement(areaName.toUpperCase());
    }
    public void remove(RoomnumberSet set)
    {
    	
    }
    
    public int roomCountAllAreas()
    {
    	int total=0;
    	CMIntegerGrouper CMI=null;
    	for(int i=0;i<root.size();i++)
    	{
            CMI=(CMIntegerGrouper)root.elementAt(i,2);
            if(CMI==null) 
            	total++;
            else
	            total+=CMI.roomCount();
    	}
    	return total;
    }
    
    public int roomCount(String prefix)
    {
    	prefix=prefix.toUpperCase();
        int x=prefix.indexOf("#");
        if(x>0)
        	prefix=prefix.substring(0,x);
        int start=0;
        int end=root.size()-1;
        int comp=-1;
        int mid=-1;
        while(start<=end)
        {
            mid=(end+start)/2;
            comp=prefix.compareTo((String)root.elementAt(mid,1));
            if(comp==0)
            {
                if(root.elementAt(mid,2)!=null)
                {
                	CMIntegerGrouper CMI=(CMIntegerGrouper)root.elementAt(mid,2);
                	if(CMI==null) return 1;
                    return CMI.roomCount();
                }
                return 0;
            }
            else
            if(comp<0)
                end=mid-1;
            else
                start=mid+1;
        }
        return 0;
    }
    
    public String random()
    {
    	int total=0;
    	CMIntegerGrouper CMI=null;
    	for(int i=0;i<root.size();i++)
    	{
            CMI=(CMIntegerGrouper)root.elementAt(i,2);
            if(CMI==null)
            	total++;
            else
	            total+=CMI.roomCount();
    	}
    	if(total<=0) return null;
    	int which=CMLib.dice().roll(1,total,-1);
    	total=0;
        String roomID=null;
    	for(int i=0;i<root.size();i++)
    	{
            CMI=(CMIntegerGrouper)root.elementAt(i,2);
            if(CMI==null)
            	total++;
            else
	    		total+=CMI.roomCount();
    		if(which<total)
    		{
    			roomID=(String)root.elementAt(i,1);
    			break;
    		}
    	}
    	if(roomID==null) return null;
    	if(CMI==null)
    	{
    		//Log.errOut("RNUMS","Unable to even select an integer group! Picked "+which+"/"+grandTotal);
    		return roomID;
    	}
		long selection=CMI.random();
		return convertRoomID(roomID,selection);
    }
    
    public int[] convertRoomID(long coded)
    {
		if(coded==-1) return null;
		int[] ids=new int[3];
		ids[1]=-1;
		ids[2]=-1;
		if(coded<=CMIntegerGrouper.NEXT_BITS)
		{
			ids[0]=(int)coded;
			return ids;
		}
		long mask=0;
		for(int i=0;i<15;i++) mask=(mask<<1)+1;
		ids[2]=(int)(coded&mask);
		long mask2=mask<<15;
		ids[1]=(int)((coded&mask2)>>15);
		mask|=mask2;
		mask=mask<<30;
		ids[0]=(int)(((coded&mask)>>30)&(CMIntegerGrouper.NEXT_BITSL-CMIntegerGrouper.GRID_FLAGL));
		return ids;
    }
    public String convertRoomID(String prefix, long coded)
    {
		if(coded==-1) return prefix;
		if(coded<CMIntegerGrouper.NEXT_BITS)
			return prefix+"#"+coded;
		long mask=0;
		for(int i=0;i<15;i++) mask=(mask<<1)+1;
		long thirdID=coded&mask;
		long mask2=mask<<15;
		long secondID=(coded&mask2)>>15;
		mask|=mask2;
		mask=mask<<30;
		long firstID=(((coded&mask)>>30)&(CMIntegerGrouper.NEXT_BITSL-CMIntegerGrouper.GRID_FLAGL));
		return prefix+"#"+firstID+"#("+secondID+","+thirdID+")";
    }
    public Vector getAreaNames(){ return (Vector)root.getDimensionVector(1).clone();}
    
    private boolean isGrouper(String areaName)
    {
    	areaName=areaName.toUpperCase();
        int start=0;
        int end=root.size()-1;
        int comp=-1;
        int mid=-1;
        while(start<=end)
        {
            mid=(end+start)/2;
            comp=areaName.compareTo((String)root.elementAt(mid,1));
            if(comp==0) return true;
            else
            if(comp<0)
                end=mid-1;
            else
                start=mid+1;
        }
        return false;
    }
    
    public CMIntegerGrouper getGrouper(String areaName)
    {
    	areaName=areaName.toUpperCase();
        int start=0;
        int end=root.size()-1;
        int comp=-1;
        int mid=-1;
        while(start<=end)
        {
            mid=(end+start)/2;
            comp=areaName.compareTo((String)root.elementAt(mid,1));
            if(comp==0)
            {
                if(root.elementAt(mid,2)!=null)
                    return ((CMIntegerGrouper)root.elementAt(mid,2));
                return null;
            }
            else
            if(comp<0)
                end=mid-1;
            else
                start=mid+1;
        }
        return null;
    }
    
    public boolean contains(String str)
    {
    	if(str==null) return false;
        String theRest=null;
        long roomNum=-1;
        int origX=str.indexOf("#");
        int x=origX;
        if(x>0)
        {
            theRest=str.substring(x+1).trim();
            str=str.substring(0,x);
            x=theRest.indexOf("#(");
            if((x>=0)&&(theRest.endsWith(")"))&&(CMath.isInteger(theRest.substring(0,x))))
            {
                int comma=theRest.indexOf(",",x);
                if(comma>0)
                {
                    roomNum=Long.parseLong(theRest.substring(0,x))<<30;
                    roomNum+=(Long.parseLong(theRest.substring(x+2,comma))<<15);
                    roomNum+=Long.parseLong(theRest.substring(comma+1,theRest.length()-1));
                    if(roomNum<CMIntegerGrouper.NEXT_BITS) roomNum|=CMIntegerGrouper.GRID_FLAGL;
                }
            }
            else
            if(CMath.isInteger(theRest))
                roomNum=Integer.parseInt(theRest.substring(x+1).trim());
        }
        
        CMIntegerGrouper myGrouper=getGrouper(str);
        if((origX<0)&&(myGrouper==null)&&(isGrouper(str)))
        	return true;
        if(myGrouper==null) return false;
        return myGrouper.contains(roomNum);
    }
    
    public String xml()
    {
        StringBuffer str=new StringBuffer("<AREAS>");
        for(int i=0;i<root.size();i++)
        {
            str.append("<AREA><ID>"+(String)root.elementAt(i,1)+"</ID>");
            if(root.elementAt(i,2)!=null)
                str.append("<NUMS>"+((CMIntegerGrouper)root.elementAt(i,2)).text()+"</NUMS>");
            str.append("</AREA>");
        }
        return str.toString()+"</AREAS>";
    }
    public void parseXML(String xml)
    {
        Vector V=CMLib.xml().parseAllXML(xml);
        if((V==null)||(V.size()==0)) return;
        Vector xV=CMLib.xml().getRealContentsFromPieces(V,"AREAS");
        root.clear();
        String ID=null;
        String NUMS=null;
        if((xV!=null)&&(xV.size()>0))
            for(int x=0;x<xV.size();x++)
            {
                XMLLibrary.XMLpiece ablk=(XMLLibrary.XMLpiece)xV.elementAt(x);
                if((ablk.tag.equalsIgnoreCase("AREA"))&&(ablk.contents!=null))
                {
                    ID=CMLib.xml().getValFromPieces(ablk.contents,"ID");
                    NUMS=CMLib.xml().getValFromPieces(ablk.contents,"NUMS");
                    if((NUMS!=null)&&(NUMS.length()>0))
                        root.addElement(ID,((CMIntegerGrouper)CMClass.getCommon("DefaultCMIntegerGrouper")).parseText(NUMS));
                    else
                        root.addElement(ID,null);
                }
            }
    }
    
    
    public void add(String str)
    {
        String areaName=str.toUpperCase().trim();
        if(areaName.length()==0) return;
        
        String theRest=null;
        long roomNum=-1;
        int x=areaName.indexOf("#");
        if(x>0)
        {
            theRest=areaName.substring(x+1).trim();
            areaName=areaName.substring(0,x);
            x=theRest.indexOf("#(");
            if((x>=0)&&(theRest.endsWith(")"))&&(CMath.isInteger(theRest.substring(0,x))))
            {
                int comma=theRest.indexOf(",",x);
                if(comma>0)
                {
                    roomNum=(Long.parseLong(theRest.substring(0,x))<<30);
                    roomNum+=(Long.parseLong(theRest.substring(x+2,comma))<<15);
                    roomNum+=Long.parseLong(theRest.substring(comma+1,theRest.length()-1));
                    if(roomNum<CMIntegerGrouper.NEXT_BITS) roomNum|=CMIntegerGrouper.GRID_FLAGL;
                }
            }
            else
            if(CMath.isInteger(theRest))
                roomNum=Integer.parseInt(theRest.substring(x+1).trim());
        }
        int start=0;
        int end=root.size()-1;
        int comp=-1;
        int mid=-1;
        int lastStart=0;
        int lastEnd=root.size()-1;
        while(start<=end)
        {
            mid=(end+start)/2;
            comp=areaName.compareTo((String)root.elementAt(mid,1));
            if(comp==0)
                break;
            else
            if(comp<0)
            {
                lastEnd=end;
                end=mid-1;
            }
            else
            {
                lastStart=start;
                start=mid+1;
            }
        }
        if(comp==0)
        {
            if(root.elementAt(mid,2)!=null)
                ((CMIntegerGrouper)root.elementAt(mid,2)).add(roomNum);
        }
        else
        {
            if(mid<0)
                root.addElement(areaName,((CMIntegerGrouper)CMClass.getCommon("DefaultCMIntegerGrouper")).add(roomNum));
            else
            {
                for(comp=lastStart;comp<=lastEnd;comp++)
                    if(areaName.compareTo((String)root.elementAt(comp,1))<0)
                    {
                        root.insertElementAt(comp,areaName,((CMIntegerGrouper)CMClass.getCommon("DefaultCMIntegerGrouper")).add(roomNum));
                        return;
                    }
                root.addElement(areaName,((CMIntegerGrouper)CMClass.getCommon("DefaultCMIntegerGrouper")).add(roomNum));
            }
        }
    }
    public Enumeration getRoomIDs(){return new RoomnumberSetEnumeration();}
    private class RoomnumberSetEnumeration implements Enumeration
    {
    	Vector areaNames=null;
    	String areaName=null;
    	long[] nums=null;
    	String nextID=null;
    	int n=0;
    	public RoomnumberSetEnumeration(){ areaNames=getAreaNames();}
    	public boolean hasMoreElements(){
    		if(nextID==null) getNextID();
    		return nextID!=null;
    	}
    	public Object nextElement(){
    		if(nextID==null) getNextID();
    		String next=nextID;
    		nextID=null;
    		return next;
    	}
    	private void getNextID()
    	{
    		if(nums==null)
    		{
    			nextID=null;
    			if((areaNames==null)||(areaNames.size()==0))
    				return;
    			areaName=(String)areaNames.elementAt(0);
    			areaNames.removeElementAt(0);
    			CMIntegerGrouper grp=getGrouper(areaName);
    			if(grp==null){ nextID=areaName; return;}
    			nums=grp.allRoomNums();
    			n=0;
    		}
    		if((nums==null)||(n>=nums.length))
    		{
    			nums=null;
    			getNextID();
    			return;
    		}
    		long num=nums[n++];
    		nextID=convertRoomID(areaName,num);
    	}
    }
}
