package codechicken.wirelessredstone.core;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.Map.Entry;

import codechicken.core.CommonUtils;
import codechicken.core.ServerUtils;
import codechicken.lib.vec.BlockCoord;
import codechicken.lib.vec.Vector3;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;

public class RedstoneEtherServer extends RedstoneEther
{
    public RedstoneEtherServer()
    {
        super(false);
    }
    
    public void init(World world)
    {
        super.init(world);
        
        SaveManager.resetWorld();
        SaveManager.loadFreqInfo();
        SaveManager.loadDimensionHash();    
        
        publicfrequencyend = SaveManager.generalProp.getProperty("PublicFrequencies", 1000);
        sharedfrequencyend = SaveManager.generalProp.getProperty("SharedFrequencies", 5000);
        numprivatefreqs = SaveManager.generalProp.getProperty("PrivateFrequencies", 50);
    }
    
    @Override
    protected void addEther(World world, int dimension)
    {
        if(ethers.get(dimension) != null)
            return;
        
        super.addEther(world, dimension);
        
        SaveManager.reloadSave(world);
        SaveManager.getInstance(dimension).loadEther();
    }

    public void remEther(World world, int dimension)
    {
        if(ethers.get(dimension) == null)
            return;
        
        super.remEther(world, dimension);
        
        SaveManager.unloadSave(dimension);
    }
    
    public void saveEther(World world)
    {
        int dimension = CommonUtils.getDimension(world);
        if(!ethers.containsKey(dimension))
            return;
        
        for(RedstoneEtherFrequency freq : ethers.get(dimension).freqsToSave)
            freq.saveFreq(dimension);
        
        ethers.get(dimension).freqsToSave.clear();
        SaveManager.getInstance(dimension).removeTrailingSectors();
        SaveManager.saveDimensionHash();
    }
    
    public void verifyChunkTransmitters(World world, int chunkx, int chunkz)
    {
        int dimension = CommonUtils.getDimension(world);
        DimensionalEtherHash ether = ethers.get(dimension);
        int blockxmin = chunkx * 16;
        int blockxmax = blockxmin + 15;
        int blockzmin = chunkz * 16;
        int blockzmax = blockzmin + 15;
        
        ArrayList<BlockCoord> transmittingblocks = new ArrayList<BlockCoord>(ether.transmittingblocks.keySet());
        
        for(BlockCoord node : transmittingblocks)
        {            
            if(node.x >= blockxmin && node.x <= blockxmax && node.z >= blockzmin && node.z <= blockzmax)
            {
                TileEntity tile = RedstoneEther.getTile(world, node);
                int freq = ether.transmittingblocks.get(node).freq;
                if(tile == null || !(tile instanceof ITileWireless) || ((ITileWireless)tile).getFreq() != freq)
                {
                    remTransmitter(world, node.x, node.y, node.z, freq);
                    System.out.println("Removed Badly Synced node at:"+node.x+","+node.y+","+node.z+" on "+freq+" in dim"+dimension);
                }
            }
        }
    }    
    
    public void setTransmitter(World world, int x, int y, int z, int freq, boolean on)
    {
        if(freq == 0)
        {
            return;
        }
        
        BlockCoord node = new BlockCoord(x, y, z);
        int dimension = CommonUtils.getDimension(world);
        
        if(isNodeInAOEofJammer(node, dimension))
        {
            jamNodeSometime(world, node, dimension, freq);
        }
        TXNodeInfo info = ethers.get(dimension).transmittingblocks.get(node);
        if(info == null)
            ethers.get(dimension).transmittingblocks.put(node, new TXNodeInfo(freq, on));
        else
            info.on = on;
        freqarray[freq].setTransmitter(world, node, dimension, on);
    }

    public void remTransmitter(World world, int x, int y, int z, int freq)
    {
        if(freq == 0)
        {
            return;
        }

        int dimension = CommonUtils.getDimension(world);
        BlockCoord node = new BlockCoord(x, y, z);
        
        ethers.get(dimension).jammednodes.remove(node);
        ethers.get(dimension).transmittingblocks.remove(node);
        freqarray[freq].remTransmitter(world, node, dimension);
    }

