package it.unipi.di.sam.sprint.ui.stats

import android.content.Context
import android.os.Bundle
import android.os.Parcelable
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import it.unipi.di.sam.sprint.R
import it.unipi.di.sam.sprint.SprintDatabaseHelper
import it.unipi.di.sam.sprint.ui.ItemDecoration
import it.unipi.di.sam.sprint.ui.RVAdapter
import it.unipi.di.sam.sprint.ui.Stat
import it.unipi.di.sam.sprint.ui.Title
import java.sql.Date
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale

class StatsFragment : Fragment() {

    private lateinit var recyclerView: RecyclerView
    private lateinit var adapter: RVAdapter
    private val items: MutableList<Any> = mutableListOf()
    private var currentDay: String = ""
    private var currentWeek: String = ""
    private var currentMonth: String = ""
    private var currentYear: String = ""
    private var isInitialized = false

    private val dateFormatDay = SimpleDateFormat("dd MMMM yyyy", Locale.getDefault())
    private val dateFormatWeek = SimpleDateFormat("dd MMMM yyyy", Locale.getDefault())
    private val dateFormatMonth = SimpleDateFormat("MMMM yyyy", Locale.getDefault())
    private val dateFormatYear = SimpleDateFormat("yyyy", Locale.getDefault())

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // inizializza il layout
        val rootView = inflater.inflate(R.layout.fragment_stats, container, false)
        // configura il RecyclerView con un LinearLayoutManager e un ItemDecoration.
        recyclerView = rootView.findViewById(R.id.rv)
        recyclerView.layoutManager = LinearLayoutManager(context)
        recyclerView.addItemDecoration(ItemDecoration(16))

        // controlla se ci sono dati salvati nello stato precedente
        if (savedInstanceState != null) {
            restoreState(savedInstanceState)
        } else {
            if (!isInitialized) {
                initializeData()
                isInitialized = true
            }
        }
        // imposta adapter per il recyclerView
        adapter = RVAdapter(requireContext(),items, null)
        recyclerView.adapter = adapter

