/*
* Copyright 2010-2013 Ning, Inc.
*
* Ning licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.ning.billing.entitlement.engine.core;
import java.util.List;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import com.ning.billing.account.api.Account;
import com.ning.billing.callcontext.InternalTenantContext;
import com.ning.billing.entitlement.api.BlockingState;
import com.ning.billing.entitlement.api.BlockingStateType;
import com.ning.billing.entitlement.api.DefaultEntitlementApi;
import com.ning.billing.entitlement.api.Entitlement.EntitlementState;
import com.ning.billing.entitlement.block.BlockingChecker.BlockingAggregator;
import com.ning.billing.subscription.api.SubscriptionBase;
import com.ning.billing.subscription.api.SubscriptionBaseTransitionType;
import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
import com.ning.billing.subscription.api.user.SubscriptionBaseTransition;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
public class EventsStream {
private final Account account;
private final SubscriptionBaseBundle bundle;
private final List<BlockingState> subscriptionEntitlementStates;
private final List<BlockingState> bundleEntitlementStates;
private final List<BlockingState> accountEntitlementStates;
private final BlockingAggregator blockingAggregator;
private final SubscriptionBase baseSubscription;
private final SubscriptionBase subscription;
private final InternalTenantContext internalTenantContext;
private final DateTime utcNow;
private LocalDate entitlementEffectiveEndDate;
private BlockingState entitlementCancelEvent;
private EntitlementState entitlementState;
public EventsStream(final Account account, final SubscriptionBaseBundle bundle,
final List<BlockingState> subscriptionEntitlementStates, final List<BlockingState> bundleEntitlementStates,
final List<BlockingState> accountEntitlementStates, final BlockingAggregator blockingAggregator,
final SubscriptionBase baseSubscription, final SubscriptionBase subscription,
final InternalTenantContext contextWithValidAccountRecordId, final DateTime utcNow) {
this.account = account;
this.bundle = bundle;
this.subscriptionEntitlementStates = subscriptionEntitlementStates;
this.bundleEntitlementStates = bundleEntitlementStates;
this.accountEntitlementStates = accountEntitlementStates;
this.blockingAggregator = blockingAggregator;
this.baseSubscription = baseSubscription;
this.subscription = subscription;
this.internalTenantContext = contextWithValidAccountRecordId;
this.utcNow = utcNow;
setup();
}
public Account getAccount() {
return account;
}
public SubscriptionBaseBundle getBundle() {
return bundle;
}
public SubscriptionBase getSubscription() {
return subscription;
}
public InternalTenantContext getInternalTenantContext() {
return internalTenantContext;
}
public LocalDate getEntitlementEffectiveEndDate() {
return entitlementEffectiveEndDate;
}
public BlockingState getEntitlementCancelEvent() {
return entitlementCancelEvent;
}
public EntitlementState getEntitlementState() {
return entitlementState;
}
public BlockingAggregator getCurrentBlockingAggregator() {
return blockingAggregator;
}
public boolean isFutureEntitlementCancelled() {
return entitlementCancelEvent != null && entitlementCancelEvent.getEffectiveDate().isAfter(utcNow);
}
public boolean isEntitlementActive() {
return entitlementState == EntitlementState.ACTIVE;
}
public boolean isEntitlementCancelled() {
return entitlementState == EntitlementState.CANCELLED;
}
public boolean isSubscriptionCancelled() {
return subscription.getState() == EntitlementState.CANCELLED;
}
public Iterable<BlockingState> getPendingEntitlementCancellationEvents() {
return getPendingEntitlementEvents(DefaultEntitlementApi.ENT_STATE_CANCELLED);
}
public Iterable<BlockingState> getPendingEntitlementEvents(final String... types) {
final List<String> typeList = ImmutableList.<String>copyOf(types);
return Iterables.<BlockingState>filter(subscriptionEntitlementStates,
new Predicate<BlockingState>() {
@Override
public boolean apply(final BlockingState input) {
return input.getEffectiveDate().isAfter(utcNow) &&
typeList.contains(input.getStateName()) &&
(
// ... for that subscription
BlockingStateType.SUBSCRIPTION.equals(input.getType()) && input.getBlockedId().equals(subscription.getId()) ||
// ... for the associated base subscription
BlockingStateType.SUBSCRIPTION.equals(input.getType()) && input.getBlockedId().equals(baseSubscription.getId()) ||
// ... for that bundle
BlockingStateType.SUBSCRIPTION_BUNDLE.equals(input.getType()) && input.getBlockedId().equals(bundle.getId()) ||
// ... for that account
BlockingStateType.ACCOUNT.equals(input.getType()) && input.getBlockedId().equals(account.getId())
);
}
});
}
public Iterable<SubscriptionBaseTransition> getPendingSubscriptionEvents(final SubscriptionBaseTransitionType... types) {
final List<SubscriptionBaseTransitionType> typeList = ImmutableList.<SubscriptionBaseTransitionType>copyOf(types);
return Iterables.<SubscriptionBaseTransition>filter(subscription.getAllTransitions(),
new Predicate<SubscriptionBaseTransition>() {
@Override
public boolean apply(final SubscriptionBaseTransition input) {
return input.getEffectiveTransitionTime().isAfter(utcNow) &&
typeList.contains(input.getTransitionType());
}
});
}
private void setup() {
computeEntitlementEffectiveEndDate();
computeEntitlementCancelEvent();
computeStateForEntitlement();
}
private void computeEntitlementEffectiveEndDate() {
LocalDate result = null;
BlockingState lastEntry;
lastEntry = (!subscriptionEntitlementStates.isEmpty()) ? subscriptionEntitlementStates.get(subscriptionEntitlementStates.size() - 1) : null;
if (lastEntry != null && DefaultEntitlementApi.ENT_STATE_CANCELLED.equals(lastEntry.getStateName())) {
result = new LocalDate(lastEntry.getEffectiveDate(), account.getTimeZone());
}
lastEntry = (!bundleEntitlementStates.isEmpty()) ? bundleEntitlementStates.get(bundleEntitlementStates.size() - 1) : null;
if (lastEntry != null && DefaultEntitlementApi.ENT_STATE_CANCELLED.equals(lastEntry.getStateName())) {
final LocalDate localDate = new LocalDate(lastEntry.getEffectiveDate(), account.getTimeZone());
result = ((result == null) || (localDate.compareTo(result) < 0)) ? localDate : result;
}
lastEntry = (!accountEntitlementStates.isEmpty()) ? accountEntitlementStates.get(accountEntitlementStates.size() - 1) : null;
if (lastEntry != null && DefaultEntitlementApi.ENT_STATE_CANCELLED.equals(lastEntry.getStateName())) {
final LocalDate localDate = new LocalDate(lastEntry.getEffectiveDate(), account.getTimeZone());
result = ((result == null) || (localDate.compareTo(result) < 0)) ? localDate : result;
}
entitlementEffectiveEndDate = result;
}
private void computeEntitlementCancelEvent() {
entitlementCancelEvent = Iterables.<BlockingState>tryFind(subscriptionEntitlementStates,
new Predicate<BlockingState>() {
@Override
public boolean apply(final BlockingState input) {
return DefaultEntitlementApi.ENT_STATE_CANCELLED.equals(input.getStateName());
}
}).orNull();
}
private void computeStateForEntitlement() {
// Current state for the ENTITLEMENT_SERVICE_NAME is set to cancelled
if (entitlementEffectiveEndDate != null && entitlementEffectiveEndDate.compareTo(new LocalDate(utcNow, account.getTimeZone())) <= 0) {
entitlementState = EntitlementState.CANCELLED;
} else {
// Gather states across all services and check if one of them is set to 'blockEntitlement'
entitlementState = (blockingAggregator != null && blockingAggregator.isBlockEntitlement() ? EntitlementState.BLOCKED : EntitlementState.ACTIVE);
}
}
}