package it.unipr.mobdev;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import android.content.Context;
import android.os.Environment;
import android.util.Log;
import android.widget.Toast;

public class BookmarkManager {

	/*
	 * The instance is static so it is shared among all instances of the class. It is also private
	 * so it is accessible only within the class.
	 */
	private static BookmarkManager instance = null;
	
	private ArrayList<BookmarkDescriptor> bookmarkList = null;
	
	/*
	 * Variable used to keep a reference to the current application Context in 
	 * order to properly work with files
	 */
	private Context context = null;

	private String outputFileName = "bookmarkList.txt";
	
	/*
	 * The constructor is private so it is accessible only within the class.
	 */
	private BookmarkManager(Context context){
		Log.d(MainActivity.TAG,"Bookmakr Manager Created !");
		this.context = context;
		
		/*
		 * Try to read an existing bookmark list and load into the ArrayList
		 */
		try {
			this.bookmarkList = readBookmarkListFromFile();
			Log.d(MainActivity.TAG,"Bookmark File available ! List size: " + this.bookmarkList.size());
		} catch(Exception e) {
			//If there is not an existing file create an empty ArrayList
			this.bookmarkList = new ArrayList<BookmarkDescriptor>();
			Log.e(MainActivity.TAG,"Error Reading Bookmark List on File: " + e.getLocalizedMessage());
		} 
		
	}
	
	public static BookmarkManager getInstance(Context context){
		/*
		 * The constructor is called only if the static instance is null, so only the first time 
		 * that the getInstance() method is invoked.
		 * All the other times the same instance object is returned.
		 */
		if(instance == null)
			instance = new BookmarkManager(context);
		return instance;
	}
	
	public int addBookmark(BookmarkDescriptor bookmark){
		if(!this.bookmarkList.contains(bookmark))
		{
			this.bookmarkList.add(bookmark);
			
			try {
				saveBookmarkListOnFile();
			} catch (Exception e) {
				e.printStackTrace();
				Toast.makeText(context, "Error Saving Bookmark List on File ...", Toast.LENGTH_LONG).show();
			}
			
			return 0;
		}
		else
			return -1;
	}
	
	public void replaceBookmark(int position, BookmarkDescriptor bookmark){
		this.bookmarkList.set(position, bookmark);
		
		try {
			saveBookmarkListOnFile();
		} catch (Exception e) {
			e.printStackTrace();
			Toast.makeText(context, "Error Saving Bookmark List on File ...", Toast.LENGTH_LONG).show();
		}
	}
	
	public void removeBookmark(int position){
		this.bookmarkList.remove(position);
		
		try {
			saveBookmarkListOnFile();
		} catch (Exception e) {
			e.printStackTrace();
			Toast.makeText(context, "Error Saving Bookmark List on File ...", Toast.LENGTH_LONG).show();
		}
	}
	
	public void removeBookmark(BookmarkDescriptor bookmark){
		this.bookmarkList.remove(bookmark);
		
		try {
			saveBookmarkListOnFile();
		} catch (Exception e) {
			e.printStackTrace();
			Toast.makeText(context, "Error Saving Bookmark List on File ...", Toast.LENGTH_LONG).show();
		}
	}
	
	public ArrayList<BookmarkDescriptor> getBookmarkList(){
		return bookmarkList;
	}
	
	private boolean isExternalMemoryReady()
	{
		boolean mExternalStorageAvailable = false;
		boolean mExternalStorageWriteable = false;
		String state = Environment.getExternalStorageState();

		if (Environment.MEDIA_MOUNTED.equals(state)) {
			mExternalStorageAvailable = mExternalStorageWriteable = true;
		} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
			mExternalStorageAvailable = true;
			mExternalStorageWriteable = false;
		} else {
			mExternalStorageAvailable = mExternalStorageWriteable = false;
		}
		
