/*******************************************************************************
* Copyright 2011 Antti Havanko
*
* This file is part of Motiver.fi.
* Motiver.fi is licensed under one open source license and one commercial license.
*
* Commercial license: This is the appropriate option if you want to use Motiver.fi in
* commercial purposes. Contact license@motiver.fi for licensing options.
*
* Open source license: This is the appropriate option if you are creating an open source
* application with a license compatible with the GNU GPL license v3. Although the GPLv3 has
* many terms, the most important is that you must provide the source code of your application
* to your users so they can be free to modify your application for their own needs.
******************************************************************************/
package com.delect.motiver.client.presenter.training;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.shared.SimpleEventBus;
import com.google.gwt.http.client.Request;
import com.google.gwt.user.client.Timer;
import com.delect.motiver.client.AppController;
import com.delect.motiver.client.Motiver;
import com.delect.motiver.client.MyAsyncCallback;
import com.delect.motiver.client.event.DateChangedEvent;
import com.delect.motiver.client.event.ExerciseRemovedEvent;
import com.delect.motiver.client.event.WorkoutCreatedEvent;
import com.delect.motiver.client.event.WorkoutMovedEvent;
import com.delect.motiver.client.event.WorkoutRemovedEvent;
import com.delect.motiver.client.event.handler.ExerciseRemovedEventHandler;
import com.delect.motiver.client.presenter.CommentsBoxPresenter;
import com.delect.motiver.client.presenter.CommentsBoxPresenter.CommentsBoxDisplay;
import com.delect.motiver.client.presenter.EmptyPresenter;
import com.delect.motiver.client.presenter.EmptyPresenter.EmptyDisplay;
import com.delect.motiver.client.presenter.PopupPresenter;
import com.delect.motiver.client.presenter.PopupPresenter.PopupDisplay;
import com.delect.motiver.client.presenter.Presenter;
import com.delect.motiver.client.presenter.UserPresenter;
import com.delect.motiver.client.presenter.UserPresenter.UserDisplay;
import com.delect.motiver.client.presenter.training.EmptyWorkoutPresenter.EmptyWorkoutDisplay;
import com.delect.motiver.client.presenter.training.ExercisePresenter.ExerciseDisplay;
import com.delect.motiver.client.service.MyServiceAsync;
import com.delect.motiver.client.view.CommentsBoxView;
import com.delect.motiver.client.view.Display;
import com.delect.motiver.client.view.EmptyView;
import com.delect.motiver.client.view.PopupView;
import com.delect.motiver.client.view.UserView;
import com.delect.motiver.client.view.training.EmptyWorkoutView;
import com.delect.motiver.client.view.training.ExerciseView;
import com.delect.motiver.client.view.training.WorkoutView;
import com.delect.motiver.client.view.widget.PopupSize;
import com.delect.motiver.shared.Constants;
import com.delect.motiver.shared.ExerciseModel;
import com.delect.motiver.shared.ExerciseNameModel;
import com.delect.motiver.shared.WorkoutModel;
import com.delect.motiver.shared.util.CommonUtils;
import com.extjs.gxt.ui.client.event.BaseEvent;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.widget.LayoutContainer;
/**
* Single workout
*/
public class WorkoutPresenter extends Presenter {
static PopupSize POPUP_WORKOUT = new PopupSize(800,500);
/**
* Abstract class for view to extend
*/
public abstract static class WorkoutDisplay extends Display {
public abstract LayoutContainer getBodyContainer();
public abstract LayoutContainer getCommentsContainer();
public abstract LayoutContainer getUserContainer();
public abstract void setAddButtonVisible(boolean b);
public abstract void setDropTarget(String string);
public abstract void setHandler(WorkoutHandler workoutHandler);
public abstract void setModel(WorkoutModel workout);
}
public interface WorkoutHandler {
/**
* Called when exercise is dragged
* @param id
* @param newPos : position which exercise is dragged (BEFORE this position)
*/
void dragged(long id, int newPos);
void exercisesHidden();
void exercisesVisible();
void newExercise();
void saveData(WorkoutModel model);
void workoutMoved(Date newDate);
void workoutRemoved();
void openNewWindow();
}
//new exercise listener
public Listener<BaseEvent> NewExerciseListener = new Listener<BaseEvent>() {
@Override
public void handleEvent(BaseEvent be) {
addNewExercise();
}
};
private CommentsBoxPresenter commentsPresenter;
private Presenter emptyPresenter;
private List<ExercisePresenter> exercisePresenters = new ArrayList<ExercisePresenter>();
private UserPresenter userPresenter;
protected WorkoutDisplay display;
protected WorkoutModel workout;
protected Timer timerUpdate;
/**
* Shows single workout
* @param rpcService
* @param eventBus
* @param display
* @param workout : model, if null -> creates new
*/
public WorkoutPresenter(MyServiceAsync rpcService, SimpleEventBus eventBus, WorkoutDisplay display, WorkoutModel workout) {
super(rpcService, eventBus);
this.display = display;
this.workout = workout;
}
@Override
public Display getView() {
return display;
}
@Override
public void onBind() {
display.setModel(workout);
display.setDropTarget(Constants.DRAG_GROUP_WORKOUT + workout.getId());
//event handler (fire event)
display.setHandler(new WorkoutHandler() {
@SuppressWarnings("unchecked")
@Override
public void dragged(long id, int newPos) {
try {
//minus one, because orders start at zero
newPos--;
boolean found = false;
//update order fields
List<ExerciseModel> arr = new ArrayList<ExerciseModel>();
for(int i=0; i < exercisePresenters.size(); i++) {
Presenter presenter = exercisePresenters.get(i);
if(presenter != null) {
final ExerciseModel ex = ((ExercisePresenter)presenter).exercise;
//if dragged exercise
if( ex.getId() == id) {
ex.setOrder(newPos);
found = true;
}
else if(found) {
//if before new pos
if(i <= newPos) {
ex.setOrder( i - 1 );
}
//if after new pos
else {
ex.setOrder( i );
}
}
else if(!found) {
//if before new pos
if(i < newPos) {
ex.setOrder( i );
}
//if after new pos
else {
ex.setOrder( i + 1 );
}
}
arr.add(ex);
//update exercise
((ExercisePresenter)presenter).setModel(ex);
}
}
//sort presenters
Collections.sort(exercisePresenters);
//get id array
Long[] ids = new Long[exercisePresenters.size()];
for(int i=0; i < exercisePresenters.size(); i++) {
final ExerciseModel ex = exercisePresenters.get(i).exercise;
ids[i] = ex.getId();
}
//update server
rpcService.updateExerciseOrder(workout, ids, MyAsyncCallback.EmptyCallback);
//remove everything
for(int i=0; i < exercisePresenters.size(); i++) {
exercisePresenters.get(i).remove();
}
//re-add
for(int i=0; i < exercisePresenters.size(); i++) {
exercisePresenters.get(i).update(display.getBodyContainer());
}
} catch (Exception e) {
Motiver.showException(e);
}
}
@Override
public void exercisesHidden() {
//stop presenters
unbindPresenters();
}
@Override
public void exercisesVisible() {
showExercises();
}
@Override
public void newExercise() {
addNewExercise();
}
@SuppressWarnings("unchecked")
@Override
public void saveData(WorkoutModel model) {
//if cancelled adding new measurement
if(model == null) {
stop();
//fire event
eventBus.fireEvent(new WorkoutRemovedEvent(workout));
}
//added new measurement
else if(model.getId() == 0) {
workout = model;
display.setContentEnabled(false);
//create model
final Request req = rpcService.addWorkout(model, new MyAsyncCallback<WorkoutModel>() {
@Override
public void onSuccess(WorkoutModel result) {
display.setContentEnabled(true);
workout = result;
refresh();
//fire event
final WorkoutCreatedEvent event = new WorkoutCreatedEvent(workout);
eventBus.fireEvent(event);
}
});
addRequest(req);
}
//edited old value
else {
workout = model;
if(timerUpdate != null)
timerUpdate.cancel();
timerUpdate = new Timer() {
@Override
public void run() {
rpcService.updateWorkout(workout, MyAsyncCallback.EmptyCallback);
}
};
timerUpdate.schedule(Constants.DELAY_MODEL_UPDATE);
}
}
@Override
public void workoutMoved(final Date newDate) {
try {
display.getBaseContainer().setEnabled(false);
workout.setDate(CommonUtils.trimDateToDatabase(newDate, true));
rpcService.updateWorkout(workout, new MyAsyncCallback<Boolean>() {
@Override
public void onSuccess(Boolean result) {
//fire events
DateChangedEvent event = new DateChangedEvent(newDate);
fireEvent(event);
fireEvent(new WorkoutMovedEvent(workout));
}
});
} catch (Exception e) {
Motiver.showException(e);
}
}
@Override
public void workoutRemoved() {
display.setContentEnabled(false);
//remove workout and fire WorkoutRemovedEvent
rpcService.removeWorkout(workout, new MyAsyncCallback<Boolean>() {
@Override
public void onSuccess(Boolean result) {
display.setContentEnabled(true);
stop();
//fire event
eventBus.fireEvent(new WorkoutRemovedEvent(workout));
}
});
}
@Override
public void openNewWindow() {
PopupPresenter p = WorkoutPresenter.getWorkoutPopup(rpcService, eventBus, workout);
p.run(display.getBodyContainer());
}
});
//EVENT: exercise removed
addEventHandler(ExerciseRemovedEvent.TYPE, new ExerciseRemovedEventHandler() {
@Override
public void onExerciseRemoved(ExerciseRemovedEvent event) {
checkExerciseEvent(event.getExercise(), 2);
}
});
}
@Override
public void onRun() {
if(workout.getId() != 0) {
showExercises();
}
//no model -> hightlight
else {
highlight();
}
}
@Override
public void onStop() {
if(emptyPresenter != null) {
emptyPresenter.stop();
}
if(commentsPresenter != null) {
commentsPresenter.stop();
}
if(userPresenter != null) {
userPresenter.stop();
}
unbindPresenters();
}
/**
* Removes presenter from view
* @param meal
*/
private void removePresenter(ExerciseModel exercise) {
//remove also from presenters
for(int i=0; i < exercisePresenters.size(); i++) {
ExercisePresenter presenter = exercisePresenters.get(i);
if(presenter != null && presenter.exercise.getId() == exercise.getId()) {
exercisePresenters.remove(presenter);
}
}
//if no exercises -> show empty presenter
if(exercisePresenters.size() == 0) {
if(workout.getUser().equals(AppController.User)) {
emptyPresenter = new EmptyWorkoutPresenter(rpcService, eventBus, (EmptyWorkoutDisplay)GWT.create(EmptyWorkoutView.class));
}
else {
emptyPresenter = new EmptyPresenter(rpcService, eventBus, (EmptyDisplay)GWT.create(EmptyView.class), AppController.Lang.NoExercises());
}
emptyPresenter.run(display.getBodyContainer());
//hide add button
display.setAddButtonVisible(false);
}
}
/**
* Unbinds all the meal/time presenters
*/
private void unbindPresenters() {
if(exercisePresenters != null) {
for(int i=0; i < exercisePresenters.size(); i++) {
final Presenter presenter = exercisePresenters.get(i);
if(presenter != null) {
presenter.stop();
}
}
exercisePresenters.clear();
}
}
/**
* Adds new exercise to workout
*/
protected void addNewExercise() {
//create dummy exercise
final ExerciseModel dummy = new ExerciseModel(new ExerciseNameModel(0L, "", 0));
dummy.setWorkoutId(workout.getId());
//init new foodpresenter
final ExercisePresenter fp = new ExercisePresenter(rpcService, eventBus, (ExerciseDisplay)GWT.create(ExerciseView.class), dummy, workout);
addNewPresenter(fp, true);
}
/**
* Adds new presenter to view
* @param presenter
*/
protected void addNewPresenter(ExercisePresenter presenter, boolean setOrder) {
//remove emptypresenter if present
if(emptyPresenter != null) {
emptyPresenter.stop();
emptyPresenter = null;
}
//set order (last exercises order plus one)
if(setOrder) {
presenter.exercise.setOrder( (exercisePresenters.size() == 0)? 0 : (exercisePresenters.get(exercisePresenters.size() - 1).exercise.getOrder() + 100) );
}
exercisePresenters.add(presenter);
presenter.run(display.getBodyContainer());
//show add button
display.setAddButtonVisible( workout.getUser().equals(AppController.User) );
}
/**
* Handle exercise created/updated/removed event
* @param exercise
* @param target : 0=created, 1=updated, 2=removed
*/
protected void checkExerciseEvent(ExerciseModel exerciseUpdated, int target) {
//if added to this time
if(exerciseUpdated.getWorkoutId() != 0) {
if(exerciseUpdated.getWorkoutId() == workout.getId()) {
//check if belongs to any exercise
for(int i=0; i < exercisePresenters.size(); i++) {
ExercisePresenter presenter = exercisePresenters.get(i);
if(presenter != null) {
ExerciseModel e = presenter.exercise;
if( e.getId() == exerciseUpdated.getId()) {
//removed
if(target == 2) {
removePresenter(e);
}
break;
}
}
}
}
}
}
protected void showExercises() {
display.setContentEnabled(false);
try {
if(emptyPresenter != null) {
emptyPresenter.stop();
}
if(commentsPresenter != null) {
commentsPresenter.stop();
}
unbindPresenters();
//userview
if(workout.getDate() == null && workout.getRoutineId() == 0) {
if(userPresenter != null) {
userPresenter.stop();
}
//show user if not our workout
if(!workout.getUser().equals(AppController.User)) {
userPresenter = new UserPresenter(rpcService, eventBus, (UserDisplay) GWT.create(UserView.class), workout.getUser(), false);
userPresenter.run(display.getUserContainer());
}
}
//if no workouts
if(workout.getExercises().size() == 0) {
if(workout.getUser().equals(AppController.User)) {
emptyPresenter = new EmptyWorkoutPresenter(rpcService, eventBus, (EmptyWorkoutDisplay)GWT.create(EmptyWorkoutView.class));
}
else {
emptyPresenter = new EmptyPresenter(rpcService, eventBus, (EmptyDisplay)GWT.create(EmptyView.class), AppController.Lang.NoExercises());
}
emptyPresenter.run(display.getBodyContainer());
//hide add button
display.setAddButtonVisible(false);
}
else {
//sort
Collections.sort(workout.getExercises());
for(ExerciseModel m : workout.getExercises()) {
//set date & id & order
m.setWorkoutId(workout.getId());
m.setDate(workout.getDate());
m.setWorkout(workout);
//init new exercisePresenter
final ExercisePresenter fp = new ExercisePresenter(rpcService, eventBus, (ExerciseDisplay)GWT.create(ExerciseView.class), m, workout);
addNewPresenter(fp, false);
}
}
//show user & comments (if not in any routine)
if(workout.getDate() != null || workout.getRoutineId() == 0) {
if(commentsPresenter != null) {
commentsPresenter.stop();
}
commentsPresenter = new CommentsBoxPresenter(rpcService, eventBus, (CommentsBoxDisplay)GWT.create(CommentsBoxView.class), workout);
commentsPresenter.run(display.getCommentsContainer());
}
} catch (Exception e) {
Motiver.showException(e);
}
display.setContentEnabled(true);
}
/**
* Returns popup presenter for workout
* @param rpcService
* @param eventBus
* @param workout
* @return
*/
protected static PopupPresenter getWorkoutPopup(MyServiceAsync rpcService, SimpleEventBus eventBus, WorkoutModel workout) {
PopupPresenter p = new PopupPresenter(rpcService, eventBus, (PopupDisplay)GWT.create(PopupView.class), new WorkoutPresenter(rpcService, eventBus, (WorkoutDisplay)GWT.create(WorkoutView.class), workout), POPUP_WORKOUT);
p.setTitle(CommonUtils.getDateString(workout.getDate(), true, false));
return p;
}
}