package com.austinv11.collectiveframework.minecraft.books.api;
import com.austinv11.collectiveframework.minecraft.books.core.GuiBook;
import com.austinv11.collectiveframework.minecraft.books.core.ItemBook;
import com.austinv11.collectiveframework.minecraft.books.core.LocalBookData;
import com.austinv11.collectiveframework.minecraft.utils.NBTHelper;
import com.austinv11.collectiveframework.minecraft.utils.client.GuiUtils;
import com.austinv11.collectiveframework.multithreading.SimpleRunnable;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompressedStreamTools;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.ResourceLocation;
import java.io.File;
import java.io.IOException;
/**
* This class is holds all required information to render and handle books
*/
public abstract class Book {
/**
* If this is true, a debug grid will be drawn atop the book
*/
public boolean drawDebugLines = false;
private static final String GLOBAL_DIR = "./CFBookData/";
private static volatile NBTTagCompound globalData; //A buffer for file I/O
private int currentPage = 0;
protected ItemBook bookItem;
protected ItemStack bookStack;
public EntityPlayer player;
/**
* The gui rendering the book
*/
@SideOnly(Side.CLIENT)
private GuiBook bookGui;
/**
* Default Book constructor, this MUST be public
* @param bookItem The book item
* @param bookStack The book stack
*/
public Book(ItemBook bookItem, ItemStack bookStack) {
this.bookItem = bookItem;
this.bookStack = bookStack;
}
/**
* Used to determine how data will be stored in the book
* @return The storage type
*/
public abstract DataStorageType getDataStorageType();
/**
* Gets the book's data
* @param stack The book stack (only necessary when {@link #getDataStorageType()} returns {@link DataStorageType#LOCAL}
* @return The data
*/
public NBTTagCompound getData(ItemStack stack) {
if (getDataStorageType() == DataStorageType.GLOBAL) {
if (globalData == null) {
File storage = new File(GLOBAL_DIR+bookItem.getModId()+"/"+bookItem.getUnlocalizedName()+".dat");
if (!storage.exists())
return new NBTTagCompound();
try {
globalData = CompressedStreamTools.read(storage);
} catch (IOException e) {
e.printStackTrace();
}
}
return (NBTTagCompound) globalData.copy();
} else if (getDataStorageType() == DataStorageType.LOCAL) {
String key = bookItem.getModId()+":"+bookItem.getUnlocalizedName();
NBTBase retrieved = LocalBookData.retrieveLocalData(key);
if (retrieved == null)
return new NBTTagCompound();
return (NBTTagCompound) retrieved.copy();
} else {
return NBTHelper.getCompoundTag(stack, "CFBookData");
}
}
/**
* Sets the book's data
* @param data The new data
* @param stack The book stack (only necessary when {@link #getDataStorageType()} returns {@link DataStorageType#LOCAL}
*/
public void setData(final NBTTagCompound data, final ItemStack stack) {
final NBTTagCompound tag = getData(stack);
if (getDataStorageType() == DataStorageType.GLOBAL) {
globalData = data;
new SimpleRunnable() { //Done to prevent speed bottlenecks from constant file modification
@Override
public void run() {
tag.setTag(player.getGameProfile().getId().toString(), data);
File storage = new File(GLOBAL_DIR+bookItem.getModId()+"/"+bookItem.getUnlocalizedName()+".dat");
try {
CompressedStreamTools.write(tag, storage);
} catch (IOException e) {
e.printStackTrace();
}
this.disable(true);
}
@Override
public String getName() {
return "Book Data Serializer";
}
}.start();
} else if (getDataStorageType() == DataStorageType.LOCAL) {
String key = bookItem.getModId()+":"+bookItem.getUnlocalizedName();
tag.setTag(player.getGameProfile().getId().toString(), data);
LocalBookData.putLocalData(key, tag);
} else {
tag.setTag(player.getGameProfile().getId().toString(), data);
NBTHelper.setTag(stack, "CFBookData", tag);
}
}
/**
* Retrieves the current page of the book
* @return The current page
*/
public int getCurrentPage() {
return currentPage;
}
/**
* Attempts to set the page of the book
* @param newPage The new page
*/
public void setPage(int newPage) {
if (newPage >= getPages().length)
newPage = 0;
else if (newPage < 0)
newPage = getPages().length;
if (onPageFlip(currentPage, newPage)) {
currentPage = newPage;
NBTTagCompound data = getData(bookStack);
data.setInteger("page", currentPage);
setData(data, bookStack);
}
}
/**
* Do NOT touch this, it is required for communications between the actual gui and book
* @param gui The actual gui
*/
@SideOnly(Side.CLIENT)
public final void handoffGui(GuiBook gui) {
this.bookGui = gui;
}
/**
* Called when the book is opened
* @param player The player who opened the book
*/
public void onOpen(EntityPlayer player) {
this.player = player;
NBTTagCompound data = getData(bookStack);
if (data.hasKey("page"))
setPage(data.getInteger("page"));
}
/**
* Called when the book is closed
*/
public void onClose() {}
/**
* Closes the book's gui
*/
@SideOnly(Side.CLIENT)
public void closeBook() {
GuiUtils.closeCurrentGui(GuiBook.class);
}
/**
* Same as {@link GuiScreen#doesGuiPauseGame()}
*/
public abstract boolean doesPauseGame();
/**
* Used to retrieve the pages in the book
* @return All the pages in the book, THE ORDER MATTERS! Page at index 0 is the page displayed on the first page of
* the book
*/
public abstract Page[] getPages();
/**
* Used to get the background image, basically the generic background
* @return The resource location of the background, or null if there aren't any
*/
public abstract ResourceLocation getBackground();
/**
* Called when the page is rendered
* @param dt The time since the last render of the current pass in milliseconds
*/
@SideOnly(Side.CLIENT)
public abstract void onRender(int dt);
/**
* Called when the page is attempted to be changed
* @param oldPage The current page of the book
* @param newPage The new page of the book
* @return True to allow the page flip, false if otherwise
*/
public abstract boolean onPageFlip(int oldPage, int newPage);
/**
* Represents where the persistence data is stored for this book:
* GLOBAL = All worlds share the same data
* LOCAL = All books in one world share the same data
* INSTANCE = The data stored is per book
*/
public static enum DataStorageType {
GLOBAL, LOCAL, INSTANCE
}
}