<commit_before>package org.stepic.droid.core.presenters

import android.support.annotation.WorkerThread
import io.reactivex.Single
import io.reactivex.rxkotlin.zipWith
import org.stepic.droid.analytic.Analytic
import org.stepic.droid.concurrency.MainHandler
import org.stepic.droid.concurrency.SingleThreadExecutor
import org.stepic.droid.core.FilterApplicator
import org.stepic.droid.core.FirstCoursePoster
import org.stepic.droid.core.earlystreak.contract.EarlyStreakPoster
import org.stepic.droid.core.presenters.contracts.CoursesView
import org.stepic.droid.di.course_list.CourseListScope
import org.stepic.droid.features.deadlines.repository.DeadlinesRepository
import org.stepic.droid.model.CourseReviewSummary
import org.stepic.droid.preferences.SharedPreferenceHelper
import org.stepic.droid.storage.operations.DatabaseFacade
import org.stepic.droid.storage.operations.Table
import org.stepic.droid.util.CourseUtil
import org.stepic.droid.util.DateTimeHelper
import org.stepic.droid.util.RWLocks
import org.stepic.droid.web.Api
import org.stepic.droid.web.CoursesMetaResponse
import org.stepik.android.model.Course
import org.stepik.android.model.Progress
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger
import javax.inject.Inject

