/******************************************************************************* * 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.handler.FoodCreatedEventHandler; import com.delect.motiver.client.event.handler.FoodRemovedEventHandler; import com.delect.motiver.client.event.handler.FoodUpdatedEventHandler; 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.Presenter; import com.delect.motiver.client.presenter.UserPresenter; import com.delect.motiver.client.presenter.UserPresenter.UserDisplay; import com.delect.motiver.client.presenter.nutrition.FoodPresenter.FoodDisplay; 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.UserView; import com.delect.motiver.client.view.nutrition.FoodView; import com.delect.motiver.shared.FoodModel; import com.delect.motiver.shared.FoodNameModel; import com.delect.motiver.shared.MealModel; import com.extjs.gxt.ui.client.widget.LayoutContainer; /** * Shows single meal (name and foods) * If new meal (no ID) shows selector panel * Events: - MealCreatedEvent (after creating new model) * - MealRemovedEvent (after removing or cancelling selection) * */ public class MealPresenter extends Presenter { /** * Abstract class for view to extend */ public abstract static class MealDisplay extends Display { public abstract LayoutContainer getBodyContainer(); public abstract LayoutContainer getCommentsContainer(); public abstract LayoutContainer getUserContainer(); public abstract void setHandler(MealHandler mealHandler); public abstract void setModel(MealModel meal); } public interface MealHandler { void foodsHidden(); void foodsVisible(); //called when meal visible (we load foods then) void mealRemoved(); /** * New food added * @param food : if not null -> adds copy of this food */ void newFood(FoodModel food); void saveData(MealModel meal); } public interface MealSelectedHandler { void select(MealModel model); } private CommentsBoxPresenter commentsPresenter; private MealDisplay display; private EmptyPresenter emptyPresenter; //child presenters private List<FoodPresenter> foodPresenters = new ArrayList<FoodPresenter>(); private UserPresenter userPresenter; protected MealModel meal; public MealPresenter(MyServiceAsync rpcService, SimpleEventBus eventBus, MealDisplay display, MealModel meal) { super(rpcService, eventBus); this.display = display; this.meal = meal; } @Override public Display getView() { return display; } @Override public void onBind() { display.setModel(meal); //Event handlers //Launch MealUpdatedEvent if meal (content) changes display.setHandler(new MealHandler() { @Override public void foodsHidden() { //stop presenters unbindPresenters(); } @Override public void foodsVisible() { showFoods(); } @Override public void mealRemoved() { display.setContentEnabled(false); //remove meal and fire MealRemovedEvent final Request req = rpcService.removeMeal(meal, new MyAsyncCallback<Boolean>() { @Override public void onSuccess(Boolean result) { if(result) { stop(); fireEvent(new MealRemovedEvent(meal)); } } }); addRequest(req); } @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.setMealId(meal.getId()); foodCopy.setTimeId(meal.getTimeId()); foodCopy.setUid(meal.getUser().getUid()); foodCopy.setName(food.getName()); foodCopy.setAmount(food.getAmount()); meal.getFoods().add(foodCopy); //add new presenter addNewFoodPresenter(foodCopy); } //create dummy food else { final FoodModel foodDummy = new FoodModel(new FoodNameModel(0L, "")); addNewFoodPresenter(foodDummy); } } @Override public void saveData(MealModel model) { //if cancelled adding new meal if(model == null) { stop(); //fire event fireEvent(new MealRemovedEvent(meal)); } //added new meal else if(model.getId() == 0) { meal = model; display.setContentEnabled(false); //create model final Request req = rpcService.addMeal(model, model.getTimeId(), new MyAsyncCallback<MealModel>() { @Override public void onSuccess(MealModel result) { display.setContentEnabled(true); meal = result; refresh(); //fire event final MealCreatedEvent event = new MealCreatedEvent(meal); fireEvent(event); } }); addRequest(req); } //edited old value else { meal = model; final Request req = rpcService.updateMeal(meal, new MyAsyncCallback<Boolean>() { @Override public void onSuccess(Boolean result) { fireEvent(new MealUpdatedEvent(meal)); } }); addRequest(req); } } }); //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) { //handle event checkFoodEvent(event.getFood(), 1, event.getSource()); } }); //EVENT: food removed addEventHandler(FoodRemovedEvent.TYPE, new FoodRemovedEventHandler() { @Override public void onFoodRemoved(FoodRemovedEvent event) { checkFoodEvent(event.getFood(), 2, event.getSource()); } }); } @Override public void onRun() { if(meal.getId() != 0) { showFoods(); } //no model -> highlight else { highlight(); } } @Override public void onStop() { unbindPresenters(); if(userPresenter != null) { userPresenter.stop(); } } /** * Adds new food presenter * @param food : to be updated */ private void addNewFoodPresenter(FoodModel food) { food.setMealId(meal.getId()); food.setTimeId(meal.getTimeId()); food.setUid(meal.getUser().getUid()); final FoodPresenter wp = new FoodPresenter(rpcService, eventBus, (FoodDisplay)GWT.create(FoodView.class), food); addNewPresenter(wp); } /** * Removes presenter from view * @param meal */ private void removePresenter(FoodModel food) { //remove also from presenters for(int i=0; i < foodPresenters.size(); i++) { FoodPresenter presenter = foodPresenters.get(i); if(presenter != null && presenter.food.getId() == food.getId()) { foodPresenters.remove(presenter); } } //if no meals/foods -> show empty presenter if(foodPresenters.size() == 0) { emptyPresenter = new EmptyPresenter(rpcService, eventBus, (EmptyDisplay)GWT.create(EmptyView.class), AppController.Lang.NoFoods()); emptyPresenter.run(display.getBodyContainer()); } } /** * Unbinds all the meal/time presenters */ private void unbindPresenters() { if(emptyPresenter != null) { emptyPresenter.stop(); } if(commentsPresenter != null) { commentsPresenter.stop(); } if(foodPresenters != null) { for(int i=0; i < foodPresenters.size(); i++) { final Presenter presenter = foodPresenters.get(i); if(presenter != null) { presenter.stop(); } } foodPresenters.clear(); } } /** * Adds new presenter to view * @param presenter */ protected void addNewPresenter(FoodPresenter presenter) { //remove emptypresenter if present if(emptyPresenter != null) { emptyPresenter.stop(); emptyPresenter = null; } foodPresenters.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.getMealId() == meal.getId()) { //check if belongs to any food for(int i=0; i < foodPresenters.size(); i++) { FoodPresenter presenter = foodPresenters.get(i); if(presenter != null && presenter.equals(source)) { FoodModel model = presenter.food; //updated if(target == 1) { model.setAmount(foodUpdated.getAmount()); model.setName(foodUpdated.getName()); } //removed else if(target == 2) { removePresenter(model); } found = true; break; } } //calculate totals if(found) { calculateTotals(true); //highlight if(target == 0) { highlight(); } } } } /** * Shows foods */ protected void showFoods() { try { unbindPresenters(); //userview if(meal.getTimeId() == 0) { if(userPresenter != null) { userPresenter.stop(); } //show user if not our workout if(!meal.getUser().equals(AppController.User)) { userPresenter = new UserPresenter(rpcService, eventBus, (UserDisplay) GWT.create(UserView.class), meal.getUser(), false); userPresenter.run(display.getUserContainer()); } } //if no meals if(meal.getFoods().size() == 0) { emptyPresenter = new EmptyPresenter(rpcService, eventBus, (EmptyDisplay)GWT.create(EmptyView.class), AppController.Lang.NoFoods()); emptyPresenter.run(display.getBodyContainer()); } else { for(FoodModel m : meal.getFoods()) { m.setUid(meal.getUser().getUid()); m.setTimeId(meal.getTimeId()); m.setMealId(meal.getId()); final FoodPresenter fp = new FoodPresenter(rpcService, eventBus, (FoodDisplay)GWT.create(FoodView.class), m); addNewPresenter(fp); } } //show comments (only if not in any time) if(meal.getTimeId() == 0 && commentsPresenter != null) { commentsPresenter.stop(); commentsPresenter = new CommentsBoxPresenter(rpcService, eventBus, (CommentsBoxDisplay)GWT.create(CommentsBoxView.class), meal); commentsPresenter.run(display.getCommentsContainer()); } calculateTotals(false); } catch (Exception e) { Motiver.showException(e); } } /** * Calculate totals from this time * Fires event if time content changes */ void calculateTotals(boolean fireEvent) { //if not in any time if(meal.getId() == 0) { return; } double e = 0D; double p = 0; double c = 0; double f = 0; try { for(int i=0; i < foodPresenters.size(); i++) { Presenter presenter = foodPresenters.get(i); if(presenter != null) { if(presenter instanceof FoodPresenter) { FoodModel model = ((FoodPresenter)presenter).food; if(model.getName() != null && model.getName().getEnergy() > 0) { final double amount = model.getAmount(); e += (model.getName().getEnergy() / 100) * amount; p += (model.getName().getProtein() / 100) * amount; c += (model.getName().getCarb() / 100) * amount; f += (model.getName().getFet() / 100) * amount; } } } } } catch (Exception e1) { Motiver.showException(e1); } //if changes if(Double.compare(e, meal.getEnergy()) != 0 || Double.compare(p, meal.getProtein()) != 0 || Double.compare(c, meal.getCarb()) != 0 || Double.compare(f, meal.getFet()) != 0 ) { meal.setEnergy(e); meal.setProtein(p); meal.setCarb(c); meal.setFet(f); //update model display.setModel(meal); //fire event if(fireEvent) { fireEvent(new MealUpdatedEvent(meal)); } } } }