    public void addReceiver(World world, int x, int y, int z, int freq)
    {
        if(freq == 0)
            return;
        
        BlockCoord node = new BlockCoord(x, y, z);
        int dimension = CommonUtils.getDimension(world);
        
        if(isNodeInAOEofJammer(node, dimension))
        {
            jamNodeSometime(world, node, dimension, freq);
        }
        ethers.get(dimension).recievingblocks.put(node, freq);
        freqarray[freq].addReceiver(world, node, dimension);
    }

    public void remReceiver(World world, int x, int y, int z, int freq)
    {
        if(freq == 0)
            return;

        int dimension = CommonUtils.getDimension(world);
        BlockCoord node = new BlockCoord(x, y, z);

        ethers.get(dimension).jammednodes.remove(node);    
        ethers.get(dimension).recievingblocks.remove(node);
        freqarray[freq].remReceiver(world, node, dimension);
    }
    
    public void addJammer(World world, int x, int y, int z)
    {
        int dimension = CommonUtils.getDimension(world);
        BlockCoord jammer = new BlockCoord(x, y, z);
        
        ethers.get(dimension).jammerset.add(jammer);
        jamNodesInAOEOfJammer(world, jammer, dimension);        
    }

    public void remJammer(World world, int x, int y, int z)
    {
        ethers.get(CommonUtils.getDimension(world)).jammerset.remove(new BlockCoord(x, y, z));
    }

    public boolean isNodeJammed(World world, int x, int y, int z)
    {
        Integer timeout = ethers.get(CommonUtils.getDimension(world)).jammednodes.get(new BlockCoord(x, y, z));
        return timeout != null && timeout > 0;
    }

    public boolean isNodeInAOEofJammer(BlockCoord node, int dimension)
    {
        for(Iterator<BlockCoord> iterator = ethers.get(dimension).jammerset.iterator(); iterator.hasNext();)
        {
            BlockCoord jammer = iterator.next();
            if(pythagorasPow2(jammer, node) < jammerrangePow2)
            {
                return true;
            }
        }
        return false;
    }

    public boolean isPointInAOEofJammer(Vector3 point, int dimension)
    {
        for(Iterator<BlockCoord> iterator = ethers.get(dimension).jammerset.iterator(); iterator.hasNext();)
        {
            BlockCoord jammer = iterator.next();
            if(pythagorasPow2(jammer, point) < jammerrangePow2)
            {
                return true;
            }
        }
        return false;
    }

    public BlockCoord getClosestJammer(BlockCoord node, int dimension)
    {
        BlockCoord closestjammer = null;
        double closestdist = jammerrangePow2;
        for(Iterator<BlockCoord> iterator = ethers.get(dimension).jammerset.iterator(); iterator.hasNext();)
        {
            BlockCoord jammer = iterator.next();
            double distance = pythagorasPow2(jammer, node);
            if(distance < closestdist)
            {
                closestjammer = jammer;
                closestdist = distance;
            }
        }
        return closestjammer;
    }

    public BlockCoord getClosestJammer(Vector3 point, int dimension)
    {
        BlockCoord closestjammer = null;
        double closestdist = jammerrangePow2;
        for(Iterator<BlockCoord> iterator = ethers.get(dimension).jammerset.iterator(); iterator.hasNext();)
        {
            BlockCoord jammer = iterator.next();
            double distance = pythagorasPow2(jammer, point);
            if(distance < closestdist)
            {
                closestjammer = jammer;
                closestdist = distance;
            }
        }
        return closestjammer;
    }

    public void jamNodeSometime(World world, BlockCoord node, int dimension, int freq)
    {
        ethers.get(dimension).jammednodes.put(node, -world.rand.nextInt(jammerblockwait));
    }

    public void jamEntitySometime(EntityLivingBase entity)
    {
        jammedentities.put(entity, -entity.worldObj.rand.nextInt(jammerentitywait));
    }

    public void jamNode(World world, BlockCoord node, int dimension, int freq)
    {        
        ethers.get(dimension).jammednodes.put(node, getRandomTimeout(world.rand));
    
        freqarray[freq].remTransmitter(world, node, dimension);
        freqarray[freq].remReceiver(world, node, dimension);
    }
    