        return rootView
    }

    // Salva lo stato corrente
    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        outState.putParcelableArrayList("items", ArrayList(items.map { it as? Parcelable }))
        outState.putString("currentDay", currentDay)
        outState.putString("currentWeek", currentWeek)
        outState.putString("currentMonth", currentMonth)
        outState.putString("currentYear", currentYear)
    }

    // ripristina lo stato salvato in precedenza
    private fun restoreState(savedInstanceState: Bundle) {
        items.clear()
        val parcelableItems = savedInstanceState.getParcelableArrayList<Parcelable>("items") ?: arrayListOf()
        items.addAll(parcelableItems.mapNotNull { it as? Any })
        currentDay = savedInstanceState.getString("currentDay", getCurrentDay())
        currentWeek = savedInstanceState.getString("currentWeek", getCurrentWeek())
        currentMonth = savedInstanceState.getString("currentMonth", getCurrentMonth())
        currentYear = savedInstanceState.getString("currentYear", getCurrentYear())
    }

    // inizializza i dati recupreando le statistiche dal DB
    private fun initializeData() {
        val dbHelper = SprintDatabaseHelper(requireContext())

        // Record
        items.add(Title("Record"))

        val records = dbHelper.getRecords()

        items.addAll(records)

        // Statistiche giornaliere
        currentDay = getCurrentDay()
        // recuprera statistiche del giorno corrente
        val todayStats = dbHelper.getStatistics(currentDay, currentDay)
        // crea un Title clickable e definisce la lambda che verrà eseguita quanto il titolo verrà cliccato
        items.add(Title("Today", currentDay, true) {
            // creo un clendarDayFragment e passo una lambda che verrà eseguita quando l'utente seleziona una data sul calendario
            val fragment = CalendarDayFragment { year, month, day ->
                // formatta la data selezionata
                val formattedDay = formatDay(year, month, day)
                // aggiorna titolo e statistiche
                updateTitle(formattedDay, 1)
                //chiude clendarDayFragment
                fragmentManager?.popBackStack()
            }
            // avvia la transazione per sostituire il fragment corrente con il nuovo clendarDayFragment e aggiunge la transazione allo stack per consentire il ritorno indietro
            fragmentManager?.beginTransaction()?.replace(R.id.fragment_container, fragment)?.addToBackStack(null)?.commit()
        })
        // aggiungo le statistiche
        items.addAll(todayStats)

        // Statistiche settimanali
        currentWeek = getCurrentWeek()
        val (startOfWeek, endOfWeek) = parseWeek(currentWeek)
        val thisWeekStats = dbHelper.getStatistics(startOfWeek, endOfWeek)
        items.add(Title("This Week", currentWeek, true) {
            val fragment = CalendarWeekFragment { startOfWeek, endOfWeek ->
                val formattedWeek = formatWeek(startOfWeek, endOfWeek)
                updateTitle(formattedWeek, 2)
                fragmentManager?.popBackStack()
            }
            fragmentManager?.beginTransaction()?.replace(R.id.fragment_container, fragment)?.addToBackStack(null)?.commit()
        })
        items.addAll(thisWeekStats)

        // Statistiche mensili
        currentMonth = getCurrentMonth()
        val (startOfMonth, endOfMonth) = parseMonth(currentMonth)
        val thisMonthStats = dbHelper.getStatistics(startOfMonth, endOfMonth)
        items.add(Title("This Month", currentMonth, true) {
            val fragment = CalendarMonthFragment { year, month ->
                val formattedMonth = formatMonth(year, month)
                updateTitle(formattedMonth, 3)
                fragmentManager?.popBackStack()
            }
            fragmentManager?.beginTransaction()?.replace(R.id.fragment_container, fragment)?.addToBackStack(null)?.commit()
        })
        items.addAll(thisMonthStats)

        // Statistiche annuali
        currentYear = getCurrentYear()
        val (startOfYear, endOfYear) = parseYear(currentYear)
        val thisYearStats = dbHelper.getStatistics(startOfYear, endOfYear)
        items.add(Title("This Year", currentYear, true) {
            val fragment = CalendarYearFragment { year ->
                val formattedYear = formatYear(year)
                updateTitle(formattedYear, 4)
                fragmentManager?.popBackStack()
            }
            fragmentManager?.beginTransaction()?.replace(R.id.fragment_container, fragment)?.addToBackStack(null)?.commit()
        })
        items.addAll(thisYearStats)

        // Statistiche totali
        items.add(Title("All time"))
        items.addAll(dbHelper.getStatistics("01 January 0000", "31 December 9999"))
    }

    // ----------------------------------------- METODI GESTIONE GIORNI -----------------------------------------

    // restituisce la data corrente nel formato "dd MMMM yyyy"
    private fun getCurrentDay(): String {
        return dateFormatDay.format(Calendar.getInstance().time)
    }

    // restituisce una data formattata
    private fun formatDay(year: Int, month: Int, day: Int): String {
        val calendar = Calendar.getInstance()
        calendar.set(year, month, day)
        val dateFormatDisplay = SimpleDateFormat("dd MMMM yyyy", Locale.getDefault())
        return dateFormatDisplay.format(calendar.time)
    }

    // ----------------------------------------- METODI GESTIONE SETTIMANE -----------------------------------------

    // restituisce una settimana formattata
    private fun getCurrentWeek(): String {
        val calendar = Calendar.getInstance()
        calendar.set(Calendar.DAY_OF_WEEK, calendar.firstDayOfWeek)
        val startOfWeek = calendar.time
        calendar.add(Calendar.DAY_OF_WEEK, 6)
        val endOfWeek = calendar.time
        return "${dateFormatWeek.format(startOfWeek)} - ${dateFormatWeek.format(endOfWeek)}"
    }

    // restituisce la settimana formattata
    private fun formatWeek(startOfWeek: Long, endOfWeek: Long): String {
        val startDate = Date(startOfWeek)
        val endDate = Date(endOfWeek)
        return "${dateFormatWeek.format(startDate)} - ${dateFormatWeek.format(endDate)}"
    }

    private fun parseWeek(week: String): Pair<String, String> {
        val dates = week.split(" - ")
        val startOfWeek = dates[0].let { dateFormatWeek.parse(it) }
        val endOfWeek = dates[1].let { dateFormatWeek.parse(it) }
        return Pair(dateFormatDay.format(startOfWeek!!), dateFormatDay.format(endOfWeek!!))
    }

    // ----------------------------------------- METODI GESTIONE MESI -----------------------------------------

    // restituisce l'intervallo di date per la settimana corrente
    private fun getCurrentMonth(): String {
        return dateFormatMonth.format(Calendar.getInstance().time)
    }

    // restituisce il mese formattato
    private fun formatMonth(year: Int, month: Int): String {
        val calendar = Calendar.getInstance()
        calendar.set(year, month, 1)
        return dateFormatMonth.format(calendar.time)
    }

    private fun parseMonth(month: String): Pair<String, String> {
        val calendar = Calendar.getInstance().apply {
            time = dateFormatMonth.parse(month)!!
            set(Calendar.DAY_OF_MONTH, 1)
        }
        val startOfMonth = dateFormatDay.format(calendar.time)
        calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH))
        val endOfMonth = dateFormatDay.format(calendar.time)
        return Pair(startOfMonth, endOfMonth)
    }

    // ----------------------------------------- METODI GESTIONE ANNI -----------------------------------------

    // restituisce l'intervallo di date per il mese corrente
    private fun getCurrentYear(): String {
        return dateFormatYear.format(Calendar.getInstance().time)
    }

    // restituisce l'anno formattato
    private fun formatYear(year: Int): String {
        val calendar = Calendar.getInstance()
        calendar.set(year, 1, 1)
        return dateFormatYear.format(calendar.time)
    }

    // restituisce data di inzio e fine di un anno
    private fun parseYear(year: String): Pair<String, String> {
        val calendar = Calendar.getInstance().apply {
            time = dateFormatYear.parse(year)!!
            set(Calendar.DAY_OF_YEAR, 1)
        }
        val startOfYear = dateFormatDay.format(calendar.time)
        calendar.set(Calendar.DAY_OF_YEAR, calendar.getActualMaximum(Calendar.DAY_OF_YEAR))
        val endOfYear = dateFormatDay.format(calendar.time)
        return Pair(startOfYear, endOfYear)
    }

    // ---------------------------------------------------------------------------------------------------------------------------

    // aggiorna il titolo di una sezione e le relative stats
    private fun updateTitle(newDate: String, titlePosition: Int) {
        var count = 0
        // trova l'indice del Title e clickable
        val index = items.indexOfFirst {
            if (it is Title && it.isClickable) {
                count++
                // se contatore è uguale alla posizione del titolo cercata
                if (count == titlePosition) {
                    return@indexOfFirst true
                }
            }
            false
        }

        if (index != -1 && items[index] is Title) {
            // salva titolo vecchio
            val oldTitleItem = items[index] as Title
            // ottengo un istanza del DB in modalità lettura
            val dbHelper = SprintDatabaseHelper(requireContext())
            val db = dbHelper.readableDatabase

            // inizializza newStats con una lista vuota
            var newStats = emptyList<Stat>()

            // aggiorno newTitle e newStats in base alla titlePosition
            val newTitle = when (titlePosition) {
                1 -> {
                    newStats = dbHelper.getStatistics(newDate, newDate)
                    if (newDate != currentDay) newDate
                    else "Today"
                }
                2 -> {
                    val (startOfWeek, endOfWeek) = parseWeek(newDate)
                    newStats = dbHelper.getStatistics(startOfWeek, endOfWeek)
                    if (newDate != currentWeek) newDate
                    else "This Week"
                }
                3 -> {
                    val (startOfMonth, endOfMonth) = parseMonth(newDate)
                    newStats = dbHelper.getStatistics(startOfMonth, endOfMonth)
                    if (newDate != currentMonth) newDate
                    else "This Month"
                }
                4 -> {
                    val (startOfYear, endOfYear) = parseYear(newDate)
                    newStats = dbHelper.getStatistics(startOfYear, endOfYear)
                    if (newDate != currentYear) newDate
                    else "This Year"
                }
                else -> oldTitleItem.name
            }

            items[index] = oldTitleItem.copy(name = newTitle)
            adapter.notifyItemChanged(index)

            // Rimuove le vecchie statistiche e aggiunge le nuove
            val statIndex = index + 1
            while (items.size > statIndex && items[statIndex] is Stat) {
                items.removeAt(statIndex)
                adapter.notifyItemRemoved(statIndex)
            }

            items.addAll(statIndex, newStats)
            adapter.notifyItemRangeInserted(statIndex, newStats.size)
        }
    }
}