		return mExternalStorageAvailable && mExternalStorageWriteable;

	}
	
	/**
	 * Export the bookmark list on file
	 * 
	 * @return
	 */
	public boolean exportStoredBookMarkList()
	{
		//Check if the external memory is ready to be used
		if(isExternalMemoryReady())
		{
				
			try {
				
				//Get a File object associated to the root path of the external storage
				File dir = Environment.getExternalStorageDirectory();
				
				//Path associated to the root of the external storage
				String path = dir.getAbsolutePath();
				
				//The filename of the new file 
				String fileName = "mobdev_bookmarklist.txt";
						
				Log.d(MainActivity.TAG,"External Storage Base Path: " + path);
				
				//Create a new File object representing the file that we are going to create
				File outputFile = new File(path + File.separator + fileName);
				
				//Check if the file does not exist and if not create it
				if(!outputFile.exists())
					outputFile.createNewFile();
				
				//Open the Output stream to write on the target file
				FileOutputStream fos = new FileOutputStream(outputFile);
				
				Gson gson = new Gson();
				
				String outputLine = null;
				
				//For each bookamark descriptor write a different Json 
				//We could also write a JSON Array 
				for(BookmarkDescriptor bookmark : this.bookmarkList)
				{
					outputLine = gson.toJson(bookmark) + "\n";
					
					//Write on the output stream
					fos.write(outputLine.getBytes());
				}
				
				//Close the output stream 
				fos.close();
				
			} catch (Exception e) {
				e.printStackTrace();
				return false;
			}
			
			return true;
		}
		else
		{	
			Log.e(MainActivity.TAG, "Error External Storage Not Available !");	
			return false;
		}
	}
	
	/**
	 * Read (if available) the Bookmark list from file
	 * 
	 * @return
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	private ArrayList<BookmarkDescriptor> readBookmarkListFromFile() throws FileNotFoundException, IOException 
	{
		Log.d(MainActivity.TAG, "Reading Bookmark List from Internal Storage ...");
		
		//Create a StringBuffer used to append the chat read from the file
		StringBuffer strContent = new StringBuffer("");
		
		int ch;

		//Open the FileInputStream using the Context object
		FileInputStream fis = context.openFileInput(outputFileName );

		//While to read char by char the file (It is not the only way ! You can also read line by line)
		while( (ch = fis.read()) != -1)
			strContent.append((char)ch);
		
		//Close the input stream
		fis.close();

		Log.d(MainActivity.TAG, "Read Internal Storage Bookamark File: " + strContent);
		
		//Create the Gson object to deserialize the ArrayList
		Gson gson = new Gson();
		
		Type collectionType = new TypeToken<Collection<BookmarkDescriptor>>(){}.getType();
		
		//Create a Type object for the deserialization of the ArrayList of BookmarkDescriptor
		Collection<BookmarkDescriptor> bookmarkList = gson.fromJson(strContent.toString(), collectionType);
		
		//Save the retrieved list
		if(bookmarkList != null)
			return new ArrayList<BookmarkDescriptor>(bookmarkList);
		
		//If the list is null return an empty list
		return new ArrayList<BookmarkDescriptor>();
	}

	/**
	 * Save the bookmark list on file using the JSON format
	 * 
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	private void saveBookmarkListOnFile() throws FileNotFoundException, IOException
	{
		Log.d(MainActivity.TAG, "Saving BookmarkList on File ...");
		
		//Create the Gson object to serialize the ArrayList
		Gson gson = new Gson();
		
		//Create a Type object for the serialization of the ArrayList of BookmarkDescriptor
		Type collectionType = new TypeToken<Collection<BookmarkDescriptor>>(){}.getType();
		
		//Serialize in JSON 
		String ssidListJson = gson.toJson(this.bookmarkList, collectionType);
		
		//Write the obtained string on file
		FileOutputStream fos = context.openFileOutput(outputFileName, Context.MODE_PRIVATE);
		fos.write(ssidListJson.getBytes());
		fos.close();
	}
	
}

