package co.smartreceipts.android.model.impl;
import android.content.Context;
import android.os.Parcel;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.google.common.base.Preconditions;
import java.io.File;
import java.sql.Date;
import java.util.Calendar;
import java.util.Currency;
import java.util.Locale;
import java.util.TimeZone;
import co.smartreceipts.android.filters.Filter;
import co.smartreceipts.android.model.Price;
import co.smartreceipts.android.model.PriceCurrency;
import co.smartreceipts.android.model.Receipt;
import co.smartreceipts.android.model.Source;
import co.smartreceipts.android.model.Trip;
import co.smartreceipts.android.model.factory.PriceBuilderFactory;
import co.smartreceipts.android.model.utils.ModelUtils;
import co.smartreceipts.android.sync.model.SyncState;
public class DefaultTripImpl implements Trip {
private final File mReportDirectory;
private final String mComment;
private final Date mStartDate;
private final TimeZone mStartTimeZone;
private final Date mEndDate;
private final TimeZone mEndTimeZone;
private final PriceCurrency mDefaultCurrency;
private final String mCostCenter;
private final SyncState mSyncState;
private Price mPrice;
private Price mDailySubTotal;
private final Source mSource;
private final Filter<Receipt> mFilter = null;
public DefaultTripImpl(@NonNull File directory, @NonNull Date startDate, @NonNull TimeZone startTimeZone, @NonNull Date endDate, @NonNull TimeZone endTimeZone,
@NonNull PriceCurrency defaultCurrency, @NonNull String comment, @NonNull String costCenter, @Nullable Source source, @NonNull SyncState syncState) {
mReportDirectory = Preconditions.checkNotNull(directory);
mStartDate = Preconditions.checkNotNull(startDate);
mStartTimeZone = Preconditions.checkNotNull(startTimeZone);
mEndDate = Preconditions.checkNotNull(endDate);
mEndTimeZone = Preconditions.checkNotNull(endTimeZone);
mDefaultCurrency = Preconditions.checkNotNull(defaultCurrency);
mComment = Preconditions.checkNotNull(comment);
mCostCenter = Preconditions.checkNotNull(costCenter);
mSource = source != null ? source : Source.Undefined;
mSyncState = Preconditions.checkNotNull(syncState);
// Sets a simple default for price and daily of 0
mPrice = new PriceBuilderFactory().setPrice(0).setCurrency(defaultCurrency).build();
mDailySubTotal = new PriceBuilderFactory().setPrice(0).setCurrency(defaultCurrency).build();
}
private DefaultTripImpl(Parcel in) {
mReportDirectory = new File(in.readString());
mPrice = in.readParcelable(Price.class.getClassLoader());
mStartDate = new Date(in.readLong());
mEndDate = new Date(in.readLong());
mStartTimeZone = TimeZone.getTimeZone(in.readString());
mEndTimeZone = TimeZone.getTimeZone(in.readString());
mDailySubTotal = in.readParcelable(Price.class.getClassLoader());
mComment = in.readString();
mCostCenter = in.readString();
mDefaultCurrency = PriceCurrency.getInstance(in.readString());
mSyncState = in.readParcelable(SyncState.class.getClassLoader());
mSource = Source.Parcel;
}
@Override
@NonNull
public String getName() {
return mReportDirectory.getName();
}
@Override
@NonNull
public File getDirectory() {
return mReportDirectory;
}
@NonNull
@Override
public String getDirectoryPath() {
return mReportDirectory.getAbsolutePath();
}
@NonNull
@Override
public Date getStartDate() {
return mStartDate;
}
@NonNull
@Override
public String getFormattedStartDate(Context context, String separator) {
return ModelUtils.getFormattedDate(mStartDate, getStartTimeZone(), context, separator);
}
@NonNull
@Override
public TimeZone getStartTimeZone() {
return (mStartTimeZone != null) ? mStartTimeZone : TimeZone.getDefault();
}
@NonNull
@Override
public Date getEndDate() {
return mEndDate;
}
@NonNull
@Override
public String getFormattedEndDate(Context context, String separator) {
return ModelUtils.getFormattedDate(mEndDate, getEndTimeZone(), context, separator);
}
@NonNull
@Override
public TimeZone getEndTimeZone() {
return (mEndTimeZone != null) ? mEndTimeZone : TimeZone.getDefault();
}
/**
* Tests if a particular date is included with the bounds of this particular trip When performing the test, it uses
* the local time zone for the date, and the defined time zones for the start and end date bounds. The start date
* time is assumed to occur at 00:01 of the start day and the end date is assumed to occur at 23:59 of the end day.
* The reasoning behind this is to ensure that it appears properly from a UI perspective. Since the initial date
* only shows the day itself, it may include an arbitrary time that is never shown to the user. Setting the time
* aspect manually accounts for this. This returns false if the date is null.
*
* @param date - the date to test
* @return true if it is contained within. false otherwise
*/
@Override
public boolean isDateInsideTripBounds(@Nullable Date date) {
if (date == null) {
return false;
}
// Build a calendar for the date we intend to test
Calendar testCalendar = Calendar.getInstance();
testCalendar.setTime(date);
testCalendar.setTimeZone(TimeZone.getDefault());
// Build a calendar for the start date
Calendar startCalendar = Calendar.getInstance();
startCalendar.setTime(mStartDate);
startCalendar.setTimeZone((mStartTimeZone != null) ? mStartTimeZone : TimeZone.getDefault());
startCalendar.set(Calendar.HOUR_OF_DAY, 0);
startCalendar.set(Calendar.MINUTE, 0);
startCalendar.set(Calendar.SECOND, 0);
startCalendar.set(Calendar.MILLISECOND, 0);
// Build a calendar for the end date
Calendar endCalendar = Calendar.getInstance();
endCalendar.setTime(mEndDate);
endCalendar.setTimeZone((mEndTimeZone != null) ? mEndTimeZone : TimeZone.getDefault());
endCalendar.set(Calendar.HOUR_OF_DAY, 23);
endCalendar.set(Calendar.MINUTE, 59);
endCalendar.set(Calendar.SECOND, 59);
endCalendar.set(Calendar.MILLISECOND, 999);
if (testCalendar.compareTo(startCalendar) >= 0 && testCalendar.compareTo(endCalendar) <= 0) {
return true;
} else {
return false;
}
}
@Override
@NonNull
public PriceCurrency getTripCurrency() {
return mDefaultCurrency;
}
@Override
@NonNull
public String getDefaultCurrencyCode() {
if (mDefaultCurrency != null) {
return mDefaultCurrency.getCurrencyCode();
} else {
return Currency.getInstance(Locale.getDefault()).getCurrencyCode();
}
}
@NonNull
@Override
public String getComment() {
return mComment;
}
@NonNull
@Override
public String getCostCenter() {
return mCostCenter;
}
@NonNull
@Override
public Source getSource() {
return mSource;
}
@NonNull
@Override
public Price getPrice() {
return mPrice;
}
@Override
public void setPrice(@NonNull Price price) {
mPrice = price;
}
@NonNull
@Override
public Price getDailySubTotal() {
return mDailySubTotal;
}
@Override
public void setDailySubTotal(@NonNull Price dailyTotal) {
mDailySubTotal = dailyTotal;
}
@Nullable
@Override
public Filter<Receipt> getFilter() {
return mFilter;
}
@NonNull
@Override
public SyncState getSyncState() {
return mSyncState;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof DefaultTripImpl)) return false;
DefaultTripImpl that = (DefaultTripImpl) o;
if (!mReportDirectory.equals(that.mReportDirectory)) return false;
if (!mComment.equals(that.mComment)) return false;
if (!mStartDate.equals(that.mStartDate)) return false;
if (!mStartTimeZone.equals(that.mStartTimeZone)) return false;
if (!mEndDate.equals(that.mEndDate)) return false;
if (!mEndTimeZone.equals(that.mEndTimeZone)) return false;
if (!mDefaultCurrency.equals(that.mDefaultCurrency)) return false;
return (mCostCenter.equals(that.mCostCenter));
}
@Override
public int hashCode() {
int result = mReportDirectory.hashCode();
result = 31 * result + mComment.hashCode();
result = 31 * result + mStartDate.hashCode();
result = 31 * result + mStartTimeZone.hashCode();
result = 31 * result + mEndDate.hashCode();
result = 31 * result + mEndTimeZone.hashCode();
result = 31 * result + mDefaultCurrency.hashCode();
result = 31 * result + mCostCenter.hashCode();
return result;
}
@Override
public String toString() {
return "DefaultTripImpl{" +
"mReportDirectory=" + mReportDirectory +
", mComment='" + mComment + '\'' +
", mCostCenter='" + mCostCenter + '\'' +
", mPrice=" + mPrice +
", mDailySubTotal=" + mDailySubTotal +
", mStartDate=" + mStartDate +
", mEndDate=" + mEndDate +
", mStartTimeZone=" + mStartTimeZone +
", mEndTimeZone=" + mEndTimeZone +
", mDefaultCurrency=" + mDefaultCurrency +
", mSource=" + mSource +
", mFilter=" + mFilter +
'}';
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(getDirectoryPath());
dest.writeParcelable(getPrice(), flags);
dest.writeLong(getStartDate().getTime());
dest.writeLong(getEndDate().getTime());
dest.writeString(getStartTimeZone().getID());
dest.writeString(getEndTimeZone().getID());
dest.writeParcelable(getDailySubTotal(), flags);
dest.writeString(getComment());
dest.writeString(getCostCenter());
dest.writeString(getDefaultCurrencyCode());
dest.writeParcelable(getSyncState(), flags);
}
public static final Creator<DefaultTripImpl> CREATOR = new Creator<DefaultTripImpl>() {
@Override
public DefaultTripImpl createFromParcel(Parcel source) {
return new DefaultTripImpl(source);
}
@Override
public DefaultTripImpl[] newArray(int size) {
return new DefaultTripImpl[size];
}
};
@Override
public int compareTo(@NonNull Trip trip) {
return trip.getEndDate().compareTo(mEndDate);
}
}