    public void jamNode(World world, int x, int y, int z, int freq)
    {
        if(freq == 0)
            return;
        
        jamNode(world, new BlockCoord(x, y, z), CommonUtils.getDimension(world), freq);
    }
    
    @Override
    public void jamEntity(EntityLivingBase entity, boolean jam)
    {
        if(jam)//iterator.remove will be used to unjam entities. We only need to send the packet.
        {
            jammedentities.put(entity, getRandomTimeout(entity.worldObj.rand));
        }
        if(entity instanceof EntityPlayer)
        {
            WRCoreSPH.sendJamPlayerPacketTo((EntityPlayer) entity, jam);
        }
    }
    
    public void jamNodesInAOEOfJammer(World world, BlockCoord jammer, int dimension)
    {
        for(int freq = 1; freq <= numfreqs; freq++)
        {
            TreeMap<BlockCoord, Boolean> transmittermap = freqarray[freq].getTransmitters(dimension);
            for(Iterator<BlockCoord> iterator = transmittermap.keySet().iterator(); iterator.hasNext();)
            {
                BlockCoord node = iterator.next();
                if(pythagorasPow2(node, jammer) < jammerrangePow2)
                {
                    jamNodeSometime(world, node, dimension, freq);
                }
            }
            
            TreeSet<BlockCoord> receiverset = freqarray[freq].getReceivers(dimension);
            for(Iterator<BlockCoord> iterator = receiverset.iterator(); iterator.hasNext();)
            {
                BlockCoord node = iterator.next();
                if(pythagorasPow2(node, jammer) < jammerrangePow2)
                {
                    jamNodeSometime(world, node, dimension, freq);
                }
            }
        }
    }

    public void unjamTile(World world, int x, int y, int z)
    {
        BlockCoord node = new BlockCoord(x, y, z);
        int dimension = CommonUtils.getDimension(world);
        
        Integer timeout = ethers.get(dimension).jammednodes.remove(node);
        
        if(timeout != null && timeout >= 0)//tile was jammed
        {
            ITileWireless tile = (ITileWireless) getTile(world, node);
            tile.unjamTile();
        }
    }
    
    public void saveJammedFrequencies(String username)
    {
        username = username.toLowerCase();
        String jammedfreqs = getJammedFrequencies(username);
        
        if(jammedfreqs.equals(""+(sharedfrequencyend+1)+"-"+numfreqs))
            SaveManager.generalProp.removeProperty(username+".jammedFreqs");
        else
            SaveManager.generalProp.setProperty(username+".jammedFreqs", jammedfreqs);
    }

    public void loadJammedFrequencies(String jammedString, String username)
    {
        String freqranges[] = jammedString.split(",");
        for(int i = 0; i < freqranges.length; i++)
        {
            String currentrange[] = freqranges[i].split("-");
            int startfreq;
            int endfreq;
            if(currentrange.length == 1)
            {
                try {
                    startfreq = endfreq = Integer.parseInt(currentrange[0]);
                } catch(NumberFormatException numberformatexception)
                {continue;}
            }
            else
            {
                try {
                    startfreq = Integer.parseInt(currentrange[0]);
                    endfreq = Integer.parseInt(currentrange[1]);
                } catch(NumberFormatException numberformatexception1)
                {continue;}
            }
            
            setFrequencyRange(username, startfreq, endfreq, true);
        }
    }
    
    @Override
    protected void loadJammedFrequencies(String username)
    {
        String openstring = SaveManager.generalProp.getProperty(username+".jammedFreqs");
        if(openstring == null)
            jamDefaultRange(username);
        else
            loadJammedFrequencies(openstring, username);
    }
    
    public void setFrequencyRangeCommand(String username, int startfreq, int endfreq, boolean flag)
    {
        setFrequencyRange(username, startfreq, endfreq, flag);
        saveJammedFrequencies(username);
    }
    
    public void jamAllFrequencies(String username)
    {
        setFrequencyRange(username, 1, numfreqs, true);
    }
    
    public void jamDefaultRange(String username)
    {
        setFrequencyRange(username, 1, numfreqs, false);
        setFrequencyRange(username, sharedfrequencyend+1, numfreqs, true);
    }
    
    public void setFreqClean(int freq, int dimension)
    {
        freqarray[freq].setClean(dimension);
    }
    
