// Copyright 2011-2012 Paulo Augusto Peccin. See licence.txt distributed with this file.

package org.javatari.pc.savestate;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.security.AccessControlException;

import org.javatari.atari.cartridge.formats.CartridgeSavestate;
import org.javatari.atari.console.savestate.ConsoleState;
import org.javatari.atari.console.savestate.SaveStateMedia;
import org.javatari.atari.console.savestate.SaveStateSocket;
import org.javatari.pc.cartridge.FileROMChooser;
import org.javatari.pc.cartridge.ROMLoader;


public final class FileSaveStateMedia implements SaveStateMedia {

	public void connect(SaveStateSocket socket) {
		socket.connectMedia(this);
	}

	@Override
	public boolean saveStateFile(ConsoleState state) {
		try {
			File file = FileROMChooser.chooseFileToSavestate();
			if (file == null) return false;
			return internalSaveToFile(file.toString(), state, true, false);
		} catch (AccessControlException e) {
			// No option if in Sandboxed mode
			return false;
		}
	}

	@Override
	public boolean saveState(int slot, ConsoleState state) {
		return internalSaveToFile("javatarisave" + slot + "." + ROMLoader.VALID_STATE_FILE_EXTENSION, state, true, true);
	}

	@Override
	public ConsoleState loadState(int slot) {
		try{
			return (ConsoleState)internalLoadFromFile("javatarisave" + slot + "." + ROMLoader.VALID_STATE_FILE_EXTENSION, true, true);
		} catch (Exception ex) {
			// ClassCast or any other error
			return null;
		}
	}

	@Override
	public boolean saveResourceToFile(String fileName, Object data) {
		return internalSaveToFile(fileName, data, false, true);
	}

	@Override
	public Object loadResourceFromFile(String fileName) {
		return internalLoadFromFile(fileName, false, true);
	}

	private boolean internalSaveToFile(String fileName, Object data, boolean isSavestate, boolean insideSavesDirectory) {
		try {
			// Create the savestate directory if needed
			File dir = new File(savesDirectory());
			if (!dir.isDirectory())
				dir.mkdir();
			FileOutputStream file = null;
			try {
				ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
				// Begin the file with the identifier if asked
				if (isSavestate) {
					byteStream.write(CartridgeSavestate.contentIdentifier);
					byteStream.flush();
				}
				// Then the Savestate data
				ObjectOutputStream stream = new ObjectOutputStream(byteStream);
				stream.writeObject(data);
				file = new FileOutputStream(insideSavesDirectory ? insideSavesDirectory(fileName) : fileName);
				file.write(byteStream.toByteArray());
			} finally {
				if (file != null) file.close();
			}
			return true;
		} catch (Exception ex) {
			// No permissions or any other IO error
			ex.printStackTrace();
			return false;
		}
	}
	
	private Object internalLoadFromFile(String fileName, boolean isSavestate, boolean insideSavesDirectory) {
		try{ 
			FileInputStream file = null;
			try{
				file = new FileInputStream(insideSavesDirectory ? insideSavesDirectory(fileName) : fileName);
				// Load the state
				byte[] data = new byte[file.available()];
				file.read(data);
				ByteArrayInputStream byteStream = new ByteArrayInputStream(data);
				// If asked, check the Savestate identifier that should be present at the beginning of the file
				if (isSavestate) {
					if (!CartridgeSavestate.checkIdentifier(data))
						throw new IllegalStateException("Invalid Javatari Savestate file");
					// Skips the Savestate identifier
					byteStream.skip(CartridgeSavestate.contentIdentifier.length);
				}
				ObjectInputStream objStream = new ObjectInputStream(byteStream);
				return objStream.readObject();
			} finally {
				if (file != null) file.close();
			}
		} catch (Exception ex) {
			// No permissions or any other IO error
			ex.printStackTrace();
			return null;
		}
	}
	
	private String insideSavesDirectory(String fileName) {
		return savesDirectory() + File.separator + fileName;
	}
	
	private String savesDirectory() {
		if (savesDirectory != null) return savesDirectory;
		try {
			String userHome = System.getProperty("user.home");
			if (userHome != null && !userHome.isEmpty()) 
				return savesDirectory = userHome + File.separator + BASE_DIR;
		} catch(SecurityException ex) {
			// No permissions... Ignore and use default directory
		}
		return savesDirectory = BASE_DIR;
	}
	
	
	private String savesDirectory;
	
	private static final String BASE_DIR = "javatarisaves";

}
