/*
* Copyright (c) 2012 Denis Solonenko.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*/
package ru.orangesoftware.financisto2.utils;
import android.database.Cursor;
import ru.orangesoftware.financisto2.filter.WhereFilter;
import ru.orangesoftware.financisto2.db.DatabaseAdapter;
import ru.orangesoftware.financisto2.model.*;
import ru.orangesoftware.financisto2.recur.Recurrence;
import java.util.*;
/**
* Created by IntelliJ IDEA.
* User: Denis Solonenko
* Date: 8/25/11 11:00 PM
*/
public abstract class AbstractPlanner {
protected final DatabaseAdapter db;
protected final WhereFilter filter;
protected final Date now;
public AbstractPlanner(DatabaseAdapter db, WhereFilter filter, Date now) {
this.db = db;
this.filter = filter;
this.now = now;
}
public TransactionList getPlannedTransactionsWithTotals() {
List<TransactionInfo> transactions = getPlannedTransactions();
Total[] totals = calculateTotals(transactions);
return new TransactionList(transactions, totals);
}
public List<TransactionInfo> getPlannedTransactions() {
List<TransactionInfo> regularTransactions = asTransactionList(getRegularTransactions());
List<TransactionInfo> scheduledTransactions = getScheduledTransactions();
if (scheduledTransactions.isEmpty()) {
return regularTransactions;
} else {
List<TransactionInfo> allTransactions = new ArrayList<TransactionInfo>();
allTransactions.addAll(regularTransactions);
allTransactions.addAll(planSchedules(scheduledTransactions));
sortTransactions(allTransactions);
return allTransactions;
}
}
protected abstract Total[] calculateTotals(List<TransactionInfo> transactions);
protected abstract Cursor getRegularTransactions();
private List<TransactionInfo> getScheduledTransactions() {
return db.getAllScheduledTransactions();
}
private List<TransactionInfo> planSchedules(List<TransactionInfo> scheduledTransactions) {
List<TransactionInfo> plannedTransactions = new ArrayList<TransactionInfo>();
for (TransactionInfo scheduledTransaction : scheduledTransactions) {
TransactionInfo transaction = prepareScheduledTransaction(scheduledTransaction);
if (includeScheduledTransaction(transaction)) {
List<Date> dates = calculatePlannedDates(transaction);
duplicateTransaction(transaction, dates, plannedTransactions);
} else if (transaction.isSplitParent()) {
planSplitSchedules(transaction, plannedTransactions);
}
}
return plannedTransactions;
}
private void planSplitSchedules(TransactionInfo scheduledTransaction, List<TransactionInfo> plannedTransactions) {
List<Date> dates = calculatePlannedDates(scheduledTransaction);
List<TransactionInfo> splits = db.getSplitsInfoForTransaction(scheduledTransaction.id);
for (TransactionInfo split : splits) {
if (includeScheduledSplitTransaction(split)) {
TransactionInfo transaction = prepareScheduledTransaction(split);
duplicateTransaction(transaction, dates, plannedTransactions);
}
}
}
protected abstract TransactionInfo prepareScheduledTransaction(TransactionInfo scheduledTransaction);
protected abstract boolean includeScheduledTransaction(TransactionInfo transaction);
protected abstract boolean includeScheduledSplitTransaction(TransactionInfo split);
private List<Date> calculatePlannedDates(TransactionInfo scheduledTransaction) {
String recurrence = scheduledTransaction.recurrence;
Date startDate = getStartDateFromFilter();
Date endDate = getEndDateFromFilter();
Date calcDate = startDate.before(now) ? now : startDate;
if (recurrence == null) {
Date scheduledDate = new Date(scheduledTransaction.dateTime);
if (insideTheRequiredPeriod(calcDate, endDate, scheduledDate)) {
return Collections.singletonList(scheduledDate);
}
} else {
Recurrence r = Recurrence.parse(recurrence);
return r.generateDates(calcDate, endDate);
}
return Collections.emptyList();
}
private Date getStartDateFromFilter() {
return new Date(filter.getDateTime().getPeriod().start);
}
private Date getEndDateFromFilter() {
return new Date(filter.getDateTime().getPeriod().end);
}
private boolean insideTheRequiredPeriod(Date startDate, Date endDate, Date date) {
return !(date.before(startDate) || date.after(endDate));
}
private void duplicateTransaction(TransactionInfo scheduledTransaction, List<Date> dates, List<TransactionInfo> plannedTransactions) {
if (dates.size() == 1) {
scheduledTransaction.dateTime = dates.get(0).getTime();
scheduledTransaction.isTemplate = 0;
plannedTransactions.add(scheduledTransaction);
} else {
for (Date date : dates) {
TransactionInfo t = scheduledTransaction.clone();
t.dateTime = date.getTime();
t.isTemplate = 0;
plannedTransactions.add(t);
}
}
}
private void sortTransactions(List<TransactionInfo> transactions) {
Collections.sort(transactions, createSortComparator());
}
protected Comparator<TransactionInfo> createSortComparator() {
return new Comparator<TransactionInfo>() {
@Override
public int compare(TransactionInfo transaction1, TransactionInfo transaction2) {
return transaction1.dateTime > transaction2.dateTime ? 1 : (transaction1.dateTime < transaction2.dateTime ? -1 : 0);
}
};
}
private List<TransactionInfo> asTransactionList(Cursor cursor) {
try {
List<TransactionInfo> transactions = new ArrayList<TransactionInfo>(cursor.getCount());
while (cursor.moveToNext()) {
transactions.add(TransactionInfo.fromBlotterCursor(cursor));
}
return transactions;
} finally {
cursor.close();
}
}
}