    public void resetPlayer(EntityPlayer player)
    {
        WRCoreSPH.sendPublicFrequencyTo(player, publicfrequencyend);
        WRCoreSPH.sendSharedFrequencyTo(player, sharedfrequencyend);        
        
        String openstring = SaveManager.generalProp.getProperty(player.getCommandSenderName()+".jammedFreqs");
        if(openstring == null)
            jamDefaultRange(player.getCommandSenderName());
        else
            loadJammedFrequencies(openstring, player.getCommandSenderName());
        
        sendFreqInfoTo(player);
        sendPrivateFreqsTo(player);
    }
    
    public void removePlayer(EntityPlayer player)
    {
        playerJammedMap.remove(player.getCommandSenderName());
    }
    
    private void sendFreqInfoTo(EntityPlayer player)
    {        
        ArrayList<Integer> freqsWithInfo = new ArrayList<Integer>();
        for(int freq = 1; freq <= numfreqs; freq++)
        {
            if(!freqarray[freq].getName().equals("") || freqarray[freq].getColourId() != -1)
                freqsWithInfo.add(freq);
        }
        
        WRCoreSPH.sendFreqInfoTo(player, freqsWithInfo);
    }
    
    private void sendPrivateFreqsTo(EntityPlayer player)
    {
        ArrayList<Integer> freqsWithOwners = new ArrayList<Integer>();
        for(int freq = 1; freq <= numfreqs; freq++)
        {
            if(isFreqPrivate(freq))
                freqsWithOwners.add(freq);
        }
        
        WRCoreSPH.sendFreqOwnerTo(player, freqsWithOwners);
    }

    public TreeMap<Integer, Integer> getLoadedFrequencies()
    {
        TreeMap<Integer, Integer> treemap = new TreeMap<Integer, Integer>();
        for(int freq = 1; freq <= numfreqs; freq++)
        {
            if(freqarray[freq].nodeCount() != 0)
            {
                treemap.put(freq, freqarray[freq].getActiveTransmitters());
            }
        }
    
        return treemap;
    }

    public Map<BlockCoord, Boolean> getTransmittersOnFreq(int freq, int dimension)
    {
        return Collections.unmodifiableMap(freqarray[freq].getTransmitters(dimension));
    }

    public Collection<BlockCoord> getReceiversOnFreq(int freq, int dimension)
    {
        return Collections.unmodifiableCollection(freqarray[freq].getReceivers(dimension));
    }

    public Map<BlockCoord, TXNodeInfo> getTransmittersInDimension(int dimension)
    {
        return Collections.unmodifiableMap(ethers.get(dimension).transmittingblocks);
    }
    
    public Set<WirelessTransmittingDevice> getTransmittingDevicesInDimension(int dimension)
    {
        return Collections.unmodifiableSet(ethers.get(dimension).transmittingdevices);
    }

    public ArrayList<FreqCoord> getActiveTransmittersOnFreq(int freq, int dimension)
    {
        ArrayList<FreqCoord> txnodes = new ArrayList<FreqCoord>();
        freqarray[freq].putActiveTransmittersInList(dimension, txnodes);
        return txnodes;
    }
    
    public TreeSet<BlockCoord> getJammers(int dimension)
    {
        return ethers.get(dimension).jammerset;
    }

    public TreeMap<BlockCoord, Integer> getJammedNodes(int dimension)
    {
        return ethers.get(dimension).jammednodes;
    }
    
    public TreeSet<BlockCoord> getNodesInRangeofPoint(int dimension, Vector3 point, float range, boolean includejammed)
    {
        TreeSet<BlockCoord> nodes = new TreeSet<BlockCoord>();
        float rangePow2 = range*range;
        for(int freq = 1; freq <= numfreqs; freq++)
        {
            TreeMap<BlockCoord, Boolean> transmittermap = freqarray[freq].getTransmitters(dimension);
            for(Iterator<BlockCoord> iterator = transmittermap.keySet().iterator(); iterator.hasNext();)
            {
                BlockCoord node = iterator.next();
                if(pythagorasPow2(node, point) < rangePow2)
                {
                    nodes.add(node);
                }
            }
            
            TreeSet<BlockCoord> receiverset = freqarray[freq].getReceivers(dimension);
            for(Iterator<BlockCoord> iterator = receiverset.iterator(); iterator.hasNext();)
            {
                BlockCoord node = iterator.next();
                if(pythagorasPow2(node, point) < rangePow2)
                {
                    nodes.add(node);
                }
            }
        }
        
        if(includejammed)
        {
            for(Iterator<BlockCoord> iterator = ethers.get(dimension).jammednodes.keySet().iterator(); iterator.hasNext();)
            {
                BlockCoord node = iterator.next();
                if(pythagorasPow2(node, point) < rangePow2)
                {
                    nodes.add(node);
                }
            }
        }
        
        return nodes;
    }
    
