package com.lateensoft.pathfinder.toolkit.views.character;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.InvalidObjectException;
import java.util.List;
import android.app.Activity;
import android.net.Uri;
import com.lateensoft.pathfinder.toolkit.R;
import com.lateensoft.pathfinder.toolkit.dao.DataAccessException;
import com.lateensoft.pathfinder.toolkit.db.dao.table.CharacterModelDAO;
import com.lateensoft.pathfinder.toolkit.db.dao.table.CharacterNameDAO;
import com.lateensoft.pathfinder.toolkit.model.IdNamePair;
import com.lateensoft.pathfinder.toolkit.model.character.PathfinderCharacter;
import com.lateensoft.pathfinder.toolkit.pref.GlobalPrefs;
import com.lateensoft.pathfinder.toolkit.pref.Preferences;
import com.lateensoft.pathfinder.toolkit.util.*;
import com.lateensoft.pathfinder.toolkit.views.BasePageFragment;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.lateensoft.pathfinder.toolkit.views.picker.PickerUtil;
import org.dom4j.DocumentException;
import roboguice.RoboGuice;
//This is a base class for all fragments in the character sheet activity
public abstract class AbstractCharacterSheetFragment extends BasePageFragment {
private static final String TAG = AbstractCharacterSheetFragment.class.getSimpleName();
public static final int GET_IMPORT_REQ_CODE = 3056;
public static final int SELECT_CHARACTER_REQ_CODE = 7563;
private long currentCharacterID;
private CharacterModelDAO characterModelDao;
private CharacterNameDAO characterNameDao;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
characterModelDao = new CharacterModelDAO(getContext());
characterNameDao = new CharacterNameDAO(getContext());
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
public void updateTitle() {
setTitle(characterNameDao.find(currentCharacterID).getName());
setSubtitle(getFragmentTitle());
}
@Override
public void onPause() {
updateDatabase();
super.onPause();
}
@Override
protected void onResumeWithoutResult() {
super.onResumeWithoutResult();
loadSelectedCharacter();
}
private void setSelectedCharacter(long characterId) {
Preferences preferences = RoboGuice.getInjector(getContext()).getInstance(Preferences.class);
preferences.put(GlobalPrefs.SELECTED_CHARACTER_ID, characterId);
currentCharacterID = characterId;
loadSelectedCharacter();
}
/**
* Load the currently set character in shared prefs If there is no character
* set in user prefs, it automatically generates a new one.
*/
public void loadSelectedCharacter() {
Preferences preferences = RoboGuice.getInjector(getContext()).getInstance(Preferences.class);
long currentCharacterID = preferences.get(GlobalPrefs.SELECTED_CHARACTER_ID, -1L);
if (currentCharacterID == -1) {
// There was no current character set in shared prefs
Log.v(TAG, "Default character add");
addNewCharacterAndSelect();
} else {
this.currentCharacterID = currentCharacterID;
loadFromDatabase();
if (getRootView() != null) {
updateFragmentUI();
}
}
updateTitle();
}
/**
* Generates a new character and sets it to the current character. Reloads
* the fragments.
*/
public void addNewCharacterAndSelect() {
PathfinderCharacter newChar = PathfinderCharacter.newDefaultCharacter("New Adventurer");
long id = addCharacterToDB(newChar);
if (id != -1) {
setSelectedCharacter(id);
} else {
Toast.makeText(getContext(), "Error creating new character. Please contact developers for support if issue persists.", Toast.LENGTH_LONG).show();
}
performUpdateReset();
}
public long addCharacterToDB(PathfinderCharacter character) {
long id = -1;
try {
id = characterModelDao.add(character);
Log.i(TAG, "Added new character");
} catch (DataAccessException e) {
Log.e(TAG, "Error occurred creating new character", e);
}
return id;
}
/**
* Deletes the current character and loads the first in the list, or creates
* a new blank one, if there was only one.
*/
public void deleteCurrentCharacter() {
int currentCharacterIndex = 0;
List<IdNamePair> characters = characterNameDao.findAll();
long characterForDeletion = currentCharacterID;
for (int i = 0; i < characters.size(); i++) {
if (characterForDeletion == characters.get(i).getId()){
currentCharacterIndex = i;
break;
}
}
if (characters.size() == 1) {
addNewCharacterAndSelect();
} else {
int charToSelect = (currentCharacterIndex == 0) ? 1 : 0;
setSelectedCharacter(characters.get(charToSelect).getId());
}
try {
characterModelDao.removeById(characterForDeletion);
Log.i(TAG, "Deleted current character: " + characterForDeletion);
} catch (DataAccessException e) {
Log.e(TAG, "Failed to delete character " + characterForDeletion, e);
Toast.makeText(getContext(), "Failed to delete character", Toast.LENGTH_SHORT).show();
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.mi_character_list:
showCharacterSelectionDialog();
break;
case R.id.mi_new_character:
showCreateCharacterDialog();
break;
case R.id.mi_delete_character:
showDeleteCharacterDialog();
break;
case R.id.mi_export_character:
exportCurrentCharacter();
break;
case R.id.mi_export_all:
exportAllCharacters();
break;
case R.id.mi_import_character:
importCharacters();
break;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.character_sheet_menu, menu);
super.onCreateOptionsMenu(menu, inflater);
}
private void showCharacterSelectionDialog() {
List<IdNamePair> characters = characterNameDao.findAll();
PickerUtil.Builder builder = new PickerUtil.Builder(getActivity());
builder.setTitle(R.string.select_character_dialog_header)
.setSingleChoice(true)
.setPickableCharacters(characters, characterNameDao.find(getCurrentCharacterID()));
Intent pickerIntent = builder.build();
startActivityForResult(pickerIntent, SELECT_CHARACTER_REQ_CODE);
}
private void showCreateCharacterDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle(getString(R.string.menu_item_new_character));
builder.setMessage(getString(R.string.new_character_dialog_message))
.setPositiveButton(R.string.ok_button_text, new OnClickListener() {
@Override public void onClick(DialogInterface dialog, int which) {
performUpdateReset();
addNewCharacterAndSelect();
}
})
.setNegativeButton(R.string.cancel_button_text, null);
builder.show();
}
private void showDeleteCharacterDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle(getString(R.string.menu_item_delete_character));
builder.setMessage(String.format(getString(R.string.confirm_delete_item_message),
characterNameDao.find(currentCharacterID)))
.setPositiveButton(R.string.delete_button_text, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
deleteCurrentCharacter();
}
})
.setNegativeButton(R.string.cancel_button_text, null);
builder.show();
}
public void exportCurrentCharacter() {
PathfinderCharacter character = characterModelDao.find(getCurrentCharacterID());
ImportExportUtils.exportCharacterWithDialog(getContext(), character, new ActivityLauncher.ActivityLauncherFragment(this));
}
public void exportAllCharacters() {
List<PathfinderCharacter> characters = characterModelDao.findAll();
ImportExportUtils.exportCharactersWithDialog(getContext(), characters, "All Characters", new ActivityLauncher.ActivityLauncherFragment(this));
}
public void importCharacters() {
ImportExportUtils.importCharacterFromContent(new ActivityLauncher.ActivityLauncherFragment(this),
GET_IMPORT_REQ_CODE);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == GET_IMPORT_REQ_CODE ) {
handleCharacterImportResult(resultCode, data);
} else if (requestCode == SELECT_CHARACTER_REQ_CODE) {
handleCharacterSelectionResult(resultCode, data);
}
}
private void handleCharacterImportResult(int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK) {
Uri uri = data.getData();
Exception thrownException = null;
try {
if (uri == null) {
throw new FileNotFoundException();
}
InputStream is = getContext().getContentResolver().openInputStream(uri);
List<PathfinderCharacter> characters = ImportExportUtils.importCharactersFromStream(is);
long lastCharacterId = -1;
boolean didFailToImport = false;
for(PathfinderCharacter character : characters) {
lastCharacterId = addCharacterToDB(character);
if (lastCharacterId == -1) {
didFailToImport = true;
}
}
if (lastCharacterId != -1) {
setSelectedCharacter(lastCharacterId);
}
if(didFailToImport) {
Toast.makeText(getContext(), R.string.import_error_unknown, Toast.LENGTH_LONG).show();
}
} catch (FileNotFoundException e) {
Toast.makeText(getContext(), R.string.import_error_file_not_found, Toast.LENGTH_LONG).show();
thrownException = e;
} catch (DocumentException e) {
Toast.makeText(getContext(), R.string.import_error_invalid_xml, Toast.LENGTH_LONG).show();
thrownException = e;
} catch (InvalidObjectException e) {
Toast.makeText(getContext(), String.format(getString(R.string.import_error_exception), e.getMessage()),
Toast.LENGTH_LONG).show();
thrownException = e;
} finally {
if (thrownException != null) {
Log.e(TAG, "Failed to import character(s) from " + uri, thrownException);
}
}
} else {
Toast.makeText(getContext(), R.string.unable_to_load_file_msg, Toast.LENGTH_LONG).show();
}
}
private void handleCharacterSelectionResult(int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK) {
PickerUtil.ResultData pickerData = new PickerUtil.ResultData(data);
IdNamePair selectedCharacter = pickerData.getCharacter();
if (selectedCharacter != null && selectedCharacter.getId() != getCurrentCharacterID()) {
setSelectedCharacter(selectedCharacter.getId());
}
}
}
/**
* Depending on the use, this forces the current tab to save its values to
* mCharacter, and updates them. Ends with current tab set to Fluff.
*/
public void performUpdateReset() {
switchToPage(CharacterFluffFragment.class);
}
public long getCurrentCharacterID() {
return currentCharacterID;
}
public CharacterModelDAO getCharacterModelDAO() {
return characterModelDao;
}
/**
* Refreshes the UI. Is automatically called onResume
*/
public abstract void updateFragmentUI();
/**
* @return The title of the fragment instance
*/
public abstract String getFragmentTitle();
/**
* Called to have the subclass update any relevant parts of the database.
* Called onPause, among other areas.
*/
public abstract void updateDatabase();
/**
* Called to notify the base class that it should load its data from the database.
* Called onResume
*/
public abstract void loadFromDatabase();
}