/*
* This file is part of Matter Overdrive
* Copyright (c) 2015., Simeon Radivoev, All rights reserved.
*
* Matter Overdrive is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Matter Overdrive is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Matter Overdrive. If not, see <http://www.gnu.org/licenses>.
*/
package matteroverdrive.machines.analyzer;
import cofh.api.energy.IEnergyStorage;
import cpw.mods.fml.common.gameevent.TickEvent;
import cpw.mods.fml.relauncher.Side;
import matteroverdrive.api.inventory.UpgradeTypes;
import matteroverdrive.api.matter.IMatterDatabase;
import matteroverdrive.api.network.IMatterNetworkBroadcaster;
import matteroverdrive.api.network.IMatterNetworkClient;
import matteroverdrive.api.network.IMatterNetworkDispatcher;
import matteroverdrive.api.network.MatterNetworkTaskState;
import matteroverdrive.data.BlockPos;
import matteroverdrive.data.Inventory;
import matteroverdrive.data.ItemPattern;
import matteroverdrive.data.inventory.DatabaseSlot;
import matteroverdrive.data.inventory.MatterSlot;
import matteroverdrive.handler.SoundHandler;
import matteroverdrive.items.MatterScanner;
import matteroverdrive.machines.MachineNBTCategory;
import matteroverdrive.machines.analyzer.components.MatterNetworkComponentAnalyzer;
import matteroverdrive.machines.components.ComponentMatterNetworkConfigs;
import matteroverdrive.matter_network.MatterNetworkPacket;
import matteroverdrive.matter_network.MatterNetworkPacketQueue;
import matteroverdrive.matter_network.MatterNetworkTaskQueue;
import matteroverdrive.matter_network.tasks.MatterNetworkTaskStorePattern;
import matteroverdrive.tile.MOTileEntityMachineEnergy;
import matteroverdrive.util.MatterDatabaseHelper;
import matteroverdrive.util.MatterHelper;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.inventory.ISidedInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;
import java.util.EnumSet;
import static matteroverdrive.util.MOBlockHelper.getOppositeSide;
/**
* Created by Simeon on 3/16/2015.
*/
public class TileEntityMachineMatterAnalyzer extends MOTileEntityMachineEnergy implements ISidedInventory, IMatterNetworkDispatcher<MatterNetworkTaskStorePattern>, IMatterNetworkClient, IMatterNetworkBroadcaster
{
public static final int BROADCAST_DELAY = 60;
public static final int BROADCAST_WEATING_DELAY = 2;
public static final int VALID_LOCATION_CHECK_DELAY = 200;
public static final int PROGRESS_AMOUNT_PER_ITEM = 20;
public static final int ENERGY_STORAGE = 512000;
public static final int ENERGY_TRANSFER = 512;
public static final int ANALYZE_SPEED = 800;
public static final int ENERGY_DRAIN_PER_ITEM = 64000;
public int input_slot = 0;
public int database_slot = 1;
public int analyzeTime;
private MatterNetworkTaskQueue<MatterNetworkTaskStorePattern> taskQueueSending;
private MatterNetworkComponentAnalyzer networkComponent;
private ComponentMatterNetworkConfigs componentMatterNetworkConfigs;
public TileEntityMachineMatterAnalyzer()
{
super(4);
this.energyStorage.setCapacity(ENERGY_STORAGE);
this.energyStorage.setMaxExtract(ENERGY_TRANSFER);
this.energyStorage.setMaxReceive(ENERGY_TRANSFER);
taskQueueSending = new MatterNetworkTaskQueue<>(this,1);
playerSlotsHotbar = true;
playerSlotsMain = true;
}
@Override
public void RegisterSlots(Inventory inventory)
{
input_slot = inventory.AddSlot(new MatterSlot(true));
database_slot = inventory.AddSlot(new DatabaseSlot(true));
super.RegisterSlots(inventory);
}
@Override
protected void registerComponents()
{
super.registerComponents();
componentMatterNetworkConfigs = new ComponentMatterNetworkConfigs(this);
networkComponent = new MatterNetworkComponentAnalyzer(this);
addComponent(componentMatterNetworkConfigs);
addComponent(networkComponent);
}
@Override
public void updateEntity()
{
super.updateEntity();
manageAnalyze();
}
protected void manageAnalyze()
{
if(!worldObj.isRemote)
{
if (isActive() && this.energyStorage.getEnergyStored() >= getEnergyDrainPerTick() && networkComponent.getConnection() != null)
{
energyStorage.modifyEnergyStored(-getEnergyDrainPerTick());
UpdateClientPower();
if (analyzeTime < getSpeed())
{
analyzeTime++;
} else {
analyzeItem();
analyzeTime = 0;
}
}
if (!isActive())
{
analyzeTime = 0;
}
}
}
public boolean isAnalyzing()
{
if (getRedstoneActive() && inventory.getSlot(input_slot).getItem() != null && getEnergyStored(ForgeDirection.UNKNOWN) > 0)
{
if (inventory.getSlot(database_slot).getItem() != null)
{
//get the Matterscanner destination
return MatterHelper.getMatterAmountFromItem(inventory.getStackInSlot(input_slot)) > 0 && hasConnectionToPatterns();
}
else
{
if (taskQueueSending.remaintingCapacity() > 0)
{
return true;
}
}
}
return false;
}
public boolean hasConnectionToPatterns()
{
if(MatterHelper.isMatterScanner(inventory.getStackInSlot(database_slot)))
{
//the matter scanner pattern storage
IMatterDatabase database = MatterScanner.getLink(worldObj, inventory.getStackInSlot(database_slot));
if (database != null)
{
if (database.hasItem(inventory.getStackInSlot(input_slot)))
{
ItemPattern itemPattern = database.getPattern(inventory.getStackInSlot(input_slot));
if(itemPattern != null)
{
if (itemPattern.getProgress() < MatterDatabaseHelper.MAX_ITEM_PROGRESS)
{
return true;
}
}
}
else if (MatterDatabaseHelper.getFirstFreePatternStorage(database) != null)
{
return true;
}
}
}
return false;
}
public void analyzeItem()
{
ItemStack scanner = inventory.getStackInSlot(database_slot);
ItemStack itemStack = inventory.getStackInSlot(input_slot);
IMatterDatabase database = null;
//get the database from the scanner first
if(scanner != null && MatterHelper.isMatterScanner(scanner))
{
database = MatterScanner.getLink(worldObj,scanner);
}
if(database != null)
{
if (database.addItem(itemStack,PROGRESS_AMOUNT_PER_ITEM,false,null))
{
SoundHandler.PlaySoundAt(worldObj, "scanner_success", xCoord, yCoord, zCoord);
}
else
{
//if the scanner cannot take the item for some reason
//then just queue the analyzed item as a task
MatterNetworkTaskStorePattern storePattern = new MatterNetworkTaskStorePattern(this,itemStack,PROGRESS_AMOUNT_PER_ITEM);
storePattern.setState(MatterNetworkTaskState.WAITING);
taskQueueSending.queue(storePattern);
}
}
else
{
MatterNetworkTaskStorePattern storePattern = new MatterNetworkTaskStorePattern(this,itemStack,PROGRESS_AMOUNT_PER_ITEM);
storePattern.setState(MatterNetworkTaskState.WAITING);
taskQueueSending.queue(storePattern);
}
decrStackSize(input_slot, 1);
forceClientUpdate = true;
markDirty();
}
@Override
public boolean isAffectedByUpgrade(UpgradeTypes type)
{
return type == UpgradeTypes.PowerUsage || type == UpgradeTypes.PowerStorage || type == UpgradeTypes.Fail || type == UpgradeTypes.Output || type == UpgradeTypes.Speed;
}
//region NBT
@Override
public void readCustomNBT(NBTTagCompound tagCompound, EnumSet<MachineNBTCategory> categories)
{
super.readCustomNBT(tagCompound, categories);
if (categories.contains(MachineNBTCategory.DATA)) {
analyzeTime = tagCompound.getShort("AnalyzeTime");
taskQueueSending.readFromNBT(tagCompound);
}
}
@Override
public void writeCustomNBT(NBTTagCompound tagCompound, EnumSet<MachineNBTCategory> categories, boolean toDisk)
{
super.writeCustomNBT(tagCompound, categories, toDisk);
if (categories.contains(MachineNBTCategory.DATA)) {
tagCompound.setShort("AnalyzeTime", (short) analyzeTime);
taskQueueSending.writeToNBT(tagCompound);
}
}
//endregion
//region Inventory Methods
@Override
public boolean canExtractItem(int slot, ItemStack item, int side)
{
return true;
}
@Override
public int[] getAccessibleSlotsFromSide(int side)
{
if(side == 1)
{
return new int[]{input_slot,database_slot};
}
else
{
return new int[]{input_slot};
}
}
//endregion
//region Matter Network Functions
@Override
public BlockPos getPosition()
{
return new BlockPos(this);
}
@Override
public boolean canConnectFromSide(ForgeDirection side) {
int meta = worldObj.getBlockMetadata(xCoord,yCoord,zCoord);
return getOppositeSide(meta) == side.ordinal();
}
@Override
public int onNetworkTick(World world,TickEvent.Phase phase)
{
return networkComponent.onNetworkTick(world,phase);
}
@Override
public MatterNetworkTaskQueue<MatterNetworkTaskStorePattern> getTaskQueue(int id)
{
return taskQueueSending;
}
@Override
public int getTaskQueueCount() {
return 1;
}
@Override
public boolean canPreform(MatterNetworkPacket packet)
{
return networkComponent.canPreform(packet);
}
@Override
public void queuePacket(MatterNetworkPacket packet, ForgeDirection from)
{
networkComponent.queuePacket(packet,from);
}
@Override
public MatterNetworkPacketQueue<MatterNetworkPacket> getPacketQueue(int queueID)
{
return networkComponent.getPacketQueue(queueID);
}
@Override
public int getPacketQueueCount() {
return networkComponent.getPacketQueueCount();
}
//endregion
//region Events
@Override
public void onAdded(World world, int x, int y, int z) {
}
@Override
public void onPlaced(World world, EntityLivingBase entityLiving) {
}
@Override
public void onDestroyed() {
}
@Override
protected void onAwake(Side side) {
}
@Override
public void onActiveChange()
{
forceSync();
}
//endregion
//region Getters and Setters
public IEnergyStorage getEnergyStorage() {return energyStorage;}
public int getSpeed() {return (int)Math.round(ANALYZE_SPEED * getUpgradeMultiply(UpgradeTypes.Speed));}
public int getEnergyDrainPerTick() {return getEnergyDrainMax() / getSpeed();}
public int getEnergyDrainMax() {return (int)Math.round(ENERGY_DRAIN_PER_ITEM * getUpgradeMultiply(UpgradeTypes.PowerUsage));}
@Override
public boolean getServerActive() {return isAnalyzing();}
@Override
public String getSound() {return "analyzer";}
@Override
public boolean hasSound() {return true;}
@Override
public float soundVolume() { return 0.3f;}
@Override
public NBTTagCompound getFilter() {
return componentMatterNetworkConfigs.getFilter();
}
public float getProgress(){return (float)analyzeTime / (float)getSpeed();}
//endregion
}