package it.unipi.di.sam.sprint

import android.content.ContentValues
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import android.util.Log
import it.unipi.di.sam.sprint.ui.Activity
import it.unipi.di.sam.sprint.ui.Split
import it.unipi.di.sam.sprint.ui.Stat
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.Locale

class SprintDatabaseHelper(context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {

    companion object {
        const val DATABASE_NAME = "running.db"
        const val DATABASE_VERSION = 6

        // Tabella activities
        const val TABLE_ACTIVITIES = "activities"
        const val COLUMN_ACTIVITY_ID = "_id"
        const val COLUMN_ACTIVITY_TITLE = "title"
        const val COLUMN_ACTIVITY_DATE = "date"
        const val COLUMN_ACTIVITY_DISTANCE = "distance"
        const val COLUMN_ACTIVITY_PACE = "pace"
        const val COLUMN_ACTIVITY_TIME = "time"
        const val COLUMN_ACTIVITY_PATH_POINTS = "pathPoints"
        const val COLUMN_ACTIVITY_KM_SPLITS = "kmSplits"

        // Tabella goals
        const val TABLE_GOALS = "goals"
        const val COLUMN_GOAL_ID = "_id"
        const val COLUMN_GOAL_OBJECTIVE = "objective"
        const val COLUMN_GOAL_DEADLINE = "deadline"
        const val COLUMN_GOAL_PROGRESS = "progress"

        // Tabella records
        const val TABLE_RECORDS = "records"
        const val COLUMN_RECORD_ID = "_id"
        const val COLUMN_RECORD_LABEL = "label"
        const val COLUMN_RECORD_TIME = "time"
        const val COLUMN_RECORD_ACTIVITY_ID = "activity_id"
    }

    override fun onCreate(db: SQLiteDatabase) {
        val createActivitiesTable = """
        CREATE TABLE $TABLE_ACTIVITIES (
            $COLUMN_ACTIVITY_ID INTEGER PRIMARY KEY AUTOINCREMENT,
            $COLUMN_ACTIVITY_TITLE TEXT NOT NULL,
            $COLUMN_ACTIVITY_DATE TEXT NOT NULL,
            $COLUMN_ACTIVITY_DISTANCE REAL NOT NULL,
            $COLUMN_ACTIVITY_PACE TEXT NOT NULL,
            $COLUMN_ACTIVITY_TIME INTEGER NOT NULL,
            $COLUMN_ACTIVITY_PATH_POINTS TEXT,  
            $COLUMN_ACTIVITY_KM_SPLITS TEXT  
        );
        """.trimIndent()

        val createGoalsTable = """
            CREATE TABLE $TABLE_GOALS (
                $COLUMN_GOAL_ID INTEGER PRIMARY KEY AUTOINCREMENT,
                $COLUMN_GOAL_OBJECTIVE TEXT NOT NULL,
                $COLUMN_GOAL_DEADLINE TEXT NOT NULL,
                $COLUMN_GOAL_PROGRESS REAL NOT NULL
            );
        """.trimIndent()

        val createRecordsTable = """
        CREATE TABLE $TABLE_RECORDS (
            $COLUMN_RECORD_ID INTEGER PRIMARY KEY AUTOINCREMENT,
            $COLUMN_RECORD_LABEL TEXT NOT NULL,
            $COLUMN_RECORD_TIME INTEGER,
            $COLUMN_RECORD_ACTIVITY_ID INTEGER,
            FOREIGN KEY($COLUMN_RECORD_ACTIVITY_ID) REFERENCES $TABLE_ACTIVITIES($COLUMN_ACTIVITY_ID)
        );
        """.trimIndent()

        // Esegui la creazione delle tabelle
        db.execSQL(createActivitiesTable)
        db.execSQL(createGoalsTable)
        db.execSQL(createRecordsTable)

        // Inserisci i record predefiniti nella tabella records
        insertDefaultRecords(db)
    }

    override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {

    }

    // Inserisce i record predefiniti nella tabella records
    fun insertDefaultRecords(db: SQLiteDatabase) {
        val defaultRecords = listOf("1km", "5km", "10km", "halfmarathon", "marathon")
        val contentValues = ContentValues()

        for (label in defaultRecords) {
            contentValues.put(COLUMN_RECORD_LABEL, label)
            contentValues.putNull(COLUMN_RECORD_TIME)  // Tempo inizialmente null
            contentValues.putNull(COLUMN_RECORD_ACTIVITY_ID)  // activity_id inizialmente null
            db.insert(TABLE_RECORDS, null, contentValues)
            contentValues.clear()
        }
    }

    fun updateRecords(activityId: Long, splits: List<Split>) {
        val db = this.readableDatabase

        // Controllo e aggiornamento per ogni record (1km, 5km, 10km, etc.)
        if (splits.size > 1) updateRecordForDistance("1km", 1, splits, db, activityId)
        if (splits.size > 5) updateRecordForDistance("5km", 5, splits, db, activityId)
        if (splits.size > 10) updateRecordForDistance("10km", 10, splits, db, activityId)
        if (splits.size > 21) updateRecordForDistance("halfmarathon", 21, splits, db, activityId)
        if (splits.size > 42) updateRecordForDistance("marathon", 42, splits, db, activityId)
    }

    // Funzione generale per controllare e aggiornare i record basati su multipli split consecutivi (incluso il singolo split)
    private fun updateRecordForDistance(label: String, requiredKilometers: Int, splits: List<Split>, db: SQLiteDatabase, activityId: Long) {
        val cursor = db.rawQuery(
            "SELECT $COLUMN_RECORD_TIME FROM $TABLE_RECORDS WHERE $COLUMN_RECORD_LABEL = ?",
            arrayOf(label)
        )

        if (cursor.moveToFirst()) {
            val currentRecordTime = cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_RECORD_TIME))?.toIntOrNull()

            // Variabile per tracciare il miglior tempo per la distanza specificata
            var bestTimeForDistance: Int? = null

            // Ciclo attraverso tutti i possibili gruppi di split consecutivi che coprono la distanza richiesta
            for (i in 0 until (splits.size - requiredKilometers)) {
                val consecutiveSplits = splits.subList(i, i + requiredKilometers)
                val totalTimeForDistance = consecutiveSplits.sumOf { it.time }

                // Se troviamo un tempo migliore, aggiorniamo bestTimeForDistance
                if (bestTimeForDistance == null || totalTimeForDistance < bestTimeForDistance) {
                    bestTimeForDistance = totalTimeForDistance
                }
            }

            // Se il miglior tempo trovato è migliore del record attuale, aggiorniamo il record
            if (bestTimeForDistance != null && (currentRecordTime == null || bestTimeForDistance < currentRecordTime)) {
                updateRecord(label, bestTimeForDistance.toString(), activityId)
            }
        }
        cursor.close()
    }



    // Funzione per aggiornare un singolo record con il tempo e collegarlo a un'attività
    private fun updateRecord(label: String, time: String, activityId: Long) {
        val db = this.writableDatabase
        val contentValues = ContentValues().apply {
            put(COLUMN_RECORD_TIME, time)
            put(COLUMN_RECORD_ACTIVITY_ID, activityId)
        }

        // Aggiorna il record basato sull'etichetta
        db.update(TABLE_RECORDS, contentValues, "$COLUMN_RECORD_LABEL = ?", arrayOf(label))
    }


    // Funzione per inserire una nuova attività
    fun insertActivity(title: String, date: String, distance: Double, pace: String, time: Int, pathPoints: String, kmSplits: String): Long {
        val db = this.writableDatabase
        val contentValues = ContentValues().apply {
            put(COLUMN_ACTIVITY_TITLE, title)
            put(COLUMN_ACTIVITY_DATE, date.toDatabaseDateFormat())
            put(COLUMN_ACTIVITY_DISTANCE, distance)
            put(COLUMN_ACTIVITY_PACE, pace)
            put(COLUMN_ACTIVITY_TIME, time)
            put(COLUMN_ACTIVITY_PATH_POINTS, pathPoints)
            put(COLUMN_ACTIVITY_KM_SPLITS, kmSplits)
        }
        // Inserisce i dati e restituisce l'ID della riga inserita
        return db.insert(TABLE_ACTIVITIES, null, contentValues)
    }


    // Funzione per inserire un nuovo goal
    fun insertGoal(objective: String, deadline: String, progress: Double) {
        val db = this.writableDatabase
        val contentValues = ContentValues().apply {
            put(COLUMN_GOAL_OBJECTIVE, objective)
            put(COLUMN_GOAL_DEADLINE, deadline.toDatabaseDateFormat())
            put(COLUMN_GOAL_PROGRESS, progress)
        }
        db.insert(TABLE_GOALS, null, contentValues)
    }

    fun clearTable(tableName: String) {
        val db = this.writableDatabase
        db.execSQL("DELETE FROM $tableName")
    }

    fun isDatabaseEmpty(): Boolean {
        val db = this.readableDatabase
        val cursor = db.rawQuery("SELECT COUNT(*) FROM $TABLE_ACTIVITIES", null)
        cursor.moveToFirst()
        val count = cursor.getInt(0)
        cursor.close()
        return count == 0
    }

    private fun String.toDatabaseDateFormat(): String {
        for (format in getKnownDateFormats()) {
            try {
                val date = format.parse(this)
                if (date != null) {
                    return getDatabaseDateFormat().format(date)
                }
            } catch (e: ParseException) {
                // Ignored, try next format
            }
        }
        throw IllegalArgumentException("Date format not recognized: $this")
    }

    private fun String.fromDatabaseDateFormat(): String {
        val date = getDatabaseDateFormat().parse(this)
        return getKnownDateFormats()[0].format(date!!)
    }

    fun getActivities(startDate: String, endDate: String): List<Activity> {
        val db = this.readableDatabase
        val activities = mutableListOf<Activity>()

        val query = """
        SELECT * FROM $TABLE_ACTIVITIES
        WHERE $COLUMN_ACTIVITY_DATE BETWEEN ? AND ?
        """
        val cursor = db.rawQuery(query, arrayOf(startDate.toDatabaseDateFormat(), endDate.toDatabaseDateFormat()))

        if (cursor.moveToFirst()) {
            do {
                val activity = Activity(cursor)
                activities.add(activity)
            } while (cursor.moveToNext())
        }
        cursor.close()

        return activities
    }

    fun getStatistics(startDate: String, endDate: String): List<Stat> {
        val db = this.readableDatabase
        val stats = mutableListOf<Stat>()
        val cursor = db.rawQuery("""
            SELECT COUNT(*) AS activity_count,
                   SUM($COLUMN_ACTIVITY_TIME) AS total_time,
                   SUM($COLUMN_ACTIVITY_DISTANCE) AS total_distance
            FROM $TABLE_ACTIVITIES
            WHERE $COLUMN_ACTIVITY_DATE BETWEEN ? AND ?
        """, arrayOf(startDate.toDatabaseDateFormat(), endDate.toDatabaseDateFormat()))

        if (cursor.moveToFirst()) {
            val time = Utils.formatTime(cursor.getInt(cursor.getColumnIndexOrThrow("total_time")))
            val distance = Utils.formatDistance(cursor.getDouble(cursor.getColumnIndexOrThrow("total_distance")))

            stats.add(Stat("Activities", cursor.getInt(cursor.getColumnIndexOrThrow("activity_count")).toString()))
            stats.add(Stat("Time",  time))
            stats.add(Stat("Distance", distance))
        }
        cursor.close()
        return stats
    }

    fun getRecords(): List<Stat> {
        val db = this.readableDatabase
        val records = mutableListOf<Stat>()

        val query = """
    SELECT * FROM $TABLE_RECORDS
    WHERE $COLUMN_RECORD_TIME IS NOT NULL
    AND $COLUMN_RECORD_ACTIVITY_ID IS NOT NULL
    """
        val cursor = db.rawQuery(query, null)

        if (cursor.moveToFirst()) {
            do {
                val time = Utils.formatTime(cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_RECORD_TIME)))

                val record = Stat(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_RECORD_LABEL)), time)

                records.add(record)
            } while (cursor.moveToNext())
        }
        cursor.close()

        return records
    }


    private fun getDatabaseDateFormat(): SimpleDateFormat {
        return SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
    }

    private fun getKnownDateFormats(): List<SimpleDateFormat> {
        return listOf(
            SimpleDateFormat("dd MMMM yyyy", Locale.getDefault()),
            SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()),
            SimpleDateFormat("dd-MM-yyyy", Locale.getDefault())
        )
    }
}

