package ru.bcs.premier.presentation.expenses
import com.arellomobile.mvp.InjectViewState
import io.reactivex.BackpressureStrategy
import io.reactivex.Flowable
import io.reactivex.Single
import io.reactivex.rxkotlin.Flowables
import io.reactivex.rxkotlin.withLatestFrom
import io.reactivex.subjects.BehaviorSubject
import io.reactivex.subjects.PublishSubject
import ru.bcs.premier.content.Repository
import ru.bcs.premier.content.Schedulers
import ru.bcs.premier.content.data.Resource
import ru.bcs.premier.content.data.asResource
import ru.bcs.premier.interactor.expenses.ExpandableInteractor
import ru.bcs.premier.interactor.expenses.Expense
import ru.bcs.premier.presentation.RxMvpPresenter
import ru.bcs.premier.presentation.historyoperations.HistoryOperationFunctions
import ru.bcs.premier.presentation.historyoperations.period.HistoryOperationPeriod
import ru.bcs.premier.presentation.products.LoadingExpenses
import ru.bcs.premier.reactivex.RxThrowable
import ru.bcs.premier.reactivex.toBehaviorFlowable
import ru.bcs.premier.util.Logger
@InjectViewState
class ExpensesAnalysisPresenter(
private val repository: Repository,
private val schedulers: Schedulers,
private val logger: Logger
) : RxMvpPresenter<ExpensesAnalysisView>() {
companion object {
private val defaultPeriod = HistoryOperationPeriod.Last30Days
private val defaultFilter = ExpensesFilter(
noCash = false,
noTransfer = false,
noBroker = false,
noPaymentForService = false
)
}
private val gettingExpensesSubject = BehaviorSubject.createDefault(Unit)
private val startExpensesLoadingSubject = BehaviorSubject.createDefault<LoadingExpenses>(LoadingExpenses.Loading)
private val retrySubject = PublishSubject.create<Unit>()
private val periodSubject = BehaviorSubject.createDefault<HistoryOperationPeriod>(defaultPeriod)
private val categorySubject = BehaviorSubject.create<String>()
private val filterSubject = BehaviorSubject.createDefault<ExpensesFilter>(defaultFilter)
private val datePickerClickSubject = PublishSubject.create<Unit>()
private lateinit var expandableInteractor: ExpandableInteractor<Expense>
override fun onFirstViewAttach() {
super.onFirstViewAttach()
val retryFlowable = retrySubject
.toFlowable(BackpressureStrategy.LATEST)
.toBehaviorFlowable()
val periodFlowable = periodSubject
.toFlowable(BackpressureStrategy.LATEST)
.toBehaviorFlowable()
val filterFlowable = filterSubject
.toFlowable(BackpressureStrategy.LATEST)
.toBehaviorFlowable()
val datePickerClickFlowable = datePickerClickSubject.toFlowable(BackpressureStrategy.LATEST)
.toBehaviorFlowable()
val gettingExpensesFlowable = gettingExpensesSubject
.toFlowable(BackpressureStrategy.LATEST)
val startExpensesLoadingFlowable = startExpensesLoadingSubject
.toFlowable(BackpressureStrategy.LATEST)
val operationExpensesFlowable = retryFlowable
.startWith(Unit)
.switchMap {
Flowables.combineLatest(
periodFlowable,
filterFlowable,
gettingExpensesFlowable,
startExpensesLoadingFlowable
) { period, filter, _, expenses ->
Triple(expenses, period, filter)
}
}
.switchMap(::getOperationExpenses)
.observeOn(schedulers.computation)
.distinctUntilChanged()
val historyOperationsFlowable = periodFlowable
.map(HistoryOperationFunctions::createOperationHistorySubRequest)
.flatMap { repository.getOperationHistory(it).asResource() }
.firstOrError()
.doOnSuccess {
startExpensesLoadingSubject.onNext(LoadingExpenses.Start)
}
.doOnError {
startExpensesLoadingSubject.onNext(LoadingExpenses.Error(it))
}
.subscribeOn(schedulers.computation)
.toFlowable()
val categoryFlowable = categorySubject.toFlowable(BackpressureStrategy.LATEST)
Flowables.combineLatest(periodFlowable, filterFlowable, historyOperationsFlowable, operationExpensesFlowable,
ExpensesAnalysisFactory::createViewState)
.distinctUntilChanged()
.subscribeOn(schedulers.computation)
.observeOn(schedulers.mainThread)
.autoDisposable()
.subscribe(viewState::onViewStateChanged, RxThrowable.printStackTrace(logger))
datePickerClickFlowable.withLatestFrom(periodFlowable) { _, period -> period }
.subscribeOn(schedulers.computation)
.observeOn(schedulers.mainThread)
.autoDisposable()
.subscribe(viewState::onDatePickerClicked, RxThrowable.printStackTrace(logger))
categoryFlowable.withLatestFrom(periodFlowable) { categoryName, period ->
Pair(categoryName, period)
}
.subscribeOn(schedulers.computation)
.observeOn(schedulers.mainThread)
.autoDisposable()
.subscribe(viewState::onCategoryClicked, RxThrowable.printStackTrace(logger))
}
fun onPeriodChanged(period: HistoryOperationPeriod) = periodSubject.onNext(period)
fun onFilterChanged(filter: ExpensesFilter) = filterSubject.onNext(filter)
fun onDatePickerClicked() = datePickerClickSubject.onNext(Unit)
fun onCategoryClicked(categoryName: String) = categorySubject.onNext(categoryName)
fun retry() {
retrySubject.onNext(Unit)
gettingExpensesSubject.onNext(Unit)
}
private fun getOperationExpenses(triple: Triple<LoadingExpenses, HistoryOperationPeriod, ExpensesFilter>) =
if (triple.first is LoadingExpenses.Start) {
Single.just(Pair(triple.second, triple.third))
.map {
ExpensesAnalysisFactory.createRequest(it.first, it.second)
}
.flatMap(repository::getOperationExpenses)
.asResource()
} else {
val loadingExpenses = triple.first
val resource = if (loadingExpenses is LoadingExpenses.Error) Resource.Error(loadingExpenses.throwable) else Resource.Loading()
Flowable.just(resource)
}
}