package org.activityinfo.legacy.shared.impl;
import com.bedatadriven.rebar.sql.client.SqlResultCallback;
import com.bedatadriven.rebar.sql.client.SqlResultSet;
import com.bedatadriven.rebar.sql.client.SqlResultSetRow;
import com.bedatadriven.rebar.sql.client.SqlTransaction;
import com.bedatadriven.rebar.sql.client.query.SqlInsert;
import com.bedatadriven.rebar.sql.client.query.SqlQuery;
import com.bedatadriven.rebar.sql.client.query.SqlUpdate;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.collect.Maps;
import com.google.gwt.user.client.rpc.AsyncCallback;
import org.activityinfo.legacy.client.type.DateUtilGWTImpl;
import org.activityinfo.legacy.shared.command.Month;
import org.activityinfo.legacy.shared.command.UpdateMonthlyReports;
import org.activityinfo.legacy.shared.command.result.VoidResult;
import org.activityinfo.legacy.shared.reports.model.DateRange;
import org.activityinfo.model.legacy.KeyGenerator;
import org.activityinfo.promise.Promise;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
public class UpdateMonthlyReportsAsync implements CommandHandlerAsync<UpdateMonthlyReports, VoidResult> {
@Override
public void execute(final UpdateMonthlyReports command, final ExecutionContext context, AsyncCallback<VoidResult> callback) {
queryPeriodMap(command, context).join(new Function<Map<Month, Integer>, Promise<VoidResult>>() {
@Override
public Promise<VoidResult> apply(Map<Month, Integer> input) {
return executeUpdates(context.getTransaction(), command, input);
}
}).then(callback);
}
private Promise<Map<Month, Integer>> queryPeriodMap(UpdateMonthlyReports command, ExecutionContext context) {
final Promise<Map<Month, Integer>> promise = new Promise<>();
SqlQuery.select("reportingPeriodId", "Date2")
.from(Tables.REPORTING_PERIOD, "rp")
.where("siteId").equalTo(command.getSiteId())
.execute(context.getTransaction(), new SqlResultCallback() {
@Override
public void onSuccess(SqlTransaction tx, SqlResultSet results) {
Map<Month, Integer> periodMap = Maps.newHashMap();
for(SqlResultSetRow row : results.getRows()) {
Date endDate = row.getDate("Date2");
Month month = Month.of(endDate);
periodMap.put(month, row.getInt("reportingPeriodId"));
}
promise.resolve(periodMap);
}
});
return promise;
}
private Promise<VoidResult> executeUpdates(SqlTransaction tx, UpdateMonthlyReports command, Map<Month, Integer> periodMap) {
KeyGenerator generator = new KeyGenerator();
List<Promise<Void>> pendingUpdates = new ArrayList<>();
for (UpdateMonthlyReports.Change change : command.getChanges()) {
Integer periodId = periodMap.get(change.getMonth());
if(periodId == null) {
periodId = generator.generateInt();
periodMap.put(change.getMonth(), periodId);
pendingUpdates.add(insertPeriod(tx, command.getSiteId(), periodId, change.getMonth()));
}
pendingUpdates.add(deleteValue(tx, periodId, change.getIndicatorId()));
if(change.getValue() != null) {
pendingUpdates.add(insertValue(tx, periodId, change.getIndicatorId(), change.getValue()));
}
}
return Promise.waitAll(pendingUpdates).then(Functions.<VoidResult>constant(null));
}
private Promise<Void> deleteValue(SqlTransaction tx, Integer periodId, int indicatorId) {
return executeUpdate(tx, SqlUpdate.delete(Tables.INDICATOR_VALUE)
.where("reportingPeriodId", periodId)
.where("indicatorId", indicatorId));
}
private Promise<Void> insertValue(SqlTransaction tx, Integer periodId, int indicatorId, Double value) {
return executeInsert(tx, SqlInsert.insertInto(Tables.INDICATOR_VALUE)
.value("reportingPeriodId", periodId)
.value("indicatorId", indicatorId)
.value("value", value));
}
private Promise<Void> insertPeriod(SqlTransaction tx, int siteId, Integer periodId, Month month) {
DateRange range = DateUtilGWTImpl.INSTANCE.monthRange(month);
SqlInsert query = SqlInsert.insertInto(Tables.REPORTING_PERIOD)
.value("ReportingPeriodId", periodId)
.value("SiteId", siteId)
.value("Date1", range.getMinLocalDate())
.value("Date2", range.getMaxLocalDate())
.value("DateCreated", new Date())
.value("DateEdited", new Date());
return executeInsert(tx, query);
}
private Promise<Void> executeUpdate(SqlTransaction tx, SqlUpdate update) {
final Promise<Void> promise = new Promise<>();
update.execute(tx, new SqlResultCallback() {
@Override
public void onSuccess(SqlTransaction tx, SqlResultSet results) {
promise.resolve(null);
}
});
return promise;
}
private Promise<Void> executeInsert(SqlTransaction tx, SqlInsert query) {
final Promise<Void> promise = new Promise<>();
query
.execute(tx, new AsyncCallback<Integer>() {
@Override
public void onFailure(Throwable caught) {
promise.reject(caught);
}
@Override
public void onSuccess(Integer result) {
promise.resolve(null);
}
});
return promise;
}
}