    public TreeSet<BlockCoord> getNodesInRangeofNode(int dimension, BlockCoord block, float range, boolean includejammed)
    {
        TreeSet<BlockCoord> nodes = new TreeSet<BlockCoord>();
        float rangePow2 = range*range;
        for(int freq = 1; freq <= numfreqs; freq++)
        {
            TreeMap<BlockCoord, Boolean> transmittermap = freqarray[freq].getTransmitters(dimension);
            for(Iterator<BlockCoord> iterator = transmittermap.keySet().iterator(); iterator.hasNext();)
            {
                BlockCoord node = iterator.next();
                if(pythagorasPow2(node, block) < rangePow2)
                {
                    nodes.add(node);
                }
            }
            
            TreeSet<BlockCoord> receiverset = freqarray[freq].getReceivers(dimension);
            for(Iterator<BlockCoord> iterator = receiverset.iterator(); iterator.hasNext();)
            {
                BlockCoord node = iterator.next();
                if(pythagorasPow2(node, block) < rangePow2)
                {
                    nodes.add(node);
                }
            }
        }

        if(includejammed)
        {
            for(Iterator<BlockCoord> iterator = ethers.get(dimension).jammednodes.keySet().iterator(); iterator.hasNext();)
            {
                BlockCoord node = iterator.next();
                if(pythagorasPow2(node, block) < rangePow2)
                {
                    nodes.add(node);
                }
            }
        }
        
        return nodes;
    }
    
    public void updateReceivingDevices(int freq, boolean on)
    {
        for(Iterator<WirelessReceivingDevice> iterator = receivingdevices.iterator(); iterator.hasNext();)
        {
            iterator.next().updateDevice(freq, on);
        }
    }

    public List<WirelessTransmittingDevice> getTransmittingDevicesOnFreq(int freq)
    {
        return Collections.unmodifiableList(freqarray[freq].getTransmittingDevices());
    }
    
    public void addTransmittingDevice(WirelessTransmittingDevice device)
    {
        ethers.get(device.getDimension()).transmittingdevices.add(device);
        freqarray[device.getFreq()].addTransmittingDevice(device);
    }
    
    public void removeTransmittingDevice(WirelessTransmittingDevice device)
    {
        ethers.get(device.getDimension()).transmittingdevices.remove(device);
        freqarray[device.getFreq()].removeTransmittingDevice(device);
    }

    public void addReceivingDevice(WirelessReceivingDevice device)
    {
        receivingdevices.add(device);
    }
    
    public void removeReceivingDevice(WirelessReceivingDevice device)
    {
        receivingdevices.remove(device);
    }

    public void setDimensionTransmitterCount(int freq, int dimension, int count)
    {
        freqarray[freq].setActiveTransmittersInDim(dimension, count);
    }
    
    public void addFreqToSave(RedstoneEtherFrequency freq, int dimension)
    {
        ethers.get(dimension).freqsToSave.add(freq);
    }
    
    public void tick(World world)
    {
        updateJammedNodes(world);
        randomJamTest(world);
        updateJammedEntities(world);
        entityJamTest(world);
        unloadJammedMap();
    }
    
    private void unloadJammedMap()
    {
        for(Iterator<String> iterator = playerJammedMap.keySet().iterator(); iterator.hasNext();)
        {
            String username = iterator.next();
            if(ServerUtils.getPlayer(username) == null)
            {
                saveJammedFrequencies(username);
                iterator.remove();
            }
        }
    }
    
