/*******************************************************************************
* 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.nutrition;
import java.util.ArrayList;
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.delect.motiver.client.AppController;
import com.delect.motiver.client.Motiver;
import com.delect.motiver.client.MyAsyncCallback;
import com.delect.motiver.client.event.FoodCreatedEvent;
import com.delect.motiver.client.event.FoodRemovedEvent;
import com.delect.motiver.client.event.FoodUpdatedEvent;
import com.delect.motiver.client.event.MealCreatedEvent;
import com.delect.motiver.client.event.MealRemovedEvent;
import com.delect.motiver.client.event.MealUpdatedEvent;
import com.delect.motiver.client.event.MealsSelectCancelledEvent;
import com.delect.motiver.client.event.TimeCreatedEvent;
import com.delect.motiver.client.event.TimeRemovedEvent;
import com.delect.motiver.client.event.TimeUpdatedEvent;
import com.delect.motiver.client.event.handler.FoodCreatedEventHandler;
import com.delect.motiver.client.event.handler.FoodRemovedEventHandler;
import com.delect.motiver.client.event.handler.FoodUpdatedEventHandler;
import com.delect.motiver.client.event.handler.MealCreatedEventHandler;
import com.delect.motiver.client.event.handler.MealRemovedEventHandler;
import com.delect.motiver.client.event.handler.MealUpdatedEventHandler;
import com.delect.motiver.client.event.handler.MealsSelectCancelledEventHandler;
import com.delect.motiver.client.presenter.EmptyPresenter;
import com.delect.motiver.client.presenter.EmptyPresenter.EmptyDisplay;
import com.delect.motiver.client.presenter.Presenter;
import com.delect.motiver.client.presenter.nutrition.EmptyTimePresenter.EmptyTimeDisplay;
import com.delect.motiver.client.presenter.nutrition.FoodPresenter.FoodDisplay;
import com.delect.motiver.client.presenter.nutrition.MealPresenter.MealDisplay;
import com.delect.motiver.client.presenter.nutrition.MealsListPresenter.MealsListDisplay;
import com.delect.motiver.client.service.MyServiceAsync;
import com.delect.motiver.client.view.Display;
import com.delect.motiver.client.view.EmptyView;
import com.delect.motiver.client.view.nutrition.EmptyTimeView;
import com.delect.motiver.client.view.nutrition.FoodView;
import com.delect.motiver.client.view.nutrition.MealView;
import com.delect.motiver.client.view.nutrition.MealsListView;
import com.delect.motiver.shared.FoodModel;
import com.delect.motiver.shared.FoodNameModel;
import com.delect.motiver.shared.MealModel;
import com.delect.motiver.shared.TimeModel;
import com.extjs.gxt.ui.client.event.BaseEvent;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.widget.LayoutContainer;
/**
* Shows single time (containing foods/meals)
* @author Antti
*
*/
public class TimePresenter extends Presenter implements Comparable<TimePresenter> {
/**
* Abstract class for view to extend
*/
public abstract static class TimeDisplay extends Display {
public abstract LayoutContainer getBodyContainer();
public abstract void setHandler(TimeHandler timeHandler);
public abstract void setModel(TimeModel time);
}
public interface TimeHandler {
/**
* New food added
* @param food : if not null -> adds copy of this food
*/
void newFood(FoodModel food);
/**
* New meal added
* @param meal : if not null -> adds copy of this meal
*/
void newMeal(MealModel meal);
void timeChanged(int time);
void timeRemoved();
}
//new food handler
public Listener<BaseEvent> NewFoodListener = new Listener<BaseEvent>() {
@Override
public void handleEvent(BaseEvent be) {
addNewFood();
}
};
//new meal handler
public Listener<BaseEvent> NewMealListener = new Listener<BaseEvent>() {
@Override
public void handleEvent(BaseEvent be) {
addNewMeal();
}
};
private TimeDisplay display;
private Presenter emptyPresenter;
//child presenters
private List<Presenter> mealfoodPresenters = new ArrayList<Presenter>();
protected TimeModel time;
public TimePresenter(MyServiceAsync rpcService, SimpleEventBus eventBus, TimeDisplay display, TimeModel time) {
super(rpcService, eventBus);
this.display = display;
this.time = time;
}
@Override
public int compareTo(TimePresenter compare) {
return time.compareTo(compare.time);
}
@Override
public Display getView() {
return display;
}
@Override
public void onBind() {
display.setModel(time);
//event handler (fire event)
display.setHandler(new TimeHandler() {
@Override
public void newFood(FoodModel food) {
//if food -> add copy of that
if(food != null) {
//create copy
FoodModel foodCopy = new FoodModel();
foodCopy.setId(food.getId());
foodCopy.setUid(time.getUser().getUid());
foodCopy.setTimeId(time.getId());
foodCopy.setName(food.getName());
foodCopy.setAmount(food.getAmount());
//add new presenter
addNewFoodPresenter(foodCopy);
}
//show selection
else {
addNewFood();
}
}
@Override
public void newMeal(MealModel meal) {
//if meal -> add copy of that
if(meal != null) {
display.setContentEnabled(false);
//create copy
MealModel mealCopy = new MealModel();
mealCopy.setId(meal.getId());
mealCopy.setTimeId(time.getId());
final Request req = rpcService.addMeal(mealCopy, time.getId(), new MyAsyncCallback<MealModel>() {
@Override
public void onSuccess(MealModel result) {
display.setContentEnabled(true);
result.setTimeId(time.getId());
time.getMeals().add(result);
//fire event
fireEvent(new MealCreatedEvent(result));
}
});
addRequest(req);
}
//show selection
else {
addNewMeal();
}
}
@Override
public void timeChanged(int newtime) {
display.setContentEnabled(false);
time.setTime(newtime);
final Request req = rpcService.updateTime(time, new MyAsyncCallback<Boolean>() {
@Override
public void onSuccess(Boolean result) {
display.setContentEnabled(true);
//fire event
fireEvent(new TimeUpdatedEvent(time));
}
});
addRequest(req);
}
@Override
public void timeRemoved() {
display.setContentEnabled(false);
//remove time and fire TimeRemovedEvent
TimeModel[] times = new TimeModel[] {time};
final Request req = rpcService.removeTimes(times, new MyAsyncCallback<Boolean>() {
@Override
public void onSuccess(Boolean result) {
if(result) {
stop();
//fire event
fireEvent(new TimeRemovedEvent(time));
}
}
});
addRequest(req);
}
});
//EVENT: meal added
addEventHandler(MealCreatedEvent.TYPE, new MealCreatedEventHandler() {
@Override
public void onMealCreated(MealCreatedEvent event) {
//handle event
checkMealEvent(event.getMeal(), 0, event.getSource());
}
});
//EVENT: meal updated
addEventHandler(MealUpdatedEvent.TYPE, new MealUpdatedEventHandler() {
@Override
public void onMealUpdated(MealUpdatedEvent event) {
//handle event
checkMealEvent(event.getMeal(), 1, event.getSource());
}
});
//EVENT: meal removed
addEventHandler(MealRemovedEvent.TYPE, new MealRemovedEventHandler() {
@Override
public void onMealRemoved(MealRemovedEvent event) {
//handle event
checkMealEvent(event.getMeal(), 2, event.getSource());
}
});
//EVENT: food created
addEventHandler(FoodCreatedEvent.TYPE, new FoodCreatedEventHandler() {
@Override
public void onFoodCreated(FoodCreatedEvent event) {
//handle event
checkFoodEvent(event.getFood(), 0, event.getSource());
}
});
//EVENT: food updated
addEventHandler(FoodUpdatedEvent.TYPE, new FoodUpdatedEventHandler() {
@Override
public void onFoodUpdated(FoodUpdatedEvent event) {
checkFoodEvent(event.getFood(), 1, event.getSource());
}
});
//EVENT: food removed
addEventHandler(FoodRemovedEvent.TYPE, new FoodRemovedEventHandler() {
@Override
public void onFoodRemoved(FoodRemovedEvent event) {
//handle event
checkFoodEvent(event.getFood(), 2, event.getSource());
}
});
//EVENT: meal selection cancelled
addEventHandler(MealsSelectCancelledEvent.TYPE, new MealsSelectCancelledEventHandler() {
@Override
public void onCancel(MealsSelectCancelledEvent event) {
//unbind list IF this time
if(event.getTime() != null) {
if(event.getTime().getId() == time.getId()) {
//check list
for(int i=0; i < mealfoodPresenters.size(); i++) {
Presenter presenter = mealfoodPresenters.get(i);
if(presenter != null && presenter instanceof MealsListPresenter) {
mealfoodPresenters.remove(presenter);
break;
}
}
checkIfEmptyPresenterNeeded();
}
}
}
});
}
@Override
public void onRun() {
showContent();
//new time
if(time.getId() == 0) {
display.setContentEnabled(false);
final Request req = rpcService.addTime(time, new MyAsyncCallback<TimeModel>() {
@Override
public void onSuccess(TimeModel result) {
display.setContentEnabled(true);
if(result != null) {
time = result;
display.setModel(time);
//fire event
eventBus.fireEvent(new TimeCreatedEvent(time));
}
//invalid result
else {
stop();
}
}
});
addRequest(req);
}
}
@Override
public void onStop() {
//stop presenters
unbindPresenters();
}
/**
* Adds new food presenter
* @param food : to be updated
*/
private void addNewFoodPresenter(FoodModel food) {
food.setUid(time.getUser().getUid());
food.setTimeId(time.getId());
food.setMealId(0L);
final FoodPresenter wp = new FoodPresenter(rpcService, eventBus, (FoodDisplay)GWT.create(FoodView.class), food);
addNewPresenter(wp);
}
/**
* Adds new meal presenter
* @param meal : to be updated
*/
private void addNewMealPresenter(MealModel meal) {
// meal.setUid(time.getUid());
meal.setTimeId(time.getId());
final MealPresenter wp = new MealPresenter(rpcService, eventBus, (MealDisplay)GWT.create(MealView.class), meal);
addNewPresenter(wp);
}
/**
* Checks how many workouts are and shows empty presenter if needed
*/
private void checkIfEmptyPresenterNeeded() {
//if no meals/foods -> show empty presenter
if(mealfoodPresenters.size() == 0) {
if(time.getUser().equals(AppController.User)) {
emptyPresenter = new EmptyTimePresenter(rpcService, eventBus, (EmptyTimeDisplay)GWT.create(EmptyTimeView.class));
}
else {
emptyPresenter = new EmptyPresenter(rpcService, eventBus, (EmptyDisplay)GWT.create(EmptyView.class), AppController.Lang.NoFoods());
}
emptyPresenter.run(display.getBodyContainer());
}
}
/**
* Removes presenter from view
* @param food
*/
private void removePresenter(FoodModel food) {
//remove also from presenters
for(int i=0; i < mealfoodPresenters.size(); i++) {
Presenter presenter = mealfoodPresenters.get(i);
if(presenter != null && presenter instanceof FoodPresenter && ((FoodPresenter)presenter).food.getId() == food.getId()) {
mealfoodPresenters.remove(presenter);
}
}
checkIfEmptyPresenterNeeded();
}
/**
* Removes presenter from view
* @param meal
*/
private void removePresenter(MealModel meal) {
//remove also from presenters
for(int i=0; i < mealfoodPresenters.size(); i++) {
Presenter presenter = mealfoodPresenters.get(i);
if(presenter != null && presenter instanceof MealPresenter && ((MealPresenter)presenter).meal.getId() == meal.getId()) {
mealfoodPresenters.remove(presenter);
}
}
checkIfEmptyPresenterNeeded();
}
/**
* Unbinds all the meal/time presenters
*/
private void unbindPresenters() {
if(emptyPresenter != null) {
emptyPresenter.stop();
}
if(mealfoodPresenters != null) {
for(int i=0; i < mealfoodPresenters.size(); i++) {
final Presenter presenter = mealfoodPresenters.get(i);
if(presenter != null) {
presenter.stop();
}
}
mealfoodPresenters.clear();
}
}
/**
* Adds new food
*/
protected void addNewFood() {
//create dummy food
final FoodModel foodDummy = new FoodModel(new FoodNameModel(0L, ""));
foodDummy.setTimeId(time.getId());
foodDummy.setUid(time.getUser().getUid());
//init new foodpresenter
final FoodPresenter fp = new FoodPresenter(rpcService, eventBus, (FoodDisplay)GWT.create(FoodView.class), foodDummy);
addNewPresenter(fp);
}
/**
* Shows meals' list
*/
protected void addNewMeal() {
MealsListPresenter mealsListPresenter = new MealsListPresenter(rpcService, eventBus, (MealsListDisplay)GWT.create(MealsListView.class), time, 0L);
addNewPresenter(mealsListPresenter);
}
/**
* Adds new presenter to view
* @param presenter
*/
protected void addNewPresenter(Presenter presenter) {
//remove emptypresenter if present
if(emptyPresenter != null) {
emptyPresenter.stop();
emptyPresenter = null;
}
mealfoodPresenters.add(presenter);
presenter.run(display.getBodyContainer());
}
/**
* Handle food created/updated/removed event
* @param food
* @param target : 0=created, 1=updated, 2=removed
*/
protected void checkFoodEvent(FoodModel foodUpdated, int target, Object source) {
boolean found = false;
//if added to this time
if(foodUpdated.getTimeId() == time.getId()) {
//check if belongs to any food
for(int i=0; i < mealfoodPresenters.size(); i++) {
Presenter presenter = mealfoodPresenters.get(i);
if(presenter != null && presenter instanceof FoodPresenter && presenter.equals(source)) {
FoodModel food = ((FoodPresenter)presenter).food;
//updated
if(target == 1) {
food.setAmount(foodUpdated.getAmount());
food.setName(foodUpdated.getName());
}
//removed
else if(target == 2) {
removePresenter(food);
}
//added
else {
time.getFoods().add(food);
}
found = true;
break;
}
}
//calculate totals
if(found) {
calculateTotals(true);
//highlight if new meal created
if(target == 0) {
highlight();
}
}
}
}
/**
* Handle meal created/updated/removed event
* @param meal
* @param target : 0=created, 1=updated, 2=removed
*/
protected void checkMealEvent(MealModel mealUpdated, int target, Object source) {
boolean found = false;
//if added to this time
if(mealUpdated.getTimeId() == time.getId()) {
if(target == 0) {
//remove list
for(int i=0; i < mealfoodPresenters.size(); i++) {
Presenter presenter = mealfoodPresenters.get(i);
if(presenter != null && presenter instanceof MealsListPresenter) {
presenter.stop();
mealfoodPresenters.remove(presenter);
break;
}
}
time.getMeals().add(mealUpdated);
addNewMealPresenter(mealUpdated);
found = true;
}
else {
//check if belongs to any meal
for(int i=0; i < mealfoodPresenters.size(); i++) {
Presenter presenter = mealfoodPresenters.get(i);
if(presenter != null && presenter instanceof MealPresenter && presenter.equals(source)) {
MealModel meal = ((MealPresenter)presenter).meal;
//removed
if(target == 2) {
removePresenter(meal);
}
found = true;
break;
}
}
}
//calculate totals
if(found) {
calculateTotals(true);
//highlight if new meal created
if(target == 0) {
highlight();
}
}
}
}
/**
* Calculate totals from this time
* @param: fireEvent: whether TimeUpdatedEvent is fired
*/
void calculateTotals(boolean fireEvent) {
if(time == null) {
return;
}
double e = 0;
double p = 0;
double c = 0;
double f = 0;
try {
for(int i=0; i < mealfoodPresenters.size(); i++) {
Presenter presenter = mealfoodPresenters.get(i);
if(presenter != null) {
if(presenter instanceof FoodPresenter) {
FoodModel food = ((FoodPresenter)presenter).food;
if(food.getName() != null && food.getName().getEnergy() > 0) {
final double amount = food.getAmount();
e += (food.getName().getEnergy() / 100) * amount;
p += (food.getName().getProtein() / 100) * amount;
c += (food.getName().getCarb() / 100) * amount;
f += (food.getName().getFet() / 100) * amount;
}
}
else if(presenter instanceof MealPresenter) {
MealModel meal = ((MealPresenter)presenter).meal;
if(meal != null) {
e += meal.getEnergy();
p += meal.getProtein();
c += meal.getCarb();
f += meal.getFet();
}
}
}
}
} catch (Exception e1) {
Motiver.showException(e1);
}
//if changes
if(e != time.getEnergy() || p != time.getProtein() || c != time.getCarb() || f != time.getFet()) {
time.setEnergy(e);
time.setProtein(p);
time.setCarb(c);
time.setFet(f);
//update model
display.setModel(time);
//fire event
if(fireEvent) {
fireEvent(new TimeUpdatedEvent(time));
}
}
}
/**
* shows meals and foods from time model
*/
void showContent() {
display.setContentEnabled(false);
unbindPresenters();
//show foods/meals
if(time.getMeals() != null) {
for(MealModel meal : time.getMeals()) {
addNewMealPresenter(meal);
}
}
if(time.getFoods() != null) {
for(FoodModel food : time.getFoods()) {
addNewFoodPresenter(food);
}
}
display.setContentEnabled(true);
calculateTotals(false);
//if nothing found
boolean found = false;
if(time.getMeals() != null && time.getMeals().size() > 0) {
found = true;
}
if(time.getFoods() != null && time.getFoods().size() > 0) {
found = true;
}
if(!found) {
if(time.getUser().equals(AppController.User)) {
emptyPresenter = new EmptyTimePresenter(rpcService, eventBus, (EmptyTimeDisplay)GWT.create(EmptyTimeView.class));
}
else {
emptyPresenter = new EmptyPresenter(rpcService, eventBus, (EmptyDisplay)GWT.create(EmptyView.class), AppController.Lang.NoFoods());
}
emptyPresenter.run(display.getBodyContainer());
}
}
}