@CourseListScope
class PersistentCourseListPresenter
@Inject constructor(
        private val databaseFacade: DatabaseFacade,
        private val singleThreadExecutor: SingleThreadExecutor,
        private val mainHandler: MainHandler,
        private val api: Api,
        private val filterApplicator: FilterApplicator,
        private val sharedPreferenceHelper: SharedPreferenceHelper,
        private val earlyStreakPoster: EarlyStreakPoster,
        private val firstCoursePoster: FirstCoursePoster,

        private val deadlinesRepository: DeadlinesRepository,
        private val analytic: Analytic
) : PresenterBase<CoursesView>() {

    companion object {
        //if hasNextPage & < MIN_COURSES_ON_SCREEN -> load next page
        private const val MIN_COURSES_ON_SCREEN = 5
        private const val MAX_CURRENT_NUMBER_OF_TASKS = 2
        private const val SEVEN_DAYS_MILLIS = 7 * 24 * 60 * 60 * 1000L
        private const val MILLIS_IN_SECOND = 1000L
    }

    private val currentPage = AtomicInteger(1)
    private val hasNextPage = AtomicBoolean(true)
    private var currentNumberOfTasks: Int = 0 //only main thread
    private val isEmptyCourses = AtomicBoolean(false)

    fun restoreState() {
        if (isEmptyCourses.get() && !hasNextPage.get()) {
            view?.showEmptyCourses()
        }
    }

    /**
     * 1) Show from cache, if not empty (hide progress)
     * 2) Load from internet (if fail -> show)
     * 3) Save to db
     * 4) show from cache (all states)
     */
    fun downloadData(courseType: Table) {
        downloadData(courseType, isRefreshing = false)
    }

    private fun downloadData(courseType: Table, isRefreshing: Boolean, isLoadMore: Boolean = false) {
        if (currentNumberOfTasks >= MAX_CURRENT_NUMBER_OF_TASKS) {
            return
        }
        currentNumberOfTasks++
        singleThreadExecutor.execute {
            try {
                downloadDataPlain(isRefreshing, isLoadMore, courseType)
            } finally {
                mainHandler.post {
                    currentNumberOfTasks--
                }
            }
        }
    }

    @WorkerThread
    private fun downloadDataPlain(isRefreshing: Boolean, isLoadMore: Boolean, courseType: Table) {
        if (!isLoadMore) {
            mainHandler.post {
                view?.showLoading()
            }
            showFromDatabase(courseType)
        } else if (hasNextPage.get()) {
            mainHandler.post {
                view?.showLoading()
            }
        }

        while (hasNextPage.get()) {
            val coursesFromInternet: List<Course>? = try {
                if (courseType == Table.featured) {
                    val response = api.getPopularCourses(currentPage.get()).blockingGet()
                    handleMeta(response)
                    response.courses
                } else {
                    val allMyCourses = arrayListOf<Course>()
                    while (hasNextPage.get()) {
                        val originalResponse = api.getUserCourses(currentPage.get())
                                .map { courses -> courses.userCourse.map { it.course } }
                                .flatMap { list -> api.getCoursesReactive(currentPage.get(), list.toLongArray())
                                        .zipWith(Single.just(list)) }
                                .map { (courses, userCourses) ->
                                    val orderedId = userCourses.asSequence().withIndex().associate { it.value to it.index }
                                    courses.courses.sortBy { orderedId[it.id] }
                                    courses
                                }
                                .blockingGet()
                        allMyCourses.addAll(originalResponse.courses)
                        handleMeta(originalResponse)
                    }<commit_msg>remove useless rx overhead<commit_after>package org.stepic.droid.core.presenters

import android.support.annotation.WorkerThread
import org.stepic.droid.analytic.Analytic
import org.stepic.droid.concurrency.MainHandler
import org.stepic.droid.concurrency.SingleThreadExecutor
import org.stepic.droid.core.FilterApplicator
import org.stepic.droid.core.FirstCoursePoster
import org.stepic.droid.core.earlystreak.contract.EarlyStreakPoster
import org.stepic.droid.core.presenters.contracts.CoursesView
import org.stepic.droid.di.course_list.CourseListScope
import org.stepic.droid.features.deadlines.repository.DeadlinesRepository
import org.stepic.droid.model.CourseReviewSummary
import org.stepic.droid.preferences.SharedPreferenceHelper
import org.stepic.droid.storage.operations.DatabaseFacade
import org.stepic.droid.storage.operations.Table
import org.stepic.droid.util.CourseUtil
import org.stepic.droid.util.DateTimeHelper
import org.stepic.droid.util.RWLocks
import org.stepic.droid.web.Api
import org.stepic.droid.web.CoursesMetaResponse
import org.stepik.android.model.Course
import org.stepik.android.model.Progress
import org.stepik.android.model.UserCourse
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger
import javax.inject.Inject

@CourseListScope
class PersistentCourseListPresenter
@Inject constructor(
        private val databaseFacade: DatabaseFacade,
        private val singleThreadExecutor: SingleThreadExecutor,
        private val mainHandler: MainHandler,
        private val api: Api,
        private val filterApplicator: FilterApplicator,
        private val sharedPreferenceHelper: SharedPreferenceHelper,
        private val earlyStreakPoster: EarlyStreakPoster,
        private val firstCoursePoster: FirstCoursePoster,

        private val deadlinesRepository: DeadlinesRepository,
        private val analytic: Analytic
) : PresenterBase<CoursesView>() {

    companion object {
        //if hasNextPage & < MIN_COURSES_ON_SCREEN -> load next page
        private const val MIN_COURSES_ON_SCREEN = 5
        private const val MAX_CURRENT_NUMBER_OF_TASKS = 2
        private const val SEVEN_DAYS_MILLIS = 7 * 24 * 60 * 60 * 1000L
        private const val MILLIS_IN_SECOND = 1000L
    }

    private val currentPage = AtomicInteger(1)
    private val hasNextPage = AtomicBoolean(true)
    private var currentNumberOfTasks: Int = 0 //only main thread
    private val isEmptyCourses = AtomicBoolean(false)

    fun restoreState() {
        if (isEmptyCourses.get() && !hasNextPage.get()) {
            view?.showEmptyCourses()
        }
    }

    /**
     * 1) Show from cache, if not empty (hide progress)
     * 2) Load from internet (if fail -> show)
     * 3) Save to db
     * 4) show from cache (all states)
     */
    fun downloadData(courseType: Table) {
        downloadData(courseType, isRefreshing = false)
    }

    private fun downloadData(courseType: Table, isRefreshing: Boolean, isLoadMore: Boolean = false) {
        if (currentNumberOfTasks >= MAX_CURRENT_NUMBER_OF_TASKS) {
            return
        }
        currentNumberOfTasks++
        singleThreadExecutor.execute {
            try {
                downloadDataPlain(isRefreshing, isLoadMore, courseType)
            } finally {
                mainHandler.post {
                    currentNumberOfTasks--
                }
            }
        }
    }

    @WorkerThread
    private fun downloadDataPlain(isRefreshing: Boolean, isLoadMore: Boolean, courseType: Table) {
        if (!isLoadMore) {
            mainHandler.post {
                view?.showLoading()
            }
            showFromDatabase(courseType)
        } else if (hasNextPage.get()) {
            mainHandler.post {
                view?.showLoading()
            }
        }

        while (hasNextPage.get()) {
            val coursesFromInternet: List<Course>? = try {
                if (courseType == Table.featured) {
                    val response = api.getPopularCourses(currentPage.get()).blockingGet()
                    handleMeta(response)
                    response.courses
                } else {
                    val allMyCourses = arrayListOf<Course>()
                    while (hasNextPage.get()) {
                        val page = currentPage.get()
                        val coursesOrder = api.getUserCourses(page)
                                .blockingGet()
                                .userCourse
                                .map(UserCourse::course)

                        val coursesResponse = api.getCoursesReactive(page, coursesOrder.toLongArray())
                                .blockingGet()
                        val courses = coursesResponse
                                .courses
                                .sortedBy { coursesOrder.indexOf(it.id) }

                        allMyCourses.addAll(courses)
                        handleMeta(coursesResponse)
                    }
{'fraction_non_alphanumeric': 0.07837917775805975, 'fraction_numerical': 0.0045351473922902496, 'mean_word_length': 2.235725677830941, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 18, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 14, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '683237', 'n_tokens_mistral': 2836, 'n_tokens_neox': 2685, 'n_words': 552}