package fetcher.model;
import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import fetcher.controller.MainController;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import java.io.*;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* Abstracts the pad itself. Provides methods for handling the pad, such as saving and loading.
* It also provides the two Observable lists that are observed by the Controller ListViews (one for the tags, one for the entries).
*/
public class Pad {
private String padName;
public ObservableList<PageEntry> listItems;
public ObservableList<String> allTags;
private ArrayList<String> resourcesToDelete;
public Pad(String padName){
this.padName = padName;
if(!this.padName.endsWith(".json"))
this.padName += ".json";
listItems = FXCollections.observableArrayList();
allTags = FXCollections.observableArrayList();
resourcesToDelete = new ArrayList<String>();
}
/**
* Checks if there's a duplicate entry (same url).
* @param clipBoardStatus the URL copied inside the clipboard.
* @return true if already in the list.
*/
public boolean EntryAlreadyExists(String clipBoardStatus) {
for(PageEntry entry : listItems){
if(entry.getURL().equalsIgnoreCase(clipBoardStatus)) return true;
}
return false;
}
/**
* Adds a tag to the array allTags only if it is not a duplicate.
* @param tag tag to be added.
*/
public void addTag(String tag){
if(!Utils.exists(this.allTags,tag)){
this.allTags.add(tag);
}
}
/**
* Saves the list of entries onto a json file.
*/
public void savePad() {
//TODO: Maybe this method should be called by a thread? The program should not close itself if the thread is still active. (NEEDS testing with lot of entries).
JsonArray arrayOfEntries = new JsonArray();
for (PageEntry entry : listItems) {
JsonObject singleEntry = entry.saveEntry();
arrayOfEntries.add(singleEntry);
}
Gson gson = new Gson();
try {
Writer writer = new FileWriter(padName);
gson.toJson(arrayOfEntries, writer);
writer.close();
} catch (IOException ex) {
ex.printStackTrace();
}
deleteUnusedImages();
}
/**
* Deletes all the images within the resourcesToDelete array.
* Once an entry is deleted, the image associated will be deleted with this method as soon as the user hits the save button.
*/
public void deleteUnusedImages(){
//Deleting images associated with deleted pictures.
for (String path : resourcesToDelete) {
if(path.startsWith("file:///")){
path = path.replace("file:///","");
}
Path pathToFile = FileSystems.getDefault().getPath(path);
try {
Files.delete(pathToFile);
} catch (IOException e) {
e.printStackTrace();
}
}
resourcesToDelete.clear();
}
/***
* Loads the json file specified by LOCATIONS_FILESAVE onto the list provided by the user.
* @param controller the MainController, this is used in the class "Worker" in PageEntry, in order to update the list without blocking the program.
*/
public void loadPad(MainController controller){
JsonParser parser = new JsonParser();
try {
Reader reader = new FileReader(padName);
JsonArray arrayOfEntries = parser.parse(reader).getAsJsonArray();
for(JsonElement JsonElementEntry : arrayOfEntries){
JsonObject JsonObjectEntry = JsonElementEntry.getAsJsonObject();
PageEntry entry = new PageEntry(controller);
entry.loadEntry(JsonObjectEntry);
HashSet<String> tags = new Gson().fromJson(JsonObjectEntry.get("Tags"), new TypeToken<Set<String>>(){}.getType());
for(String tag : tags)
addTag(tag);
entry.setTags(tags);
}
}
catch(FileNotFoundException e){
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
}
}
/**
* Exports json file and images directory as a ZIP file.
*/
public void exportPad() {
final int BUFFER = 2048;
byte data[] = new byte[BUFFER];
File jsonFile = new File(padName);
File imageDirectory = new File((new File(padName)).getParent() + File.separator + "urlpadimages" + File.separator);
List<File> files = Utils.filesList(jsonFile, imageDirectory);
try {
FileOutputStream fileExport = new FileOutputStream(padName.replace(".json", ".zip"));
ZipOutputStream zipExport = new ZipOutputStream(fileExport);
for (File file : files) {
FileInputStream input = new FileInputStream(file);
if(file.getName().endsWith(".json"))
zipExport.putNextEntry(new ZipEntry(file.getName()));
else
zipExport.putNextEntry(new ZipEntry(Utils.IMAGES_SUBFOLDER + File.separator + file.getName()));
int count;
BufferedInputStream origin = new BufferedInputStream(input, BUFFER);
while ((count = origin.read(data, 0, BUFFER)) != -1) {
zipExport.write(data, 0, count);
}
origin.close();
}
zipExport.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Returns a filtered list based on a tag.
* @param tagIndex the index of the tag.
* @return the Filtered list or listItems if the tag is 'all'.
*/
public ObservableList<PageEntry> getFilterTag(int tagIndex) {
//Getting the tag.
String filterTag = allTags.get(tagIndex);
//Creating the new Observable list based on that value.
if(filterTag.equals("all"))
return listItems;
else {
ObservableList<PageEntry> entriesWithTag = FXCollections.observableArrayList();
for (PageEntry entry : listItems) {
if (entry.getTags().contains(filterTag)) entriesWithTag.add(entry);
}
return entriesWithTag;
}
}
/**
* Deletes an entry from the pad. The parameter filterList exists because the user might want to delete the entry within a filtered list.
* @param index of the entry.
* @param filterList the list whitin the user clicked delete from.
*/
public void deleteEntry(int index , ObservableList<PageEntry> filterList) {
if (filterList != listItems) {
//If we are here, it means the user is trying to delete an entry from a filtered list, we need to find the index in the "all" list.
for (int i = 0; i < listItems.size(); i++) {
if (filterList.get(index).equals(listItems.get(i))) {
filterList.remove(index);
index = i;
break;
}
}
}
PageEntry tempEntry = listItems.get(index);
//Adding the image of the deleted entry to the array of resources to be deleted. Such image will be deleted upon saving.
resourcesToDelete.add(tempEntry.getPageSnapshot());
listItems.remove(index);
}
public String getpadName() {
return padName;
}
}