    private void updateJammedNodes(World world)
    {
        int dimension = CommonUtils.getDimension(world);
        for(Iterator<BlockCoord> iterator = ethers.get(dimension).jammednodes.keySet().iterator(); iterator.hasNext();)
        {
            BlockCoord node = iterator.next();
            int inactivetime = ethers.get(dimension).jammednodes.get(node);
            inactivetime--;
            
            if(inactivetime == 0 || inactivetime < 0 && inactivetime%jammerrandom == 0)
            {
                ITileWireless tile = (ITileWireless) getTile(world, node);
                if(tile == null)
                {
                    iterator.remove();
                    continue;
                }
                
                BlockCoord jammer = getClosestJammer(node, dimension);
                ITileJammer jammertile = jammer == null ? null : (ITileJammer)getTile(world, jammer);
                if(jammertile == null)
                {
                    iterator.remove();
                    tile.unjamTile();
                    continue;
                }
                jammertile.jamTile(tile);
            }
            
            if(inactivetime == 0)//so the node doesn't think it's unjammed
                inactivetime = jammertimeout;
            
            ethers.get(dimension).jammednodes.put(node, inactivetime);
        }
    }

    private void randomJamTest(World world)
    {
        if(world.getTotalWorldTime() % 600 != 0)//30 seconds
            return;
        
        for(Entry<Integer, DimensionalEtherHash> entry : ethers.entrySet())
            if(entry.getValue().jammerset != null)
                for(Iterator<BlockCoord> iterator = entry.getValue().jammerset.iterator(); iterator.hasNext();)
                    jamNodesInAOEOfJammer(world, iterator.next(), entry.getKey());
    }

    private void updateJammedEntities(World world)
    {
        int dimension = CommonUtils.getDimension(world);
        for(Iterator<EntityLivingBase> iterator = jammedentities.keySet().iterator(); iterator.hasNext();)
        {
            EntityLivingBase entity = iterator.next();
            int inactivetime = jammedentities.get(entity);
            inactivetime--;
            
            if(entity == null || entity.isDead)//logged out or killed
            {
                iterator.remove();
                continue;
            }
            
            if(inactivetime == 0//time for unjam or rejam
                    || (inactivetime < 0 && inactivetime%jammerentitywait == 0)//time to jam from the sometime
                    || (inactivetime > 0 && inactivetime%jammerentityretry == 0))//send another bolt after the retry time
            {
                BlockCoord jammer = getClosestJammer(Vector3.fromEntity(entity), dimension);
                ITileJammer jammertile = jammer == null ? null : (ITileJammer)getTile(world, jammer);
                if(jammertile == null)
                {
                    if(inactivetime <= 0)//not a rejam test
                    {
                        iterator.remove();
                        jamEntity(entity, false);
                        continue;
                    }
                }
                else
                {
                    jammertile.jamEntity(entity);
                }
            }
            
            if(inactivetime == 0)//so the node doesn't think it's unjammed
            {
                inactivetime = jammertimeout;
            }
            
            jammedentities.put(entity, inactivetime);
        }
    }

    private void entityJamTest(World world)
    {
        if(world.getTotalWorldTime() % 10 != 0)
            return;
        
        int dimension = CommonUtils.getDimension(world);
        for(Iterator<BlockCoord> iterator = ethers.get(dimension).jammerset.iterator(); iterator.hasNext();)
        {
            BlockCoord jammer = iterator.next();
            List<Entity> entitiesinrange = world.getEntitiesWithinAABBExcludingEntity(null, AxisAlignedBB.getBoundingBox(jammer.x-9.5, jammer.y-9.5, jammer.z-9.5, jammer.x+10.5, jammer.y+10.5, jammer.z+10.5));
            for(Iterator<Entity> iterator2 = entitiesinrange.iterator(); iterator2.hasNext();)
            {
                Entity entity = iterator2.next();
                if(!(entity instanceof EntityLivingBase))
                    continue;
                
                if(entity instanceof EntityPlayer)
                    if(isPlayerJammed((EntityPlayer)entity))
                        continue;
                
                jamEntitySometime((EntityLivingBase) entity);
            }
        }
    }

    public void unload()
    {
        SaveManager.unloadAll();
    }
    
    @Override
    public void setFreq(ITileWireless tile, int freq)
    {
        tile.setFreq(freq);
    }
}