/*******************************************************************************
* 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.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.Window;
import com.google.gwt.user.client.Window.ScrollEvent;
import com.google.gwt.user.client.Window.ScrollHandler;
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.TimeCreatedEvent;
import com.delect.motiver.client.event.TimeRemovedEvent;
import com.delect.motiver.client.event.TimeUpdatedEvent;
import com.delect.motiver.client.event.handler.DateChangedEventHandler;
import com.delect.motiver.client.event.handler.TimeCreatedEventHandler;
import com.delect.motiver.client.event.handler.TimeRemovedEventHandler;
import com.delect.motiver.client.event.handler.TimeUpdatedEventHandler;
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.nutrition.EmptyNutritionDayPresenter.EmptyNutritionDayDisplay;
import com.delect.motiver.client.presenter.nutrition.GuideValuesListPresenter.GuideValuesListDisplay;
import com.delect.motiver.client.presenter.nutrition.NutritionDayDetailsPresenter.NutritionDayDetailsDisplay;
import com.delect.motiver.client.presenter.nutrition.TimePresenter.TimeDisplay;
import com.delect.motiver.client.presenter.nutrition.TotalsContainerPresenter.TotalsContainerDisplay;
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.nutrition.EmptyNutritionDayView;
import com.delect.motiver.client.view.nutrition.GuideValuesListView;
import com.delect.motiver.client.view.nutrition.NutritionDayDetailsView;
import com.delect.motiver.client.view.nutrition.TimeView;
import com.delect.motiver.client.view.nutrition.TotalsContainerView;
import com.delect.motiver.shared.NutritionDayModel;
import com.delect.motiver.shared.TimeModel;
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;
/**
* <pre>
* Init one day in calendar (nutrition)
* - todays foods/meals
* </pre>
*/
public class NutritionDayPresenter extends Presenter {
/**
* Abstract class for view to extend
*/
public abstract static class NutritionDayDisplay extends Display {
public abstract LayoutContainer getBodyContainer();
public abstract LayoutContainer getCommentsContainer();
public abstract LayoutContainer getDetailsContainer();
public abstract LayoutContainer getGuideContainer();
public abstract LayoutContainer getTotalsContainer();
public abstract void onScroll(int scrollTop);
public abstract void removeAllFromTotals();
public abstract void setAddButtonVisible(boolean b);
public abstract void setDate(Date date);
public abstract void setFoodsEnabled(boolean foodsPermission);
public abstract void setHandler(NutritionDayHandler todayNutritionHandler);
public abstract void showContent();
}
public interface NutritionDayHandler {
void detailsHidden();
void detailsVisible();
void newTime();
void removeTimes();
void timesHidden();
void timesVisible();
void copyTimes(Date date);
}
//new time handler
public Listener<BaseEvent> NewTimeListener = new Listener<BaseEvent>() {
@Override
public void handleEvent(BaseEvent be) {
addNewTime();
}
};
private CommentsBoxPresenter commentsPresenter;
private Date date;
private NutritionDayDisplay display;
private Presenter emptyPresenter;
private GuideValuesListPresenter guideValuesPresenter;
private NutritionDayModel nutritionDayModel;
private boolean showGuides;
//child presenters
private List<TimePresenter> timePresenters = new ArrayList<TimePresenter>();
private TotalsContainerPresenter totalsContainerPresenter;
private String uid;
protected NutritionDayDetailsPresenter nutritionDayDetails;
protected List<TimeModel> times = new ArrayList<TimeModel>();
/**
* Loads times from server and shows them by default. Guides are shown
* @param rpcService
* @param eventBus
* @param display
* @param uid : who's foods
* @param date : which date
* @param showGuides : if guide values are shown
*/
public NutritionDayPresenter(MyServiceAsync rpcService, SimpleEventBus eventBus, NutritionDayDisplay display, String uid, Date date) {
super(rpcService, eventBus);
this.display = display;
this.uid = uid;
this.date = date;
showGuides = true;
totalsContainerPresenter = new TotalsContainerPresenter(rpcService, eventBus, (TotalsContainerDisplay)GWT.create(TotalsContainerView.class), uid, date);
if(uid.equals(AppController.User.getUid()) && showGuides) {
guideValuesPresenter = new GuideValuesListPresenter(rpcService, eventBus, (GuideValuesListDisplay)GWT.create(GuideValuesListView.class));
}
}
/**
* Shows total info, but times are not visible. Guide values are not shown
* @param rpcService
* @param eventBus
* @param display
* @param uid : who's foods
* @param date : which date
* @param nutritionDayModel : model containing total calories, protein, ...
*/
public NutritionDayPresenter(MyServiceAsync rpcService, SimpleEventBus eventBus, NutritionDayDisplay display, String uid, Date date, NutritionDayModel nutritionDayModel) {
super(rpcService, eventBus);
this.display = display;
this.uid = uid;
this.date = date;
showGuides = false;
this.nutritionDayModel = nutritionDayModel;
totalsContainerPresenter = new TotalsContainerPresenter(rpcService, eventBus, (TotalsContainerDisplay)GWT.create(TotalsContainerView.class), uid, date);
if(uid.equals(AppController.User.getUid()) && showGuides) {
guideValuesPresenter = new GuideValuesListPresenter(rpcService, eventBus, (GuideValuesListDisplay)GWT.create(GuideValuesListView.class));
}
}
@Override
public Display getView() {
return display;
}
@Override
public void onBind() {
display.setDate(date);
//hide add button if not our times
display.setAddButtonVisible(uid.equals(AppController.User.getUid()));
if(nutritionDayModel != null) {
display.setFoodsEnabled(nutritionDayModel.getFoodsPermission());
}
display.setHandler(new NutritionDayHandler() {
@Override
public void detailsHidden() {
if(nutritionDayDetails != null) {
nutritionDayDetails.stop();
}
}
@Override
public void detailsVisible() {
nutritionDayDetails = new NutritionDayDetailsPresenter(rpcService, eventBus, (NutritionDayDetailsDisplay)GWT.create(NutritionDayDetailsView.class), uid, date);
nutritionDayDetails.run(display.getDetailsContainer());
}
@Override
public void newTime() {
addNewTime();
}
@Override
public void removeTimes() {
display.setContentEnabled(false);
//remove times and fire TimeRemovedEvent
final TimeModel[] arr = new TimeModel[timePresenters.size()];
for(int i=0; i < timePresenters.size(); i++) {
arr[i] = timePresenters.get(i).time;
}
final Request req = rpcService.removeTimes(arr, new MyAsyncCallback<Boolean>() {
@Override
public void onSuccess(Boolean result) {
//stop each presenter
for(int i=0; i < timePresenters.size(); i++) {
timePresenters.get(i).stop();
}
display.setContentEnabled(true);
//fire event for each time
for(TimeModel time : arr) {
fireEvent(new TimeRemovedEvent(time));
}
display.scrollToView();
}
});
addRequest(req);
}
@Override
public void copyTimes(final Date date) {
display.setContentEnabled(false);
//remove times and fire TimeRemovedEvent
final TimeModel[] arr = new TimeModel[timePresenters.size()];
for(int i=0; i < timePresenters.size(); i++) {
arr[i] = timePresenters.get(i).time;
arr[i].setDate(CommonUtils.trimDateToDatabase(date, true));
}
final Request req = rpcService.addTimes(arr, new MyAsyncCallback<TimeModel[]>() {
@Override
public void onSuccess(TimeModel[] result) {
display.setContentEnabled(true);
//fire event
final DateChangedEvent event = new DateChangedEvent(date);
fireEvent(event);
}
});
addRequest(req);
}
@Override
public void timesHidden() {
unbindPresenters();
}
@Override
public void timesVisible() {
loadTimes();
}
});
//EVENT: reload view when date changes
addEventHandler(DateChangedEvent.TYPE, new DateChangedEventHandler() {
@Override
public void onDateChanged(DateChangedEvent event) {
//open parent panel
if(getParent() != null) {
getParent().show();
}
date = event.getDate();
display.setDate(date);
if(commentsPresenter != null) {
commentsPresenter.stop();
}
commentsPresenter = null;
loadTimes();
}
});
//EVENT: time created
addEventHandler(TimeCreatedEvent.TYPE, new TimeCreatedEventHandler() {
@Override
public void onTimeCreated(TimeCreatedEvent event) {
//handle event
checkTimeEvent(event.getTime(), 0);
}
});
//EVENT: time updated
addEventHandler(TimeUpdatedEvent.TYPE, new TimeUpdatedEventHandler() {
@Override
public void onTimeUpdated(TimeUpdatedEvent event) {
//handle event
checkTimeEvent(event.getTime(), 1);
}
});
//EVENT: time removed
addEventHandler(TimeRemovedEvent.TYPE, new TimeRemovedEventHandler() {
@Override
public void onTimeRemoved(TimeRemovedEvent event) {
//handle event
checkTimeEvent(event.getTime(), 2);
}
});
}
@Override
public void onRefresh() {
if(emptyPresenter != null) {
emptyPresenter.run(display.getBodyContainer());
}
if(commentsPresenter != null) {
commentsPresenter.run(display.getBodyContainer());
}
if(timePresenters != null) {
for(int i=0; i < timePresenters.size(); i++) {
final Presenter presenter = timePresenters.get(i);
if(presenter != null) {
presenter.run(display.getBodyContainer());
}
}
}
if(guideValuesPresenter != null) {
guideValuesPresenter.run(display.getGuideContainer());
}
if(totalsContainerPresenter != null) {
totalsContainerPresenter.run(display.getTotalsContainer());
}
}
@Override
public void onRun() {
//guides
if(uid.equals(AppController.User.getUid()) && showGuides) {
guideValuesPresenter.run(display.getGuideContainer());
}
//totals
totalsContainerPresenter.run(display.getTotalsContainer());
//if loading times from server
if(nutritionDayModel == null) {
display.showContent();
}
//just set totals
else {
totalsContainerPresenter.setData(date, nutritionDayModel);
showComments();
}
}
@Override
public void onStop() {
//stop time presenters
unbindPresenters();
if(commentsPresenter != null) {
commentsPresenter.stop();
}
if(nutritionDayDetails != null) {
nutritionDayDetails.stop();
}
if(guideValuesPresenter != null) {
guideValuesPresenter.stop();
}
if(totalsContainerPresenter != null) {
totalsContainerPresenter.stop();
}
}
/**
* Loads times from server (times contains foods/meals)
*/
private void loadTimes() {
unbindPresenters();
//add empty presenter
emptyPresenter = new EmptyPresenter(rpcService, eventBus, (EmptyDisplay)GWT.create(EmptyView.class), EmptyPresenter.EMPTY_LOADING);
emptyPresenter.run(display.getBodyContainer());
//hide add button
display.setAddButtonVisible(false);
//fetch workouts for given day
Motiver.setNextCallCacheable(true);
final Request req = rpcService.getTimesInCalendar(uid, CommonUtils.trimDateToDatabase(date, true), new MyAsyncCallback<List<TimeModel>>() {
@Override
public void onSuccess(List<TimeModel> result) {
times = result;
showTimes();
}
});
addRequest(req);
}
/**
* Removes presenter from view
* @param workout
*/
private void removePresenter(TimeModel time) {
//remove also from presenters
for(int i=0; i < timePresenters.size(); i++) {
TimePresenter presenter = timePresenters.get(i);
if(presenter != null && presenter.time.getId() == time.getId()) {
timePresenters.remove(presenter);
}
}
//if no foods -> show empty presenter
if(timePresenters.size() == 0 && emptyPresenter == null) {
if(uid.equals(AppController.User.getUid())) {
emptyPresenter = new EmptyNutritionDayPresenter(rpcService, eventBus, (EmptyNutritionDayDisplay)GWT.create(EmptyNutritionDayView.class), date);
}
else {
emptyPresenter = new EmptyPresenter(rpcService, eventBus, (EmptyDisplay)GWT.create(EmptyView.class), AppController.Lang.NoTimes());
}
emptyPresenter.run(display.getBodyContainer());
//hide add button
display.setAddButtonVisible(false);
}
}
/**
* Shows comments presenter
*/
private void showComments() {
if(commentsPresenter == null) {
//show comments
commentsPresenter = new CommentsBoxPresenter(rpcService, eventBus, (CommentsBoxDisplay)GWT.create(CommentsBoxView.class), date, uid);
commentsPresenter.run(display.getCommentsContainer());
}
}
/**
* Unbinds all the time presenters
*/
private void unbindPresenters() {
if(emptyPresenter != null) {
emptyPresenter.stop();
}
emptyPresenter = null;
if(timePresenters != null) {
for(int i=0; i < timePresenters.size(); i++) {
final Presenter presenter = timePresenters.get(i);
if(presenter != null) {
presenter.stop();
}
}
timePresenters.clear();
}
}
/**
* Adds new presenter to view
* @param presenter
*/
protected void addNewPresenter(TimePresenter presenter, boolean highlight) {
//remove emptypresenter if present
if(emptyPresenter != null) {
emptyPresenter.stop();
emptyPresenter = null;
}
timePresenters.add(presenter);
presenter.run(display.getBodyContainer());
if(highlight) {
presenter.highlight();
}
//show add button
if(uid.equals(AppController.User.getUid())) {
display.setAddButtonVisible(true);
}
}
/**
* Adds dummy time
*/
@SuppressWarnings("deprecation")
protected void addNewTime() {
try {
display.setContentEnabled(false);
//create dummy time
//get current time
Date today = new Date();
int timeDbl = today.getHours() * 3600 + today.getMinutes() * 60;
//round
timeDbl -= timeDbl % 900;
for(int i=timeDbl; i < 85500; i+=900) {
String str = CommonUtils.getTimeToString(i);
//go through presenters to see if that time exist
if(timePresenters != null) {
boolean found = false;
for(int p=0; p < timePresenters.size(); p++) {
final TimePresenter presenter = timePresenters.get(p);
String strP = CommonUtils.getTimeToString(presenter.time.getTime());
if(str.equals(strP)) {
found = true;
break;
}
if(found) {
break;
}
}
//if time not found -> use that
if(!found) {
timeDbl = i;
break;
}
}
}
display.setContentEnabled(true);
//add new presenter
TimeModel time = new TimeModel(CommonUtils.trimDateToDatabase(date, true), timeDbl);
time.setUser(AppController.User);
final TimePresenter tp = new TimePresenter(rpcService, eventBus, (TimeDisplay)GWT.create(TimeView.class), time);
addNewPresenter(tp, true);
} catch (Exception e) {
Motiver.showException(e);
display.setContentEnabled(true);
}
}
/**
* Handle time updated/removed event
* @param time
* @param target : 0=created, 1=updated, 2=removed
*/
protected void checkTimeEvent(TimeModel timeUpdated, int target) {
try {
//load times if necessary
boolean orderChanged = false;
long lastTime = -1;
int timesFound = 0;
//if created
if(target == 0) {
//add presenter if not found
boolean found = false;
for(TimePresenter tp : timePresenters) {
if(tp.time.getId() == timeUpdated.getId()) {
found = true;
break;
}
}
if(!found) {
final TimePresenter tp = new TimePresenter(rpcService, eventBus, (TimeDisplay)GWT.create(TimeView.class), timeUpdated);
addNewPresenter(tp, false);
}
//check if order changed
for(int i=0; i < timePresenters.size(); i++) {
try {
final TimePresenter presenter = timePresenters.get(i);
if(presenter != null) {
long timeStr = presenter.time.getTime();
//check order
if(lastTime >= timeStr) {
orderChanged = true;
break;
}
lastTime = timeStr;
}
} catch (Exception e) {
Motiver.showException(e);
}
}
//sort times if order changed
if(orderChanged || timesFound > 1) {
//remove everything
try {
for(int i=0; i < timePresenters.size(); i++) {
timePresenters.get(i).remove();
}
//sort
Collections.sort(timePresenters);
//re-add
for(int i=0; i < timePresenters.size(); i++) {
timePresenters.get(i).update(display.getBodyContainer());
}
} catch (Exception e) {
Motiver.showException(e);
}
}
}
//updated
else if(target == 1) {
//check if time are in correct order
for(int i=0; i < timePresenters.size(); i++) {
try {
final TimePresenter presenter = timePresenters.get(i);
if(presenter != null) {
long timeStr = presenter.time.getTime();
//check order
if(lastTime >= timeStr) {
orderChanged = true;
}
//check if more than one same time
if(timeStr == timeUpdated.getTime()) {
timesFound++;
}
lastTime = timeStr;
}
} catch (Exception e) {
Motiver.showException(e);
}
}
//sort times if order changed
if(orderChanged || timesFound > 1) {
//remove everything
try {
for(int i=0; i < timePresenters.size(); i++) {
timePresenters.get(i).remove();
}
//sort
Collections.sort(timePresenters);
//re-add
for(int i=0; i < timePresenters.size(); i++) {
timePresenters.get(i).update(display.getBodyContainer());
}
} catch (Exception e) {
Motiver.showException(e);
}
}
}
//removed
else if(target == 2) {
removePresenter(timeUpdated);
}
} catch (Exception e) {
Motiver.showException(e);
}
}
/**
* Shows times in content (view)
* @param models : workout models to show
*/
protected void showTimes() {
if(times == null) {
return;
}
try {
unbindPresenters();
//if no times
if(times.size() == 0) {
if(uid.equals(AppController.User.getUid())) {
emptyPresenter = new EmptyNutritionDayPresenter(rpcService, eventBus, (EmptyNutritionDayDisplay)GWT.create(EmptyNutritionDayView.class), date);
}
else {
emptyPresenter = new EmptyPresenter(rpcService, eventBus, (EmptyDisplay)GWT.create(EmptyView.class), AppController.Lang.NoTimes());
}
emptyPresenter.run(display.getBodyContainer());
//hide add button
display.setAddButtonVisible(false);
//update totals
totalsContainerPresenter.setData(date, times);
}
else {
//orders
Collections.sort(times);
for(TimeModel m : times) {
//init new timePresenter
final TimePresenter tp = new TimePresenter(rpcService, eventBus, (TimeDisplay)GWT.create(TimeView.class), m);
addNewPresenter(tp, false);
}
//update totals container
totalsContainerPresenter.setData(date, times);
}
showComments();
} catch (Exception e) {
Motiver.showException(e);
}
}
/**
* Enable floating when panel is not visible
* @param enabled
*/
void setFloatingEnabled() {
//TODO ei poisteta mitenk��n?!!?
Window.addWindowScrollHandler(new ScrollHandler() {
public void onWindowScroll(ScrollEvent event) {
display.onScroll(event.getScrollTop());
}
});
}
}