package it.unipi.di.sam.sprint.ui

import ParcelablePair
import android.content.Context
import android.content.Intent
import android.text.format.DateUtils.formatElapsedTime
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import it.unipi.di.sam.sprint.DetailsActivity
import it.unipi.di.sam.sprint.R
import it.unipi.di.sam.sprint.Utils
import it.unipi.di.sam.sprint.databinding.*

class RVAdapter(private val context: Context, private val items: List<Any>, private val listener: (() -> Unit)?) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    private val TYPE_TITLE = 0
    private val TYPE_STAT = 1
    private val TYPE_READ_MORE_BUTTON = 2
    private val TYPE_ACTIVITY = 3
    private val TYPE_GOAL = 4
    private val TYPE_SPLIT = 5

    override fun getItemViewType(position: Int): Int {
        return when (items[position]) {
            is Title -> TYPE_TITLE
            is Stat -> TYPE_STAT
            "READ_MORE_BUTTON" -> TYPE_READ_MORE_BUTTON
            is Activity -> TYPE_ACTIVITY
            is Goal -> TYPE_GOAL
            is Split -> TYPE_SPLIT
            else -> throw IllegalArgumentException("Unknown item")
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        return when (viewType) {
            TYPE_TITLE -> {
                val view = LayoutInflater.from(parent.context).inflate(R.layout.title_item, parent, false)
                TitleViewHolder(view)
            }
            TYPE_STAT -> {
                val binding = StatItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
                StatViewHolder(binding)
            }
            TYPE_READ_MORE_BUTTON -> {
                val binding = ReadmoreButtonItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
                ReadMoreButtonViewHolder(binding.root, listener)
            }
            TYPE_ACTIVITY -> {
                val binding = ActivityItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
                ActivityViewHolder(binding, context)
            }
            TYPE_GOAL -> {
                val binding = GoalItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
                GoalViewHolder(binding)
            }
            TYPE_SPLIT -> {
                val binding = SplitItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
                SplitViewHolder(binding)
            }
            else -> {
                throw IllegalArgumentException("Unknown view type")
            }
        }
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        when (holder) {
            is TitleViewHolder -> holder.bind(items[position] as Title)
            is StatViewHolder -> holder.bind(items[position] as Stat)
            is ReadMoreButtonViewHolder -> holder.bind()
            is ActivityViewHolder -> holder.bind(items[position] as Activity)
            is GoalViewHolder -> holder.bind(items[position] as Goal)
            is SplitViewHolder -> holder.bind(items[position] as Split)
        }
    }

    override fun getItemCount(): Int = items.size

    class TitleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        private val titleTextView: TextView = itemView.findViewById(R.id.title_value)

        fun bind(title: Title) {
            titleTextView.text = title.name
            itemView.setOnClickListener {
                if (title.isClickable) {
                    title.onClick?.invoke()
                }
            }
        }
    }

    class StatViewHolder(private val binding: StatItemBinding) : RecyclerView.ViewHolder(binding.root) {
        fun bind(stat: Stat) {
            binding.statName.text = stat.name
            binding.statValue.text = stat.value
        }
    }

    class ReadMoreButtonViewHolder(view: View, private val listener: (() -> Unit)?) : RecyclerView.ViewHolder(view) {
        private val readMoreButton: Button = view.findViewById(R.id.btnReadMore)

        init {
            readMoreButton.setOnClickListener {
                listener?.invoke()
            }
        }

        fun bind() {
            // Bind data if necessary
        }
    }

    class ActivityViewHolder(private val binding: ActivityItemBinding, private val context: Context) : RecyclerView.ViewHolder(binding.root) {

        private var currentActivity: Activity? = null

        fun bind(activity: Activity) {
            binding.activityTitle.text = activity.title
            binding.activityDate.text = activity.date
            val formattedDistance = context.getString(R.string.distance_text, activity.distance)
            binding.activityDistance.text = formattedDistance
            binding.activityPace.text = activity.pace
            val totalTime = Utils.formatTime(activity.time)
            binding.activityTime.text = totalTime

            this.currentActivity = activity

            // Imposta il listener per avviare una nuova Activity
            binding.root.setOnClickListener {
                currentActivity?.let {
                    val context = binding.root.context
                    val intent = Intent(context, DetailsActivity::class.java).apply {
                        putExtra("EXTRA_TITLE", it.title)
                        putExtra("EXTRA_DATE", it.date)
                        putExtra("EXTRA_TOTAL_DISTANCE", it.distance)
                        putExtra("EXTRA_AVERAGE_PACE", it.pace)
                        putExtra("EXTRA_TOTAL_TIME", it.time)
                        putExtra("EXTRA_PATH_POINTS", it.pathPoints)
                        putExtra("EXTRA_KM_SPLITS", it.kmSplits)
                    }
                    context.startActivity(intent)
                }
            }
        }
    }

    class GoalViewHolder(private val binding: GoalItemBinding) : RecyclerView.ViewHolder(binding.root) {
        fun bind(goal: Goal) {
            binding.goalName.text = goal.name
            binding.goalDeadline.text = goal.deadline
            binding.goalProgressValue.text = itemView.context.getString(R.string.progress_text, goal.progress)
            binding.goalProgressBar.progress = goal.progress
        }
    }

    class SplitViewHolder(private val binding: SplitItemBinding) : RecyclerView.ViewHolder(binding.root) {
        fun bind(split: Split) {
            val time = Utils.formatTime(split.time)

            binding.splitKilometer.text = "${split.kilometer} Km"
            binding.splitPace.text = time
        }
    }
}



In questo modo per leggere le statistiche io le leggo e le carico tutte in una array in memoria giusto? Io vorrei fare in modo che l'app tenga in memoria solo i dati che servono in un dato momento e non leggerli tutti per poi utilizzarne solo alcuni.