package ethanjones.cubes.world.save;

import ethanjones.cubes.core.logging.Log;
import ethanjones.cubes.entity.living.player.Player;
import ethanjones.cubes.world.storage.Area;
import ethanjones.data.Data;
import ethanjones.data.DataGroup;

import com.badlogic.gdx.files.FileHandle;

import java.io.*;
import java.util.UUID;

public class Save {
  public final String name;
  public final FileHandle fileHandle;
  private SaveOptions saveOptions;
  private SaveAreaList saveAreaList;
  private int saveAreaListModCount = 0;

  public Save(String name, FileHandle fileHandle) {
    this.name = name;
    this.fileHandle = fileHandle;
    this.fileHandle.mkdirs();
    folderArea().mkdirs();
    folderAreaList().mkdirs();
    folderPlayer().mkdirs();
  }

  public boolean writeArea(Area area) {
    return SaveAreaIO.write(this, area);
  }

  public Area readArea(int x, int z) {
    SaveAreaList saveAreaList = getSaveAreaList();
    byte[] hash = saveAreaList.getArea(x, z);
    if (hash == null) return null;
    return SaveAreaIO.read(this, x, z, hash);
  }

  public void writePlayer(Player player) {
    FileHandle folder = folderPlayer();
    FileHandle file = folder.child(player.uuid.toString());
    DataGroup data = player.write();
    try {
      Data.output(data, file.file());
    } catch (Exception e) {
      Log.warning("Failed to write player", e);
    }
  }

  public Player readPlayer(UUID uuid) {
    FileHandle folder = folderPlayer();
    FileHandle file = folder.child(uuid.toString());
    if (!file.exists()) return null;
    try {
      DataGroup data = (DataGroup) Data.input(file.file());
      Player player = new Player(data.getString("username"), uuid);
      player.read(data);
      return player;
    } catch (Exception e) {
      Log.warning("Failed to read player", e);
      return null;
    }
  }

  public synchronized SaveOptions getSaveOptions() {
    if (saveOptions == null) {
      saveOptions = new SaveOptions();
      try {
        DataGroup dataGroup = (DataGroup) Data.input(fileHandle.child("options").file());
        saveOptions.read(dataGroup);
      } catch (Exception e) {
        Log.warning("Failed to read save options", e);
        writeSaveOptions();
      }
    }
    return saveOptions;
  }

  public synchronized SaveOptions writeSaveOptions() {
    if (saveOptions != null) {
      try {
        DataGroup dataGroup = saveOptions.write();
        Data.output(dataGroup, fileHandle.child("options").file());
      } catch (Exception e) {
        Log.warning("Failed to write save options", e);
      }
    }
    return saveOptions;
  }

  public synchronized SaveOptions setSaveOptions(SaveOptions saveOptions) {
    this.saveOptions = saveOptions;
    writeSaveOptions();
    return this.saveOptions;
  }

  public synchronized SaveAreaList getSaveAreaList() {
    if (saveAreaList == null) {
      saveAreaList = new SaveAreaList();
      FileHandle tagFile = fileHandle.child("areatag");
      if (tagFile.exists()) {
        String tag = tagFile.readString("UTF-8");
        FileHandle file = folderAreaList().child(tag);
        try {
          BufferedInputStream stream = file.read(8192);
          saveAreaList.read(new DataInputStream(stream));
          stream.close();
        } catch (Exception e) {
          Log.warning("Failed to read save area list", e);
        }
      }
    }
    return saveAreaList;
  }

  public synchronized SaveAreaList writeSaveAreaList(String tag) {
    if (tag == null) {
      if (saveAreaListModCount == saveAreaList.getModCount()) return saveAreaList;
      tag = Long.toString(System.currentTimeMillis());
    }
    saveAreaListModCount = saveAreaList.getModCount();
    FileHandle file = folderAreaList().child(tag);
    try {
      OutputStream outputStream = file.write(false, 8192);
      saveAreaList.write(new DataOutputStream(outputStream));
      outputStream.close();

      fileHandle.child("areatag").writeString(tag, false, "UTF-8");
    } catch (Exception e) {
      Log.warning("Failed to write save area list", e);
    }
    return saveAreaList;
  }

  public FileHandle folderArea() {
    return fileHandle.child("area");
  }

  public FileHandle folderAreaList() {
    return fileHandle.child("arealist");
  }

  public FileHandle folderPlayer() {
    return fileHandle.child("player");
  }

  @Override
  public String toString() {
    return name;
  }
}

@@ -405,16 +405,49 @@
 Handle;%0A
+  public final boolean readOnly;%0A
   privat
@@ -473,16 +473,16 @@
 ptions;%0A
-
   privat
@@ -599,24 +599,134 @@
 leHandle) %7B%0A
+    this(name, fileHandle, false);%0A  %7D%0A%0A  public Save(String name, FileHandle fileHandle, boolean readOnly) %7B%0A
     this.nam
@@ -886,16 +886,46 @@
 dirs();%0A
+    this.readOnly = readOnly;%0A
   %7D%0A%0A  p
@@ -957,24 +957,56 @@
 rea area) %7B%0A
+    if (readOnly) return false;%0A
     return S
@@ -1300,24 +1300,50 @@
 r player) %7B%0A
+    if (readOnly) return;%0A
     FileHand
@@ -2534,32 +2534,45 @@
 ons() %7B%0A    if (
+!readOnly && 
 saveOptions != n
@@ -3618,32 +3618,32 @@
 eAreaList;%0A  %7D%0A%0A
-
   public synchro
@@ -3689,24 +3689,63 @@
 ring tag) %7B%0A
+    if (readOnly) return saveAreaList;%0A
     if (tag 

Add readOnly tag to Save