diff --git a/app/src/main/java/it/reyboz/bustorino/backend/Notifications.java b/app/src/main/java/it/reyboz/bustorino/backend/Notifications.java
--- a/app/src/main/java/it/reyboz/bustorino/backend/Notifications.java
+++ b/app/src/main/java/it/reyboz/bustorino/backend/Notifications.java
@@ -1,9 +1,11 @@
 package it.reyboz.bustorino.backend;
 
+import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.content.Context;
 import android.os.Build;
+import androidx.core.app.NotificationCompat;
 import it.reyboz.bustorino.R;
 
 public class Notifications {
@@ -44,4 +46,35 @@
             notificationManager.createNotificationChannel(channel);
         }
     }
+
+
+    public static Notification makeMatoDownloadNotification(Context context,String title){
+        return new NotificationCompat.Builder(context, Notifications.DB_UPDATE_CHANNELS_ID)
+                //.setContentIntent(PendingIntent.getActivity(context, 0, Intent(context, MainActivity::class.java), Constants.PENDING_INTENT_FLAG_IMMUTABLE))
+                .setSmallIcon(R.drawable.bus)
+                .setOngoing(true)
+                .setAutoCancel(true)
+                .setOnlyAlertOnce(true)
+                .setPriority(NotificationCompat.PRIORITY_MIN)
+                .setContentTitle(context.getString(R.string.app_name))
+                .setLocalOnly(true)
+                .setVisibility(NotificationCompat.VISIBILITY_SECRET)
+                .setContentText(title)
+                .build();
+    }
+    public static Notification makeMatoDownloadNotification(Context context){
+        return makeMatoDownloadNotification(context, "Downloading data from MaTO");
+    }
+
+    public static void createDBNotificationChannel(Context context){
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            NotificationChannel channel = new NotificationChannel(
+                Notifications.DB_UPDATE_CHANNELS_ID,
+                context.getString(R.string.database_notification_channel),
+                NotificationManager.IMPORTANCE_MIN
+            );
+            NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
+            notificationManager.createNotificationChannel(channel);
+        }
+    }
 }
diff --git a/app/src/main/java/it/reyboz/bustorino/backend/mato/MatoAPIFetcher.kt b/app/src/main/java/it/reyboz/bustorino/backend/mato/MatoAPIFetcher.kt
--- a/app/src/main/java/it/reyboz/bustorino/backend/mato/MatoAPIFetcher.kt
+++ b/app/src/main/java/it/reyboz/bustorino/backend/mato/MatoAPIFetcher.kt
@@ -408,22 +408,7 @@
                 res?.set(Fetcher.Result.PARSER_ERROR)
                 //Log.e(DEBUG_TAG, "Downloading feeds: $outObj")
             }
-            /*
-            var numRequests = 0
-            for(routeName in routesGTFSIds){
-                if (!routeName.isEmpty()) numRequests++
-            }
-            val countDownForRequests = CountDownLatch(numRequests)
-            val lockSave = ReentrantLock()
-            //val countDownFor
-            for (routeName in routesGTFSIds){
-                val pars = JSONObject()
-                pars.put("")
 
-            }
-            val goodResponseListener = Response.Listener<JSONObject> {  }
-            val errorResponseListener = Response.ErrorListener {  }
-             */
 
             return patterns
         }
diff --git a/app/src/main/java/it/reyboz/bustorino/backend/mato/MatoQueries.kt b/app/src/main/java/it/reyboz/bustorino/backend/mato/MatoQueries.kt
--- a/app/src/main/java/it/reyboz/bustorino/backend/mato/MatoQueries.kt
+++ b/app/src/main/java/it/reyboz/bustorino/backend/mato/MatoQueries.kt
@@ -118,7 +118,7 @@
           }
         }
         """
-
+        //Query for the patterns, with the associated route
         const val ROUTES_WITH_PATTERNS="""
             query RoutesWithPatterns(${'$'}routes: [String]) {
               routes(ids: ${'$'}routes) {
diff --git a/app/src/main/java/it/reyboz/bustorino/data/DatabaseUpdate.java b/app/src/main/java/it/reyboz/bustorino/data/DatabaseUpdate.java
--- a/app/src/main/java/it/reyboz/bustorino/data/DatabaseUpdate.java
+++ b/app/src/main/java/it/reyboz/bustorino/data/DatabaseUpdate.java
@@ -31,7 +31,6 @@
 import it.reyboz.bustorino.backend.Fetcher;
 import it.reyboz.bustorino.backend.FiveTAPIFetcher;
 import it.reyboz.bustorino.backend.Palina;
-import it.reyboz.bustorino.backend.Route;
 import it.reyboz.bustorino.backend.mato.MatoAPIFetcher;
 import it.reyboz.bustorino.data.gtfs.GtfsAgency;
 import it.reyboz.bustorino.data.gtfs.GtfsDatabase;
@@ -126,7 +125,9 @@
         }
         //match patterns with routes
 
-        final ArrayList<PatternStop> patternStops = new ArrayList<>(patterns.size());
+        final ArrayList<PatternStop> patternStops = makeStopsForPatterns(patterns);
+        final List<String> allPatternsCodeInDB = dao.getPatternsCodes();
+        final HashSet<String> patternsCodesToDelete = new HashSet<>(allPatternsCodeInDB);
 
         for(MatoPattern p: patterns){
             //scan patterns
@@ -135,25 +136,47 @@
             if (mRoute == null) {
                 Log.e(DEBUG_TAG, "Error in parsing the route: " + p.getRouteGtfsId() + " , cannot find the IDs in the map");
             }
-            for (int i=0; i<stopsIDs.size(); i++){
-                final String ID = stopsIDs.get(i);
-                patternStops.add(new PatternStop(p.getCode(),ID, i));
+            for (final String sID : stopsIDs) {
+                //add stops to pattern stops
                 // save routes stopping in the stop
-
-                if (!routesStoppingInStop.containsKey(ID)){
-                    routesStoppingInStop.put(ID, new HashSet<>());
+                if (!routesStoppingInStop.containsKey(sID)) {
+                    routesStoppingInStop.put(sID, new HashSet<>());
                 }
-                Set<String> mset= routesStoppingInStop.get(ID);
+                Set<String> mset = routesStoppingInStop.get(sID);
                 assert mset != null;
                 mset.add(mRoute.getShortName());
             }
+            //finally, remove from deletion list
+            patternsCodesToDelete.remove(p.getCode());
         }
+        // final time for insert
         dao.insertPatterns(patterns);
+        // clear patterns that are unused
+        Log.d(DEBUG_TAG, "Have to remove "+patternsCodesToDelete.size()+ " patterns from the DB");
+        dao.deletePatternsWithCodes(new ArrayList<>(patternsCodesToDelete));
         dao.insertPatternStops(patternStops);
 
         return routesStoppingInStop;
     }
 
+    /**
+     * Make the list of stops that each pattern does, to be inserted into the DB
+     * @param patterns the MatoPattern
+     * @return a list of PatternStop
+     */
+    public static ArrayList<PatternStop> makeStopsForPatterns(List<MatoPattern> patterns){
+        final ArrayList<PatternStop> patternStops = new ArrayList<>(patterns.size());
+        for (MatoPattern p: patterns){
+            final ArrayList<String> stopsIDs = p.getStopsGtfsIDs();
+            for (int i=0; i<stopsIDs.size(); i++) {
+                //add stops to pattern stops
+                final String ID = stopsIDs.get(i);
+                patternStops.add(new PatternStop(p.getCode(), ID, i));
+            }
+        }
+        return patternStops;
+    }
+
 
     /**
      * Run the DB Update
diff --git a/app/src/main/java/it/reyboz/bustorino/data/MatoPatternsDownloadWorker.kt b/app/src/main/java/it/reyboz/bustorino/data/MatoPatternsDownloadWorker.kt
new file mode 100644
--- /dev/null
+++ b/app/src/main/java/it/reyboz/bustorino/data/MatoPatternsDownloadWorker.kt
@@ -0,0 +1,101 @@
+/*
+	BusTO  - Data components
+    Copyright (C) 2023 Fabio Mazza
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package it.reyboz.bustorino.data
+
+import android.app.NotificationManager
+import android.content.Context
+import android.util.Log
+import androidx.work.*
+import it.reyboz.bustorino.backend.Fetcher
+import it.reyboz.bustorino.backend.Notifications
+import it.reyboz.bustorino.backend.mato.MatoAPIFetcher
+import java.util.concurrent.atomic.AtomicReference
+
+class MatoPatternsDownloadWorker(appContext: Context, workerParams: WorkerParameters)
+    : CoroutineWorker(appContext, workerParams) {
+
+
+    override suspend fun doWork(): Result {
+        val routesList = inputData.getStringArray(ROUTES_KEYS)
+
+        if (routesList== null){
+            Log.e(DEBUG_TAG,"routes list given is null")
+            return Result.failure()
+        }
+
+        val res = AtomicReference<Fetcher.Result>(Fetcher.Result.OK)
+
+        val gtfsRepository = GtfsRepository(applicationContext)
+        val patterns = MatoAPIFetcher.getPatternsWithStops(applicationContext, routesList.asList().toMutableList(), res)
+        if (res.get() != Fetcher.Result.OK) {
+            Log.e(DatabaseUpdate.DEBUG_TAG, "Something went wrong downloading patterns")
+            return Result.failure()
+        }
+
+        gtfsRepository.gtfsDao.insertPatterns(patterns)
+        //Insert the PatternStops
+        gtfsRepository.gtfsDao.insertPatternStops(DatabaseUpdate.makeStopsForPatterns(patterns))
+        return Result.success()
+    }
+
+
+    override suspend fun getForegroundInfo(): ForegroundInfo {
+        val notificationManager =
+            applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+        val context = applicationContext
+        Notifications.createDBNotificationChannel(context)
+
+        return ForegroundInfo(NOTIFICATION_ID, Notifications.makeMatoDownloadNotification(context))
+    }
+
+
+    companion object{
+        const val ROUTES_KEYS = "routesToDownload"
+        const val DEBUG_TAG="BusTO:MatoPattrnDownWRK"
+        const val NOTIFICATION_ID=21983102
+
+        const val TAG_PATTERNS ="matoPatternsDownload"
+
+
+
+        fun downloadPatternsForRoutes(routesIds: List<String>, context: Context): Boolean{
+            if(routesIds.isEmpty()) return false;
+
+            val workManager = WorkManager.getInstance(context);
+            val info = workManager.getWorkInfosForUniqueWork(TAG_PATTERNS).get()
+
+            val runNewWork = if(info.isEmpty()) true
+                        else info[0].state!= WorkInfo.State.RUNNING && info[0].state!= WorkInfo.State.ENQUEUED
+            val addDat = if(info.isEmpty())
+                null else  info[0].state
+
+            Log.d(DEBUG_TAG, "Request to download and insert patterns for ${routesIds.size} routes, proceed: $runNewWork, workstate: $addDat")
+            if(runNewWork){
+                val routeIdsArray = routesIds.toTypedArray()
+                val dataBuilder = Data.Builder().putStringArray(ROUTES_KEYS,routeIdsArray)
+
+                val requ = OneTimeWorkRequest.Builder(MatoPatternsDownloadWorker::class.java)
+                    .setInputData(dataBuilder.build()).setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
+                    .addTag(TAG_PATTERNS)
+                    .build()
+                workManager.enqueueUniqueWork(TAG_PATTERNS, ExistingWorkPolicy.KEEP, requ)
+            }
+            return true
+        }
+    }
+}
diff --git a/app/src/main/java/it/reyboz/bustorino/data/MatoDownloadTripsWorker.kt b/app/src/main/java/it/reyboz/bustorino/data/MatoTripsDownloadWorker.kt
rename from app/src/main/java/it/reyboz/bustorino/data/MatoDownloadTripsWorker.kt
rename to app/src/main/java/it/reyboz/bustorino/data/MatoTripsDownloadWorker.kt
--- a/app/src/main/java/it/reyboz/bustorino/data/MatoDownloadTripsWorker.kt
+++ b/app/src/main/java/it/reyboz/bustorino/data/MatoTripsDownloadWorker.kt
@@ -17,32 +17,26 @@
  */
 package it.reyboz.bustorino.data
 
-import android.app.NotificationChannel
 import android.app.NotificationManager
-import android.app.PendingIntent
 import android.content.Context
-import android.content.Intent
-import android.os.Build
 import android.util.Log
-import androidx.core.app.NotificationCompat
-import androidx.lifecycle.viewModelScope
-import androidx.work.CoroutineWorker
-import androidx.work.ForegroundInfo
-import androidx.work.Worker
-import androidx.work.WorkerParameters
-import com.android.volley.Response
-import it.reyboz.bustorino.R
+import androidx.work.*
 import it.reyboz.bustorino.backend.Notifications
 import it.reyboz.bustorino.data.gtfs.GtfsTrip
-import kotlinx.coroutines.launch
 import java.util.concurrent.CountDownLatch
 
-class MatoDownloadTripsWorker(appContext: Context, workerParams: WorkerParameters)
+class MatoTripsDownloadWorker(appContext: Context, workerParams: WorkerParameters)
     : CoroutineWorker(appContext, workerParams) {
 
+
     override suspend fun doWork(): Result {
-        //val imageUriInput =
-        //    inputData.("IMAGE_URI") ?: return Result.failure()
+        return downloadGtfsTrips()
+    }
+
+    /**
+     * Download GTFS Trips from Mato
+     */
+    private fun downloadGtfsTrips():Result{
         val tripsList = inputData.getStringArray(TRIPS_KEYS)
 
         if (tripsList== null){
@@ -80,7 +74,7 @@
                 }
                 Log.i(
                     DEBUG_TAG,"Result download, so far, trips: ${queriedMatoTrips.size}, failed: ${failedMatoTripsDownload.size}," +
-                        " succeded: ${downloadedMatoTrips.size}")
+                            " succeded: ${downloadedMatoTrips.size}")
                 //check if we can insert the trips
                 requestCountDown.countDown()
             }
@@ -102,35 +96,40 @@
         val notificationManager =
             applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
         val context = applicationContext
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-            val channel = NotificationChannel(
-                Notifications.DB_UPDATE_CHANNELS_ID,
-                context.getString(R.string.database_notification_channel),
-                NotificationManager.IMPORTANCE_MIN
-            )
-            notificationManager.createNotificationChannel(channel)
-        }
+        Notifications.createDBNotificationChannel(context)
 
-        val notification = NotificationCompat.Builder(context, Notifications.DB_UPDATE_CHANNELS_ID)
-            //.setContentIntent(PendingIntent.getActivity(context, 0, Intent(context, MainActivity::class.java), Constants.PENDING_INTENT_FLAG_IMMUTABLE))
-            .setSmallIcon(R.drawable.bus)
-            .setOngoing(true)
-            .setAutoCancel(true)
-            .setOnlyAlertOnce(true)
-            .setPriority(NotificationCompat.PRIORITY_MIN)
-            .setContentTitle(context.getString(R.string.app_name))
-            .setLocalOnly(true)
-            .setVisibility(NotificationCompat.VISIBILITY_SECRET)
-            .setContentText("Downloading data")
-            .build()
-        return ForegroundInfo(NOTIFICATION_ID, notification)
+        return ForegroundInfo(NOTIFICATION_ID, Notifications.makeMatoDownloadNotification(context))
     }
 
 
     companion object{
         const val TRIPS_KEYS = "tripsToDownload"
-        const val WORK_TAG = "tripsDownloaderAndInserter"
         const val DEBUG_TAG="BusTO:MatoTripDownWRK"
-        const val NOTIFICATION_ID=424242
+        const val NOTIFICATION_ID=42424221
+
+        const val TAG_TRIPS ="gtfsTripsDownload"
+
+        fun downloadTripsFromMato(trips: List<String>, context: Context, debugTag: String): Boolean{
+            if (trips.isEmpty()) return false
+            val workManager = WorkManager.getInstance(context)
+            val info = workManager.getWorkInfosForUniqueWork(TAG_TRIPS).get()
+
+            val runNewWork = if(info.isEmpty()) true
+                            else info[0].state!= WorkInfo.State.RUNNING && info[0].state!= WorkInfo.State.ENQUEUED
+            val addDat = if(info.isEmpty())
+                null else  info[0].state
+            Log.d(debugTag, "Request to download and insert ${trips.size} trips, proceed: $runNewWork, workstate: $addDat")
+            if(runNewWork) {
+                val tripsArr = trips.toTypedArray()
+                val dataBuilder = Data.Builder().putStringArray(TRIPS_KEYS, tripsArr)
+                //build()
+                val requ = OneTimeWorkRequest.Builder(MatoTripsDownloadWorker::class.java)
+                    .setInputData(dataBuilder.build()).setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
+                    .addTag(TAG_TRIPS)
+                    .build()
+                workManager.enqueueUniqueWork(TAG_TRIPS, ExistingWorkPolicy.KEEP, requ)
+            }
+            return true
+        }
     }
 }
diff --git a/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsDBDao.kt b/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsDBDao.kt
--- a/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsDBDao.kt
+++ b/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsDBDao.kt
@@ -48,6 +48,8 @@
     @Query("SELECT * FROM ${GtfsRoute.DB_TABLE} WHERE ${GtfsRoute.COL_AGENCY_ID} LIKE :agencyID")
     fun getRoutesByAgency(agencyID:String) : LiveData<List<GtfsRoute>>
 
+    @Query("SELECT ${MatoPattern.COL_CODE} FROM ${MatoPattern.TABLE_NAME} ")
+    fun getPatternsCodes(): List<String>
     @Query("SELECT * FROM ${MatoPattern.TABLE_NAME} WHERE ${MatoPattern.COL_ROUTE_ID} LIKE :routeID")
     fun getPatternsLiveDataByRouteID(routeID: String): LiveData<List<MatoPattern>>
 
@@ -65,6 +67,9 @@
     @Query("SELECT * FROM ${MatoPattern.TABLE_NAME} WHERE ${MatoPattern.COL_CODE} IN (:patternGtfsIDs)")
     fun getPatternsWithStopsFromIDs(patternGtfsIDs: List<String>) : LiveData<List<MatoPatternWithStops>>
 
+    @Query("SELECT ${MatoPattern.COL_CODE} FROM ${MatoPattern.TABLE_NAME} WHERE ${MatoPattern.COL_CODE} IN (:codes)")
+    fun getPatternsCodesInTheDB(codes: List<String>): List<String>
+
     @Transaction
     @Query("SELECT * FROM ${GtfsTrip.DB_TABLE} WHERE ${GtfsTrip.COL_TRIP_ID} IN (:tripsIds)")
     fun getTripsFromIDs(tripsIds: List<String>) : List<GtfsTrip>
@@ -73,10 +78,14 @@
     @Query("SELECT * FROM ${GtfsTrip.DB_TABLE} WHERE ${GtfsTrip.COL_TRIP_ID} IN (:trips)")
     fun getTripPatternStops(trips: List<String>): LiveData<List<TripAndPatternWithStops>>
 
+
+
     fun getRoutesForFeed(feed:String): LiveData<List<GtfsRoute>>{
         val agencyID = "${feed}:%"
         return getRoutesByAgency(agencyID)
     }
+
+
     @Transaction
     fun clearAndInsertRoutes(routes: List<GtfsRoute>){
         deleteAllRoutes()
@@ -111,6 +120,9 @@
     fun deleteAllStops()
     @Query("DELETE FROM "+GtfsTrip.DB_TABLE)
     fun deleteAllTrips()
+
+    @Query("DELETE FROM ${MatoPattern.TABLE_NAME} WHERE ${MatoPattern.COL_CODE} IN (:codes)")
+    fun deletePatternsWithCodes(codes: List<String>): Int
     @Update(onConflict = OnConflictStrategy.REPLACE)
     fun updateShapes(shapes: List<GtfsShape>) : Int
 
diff --git a/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsTrip.kt b/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsTrip.kt
--- a/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsTrip.kt
+++ b/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsTrip.kt
@@ -120,7 +120,7 @@
         parentColumn = GtfsTrip.COL_PATTERN_ID,
         entityColumn = MatoPattern.COL_CODE
     )
-    val pattern: MatoPattern,
+    val pattern: MatoPattern?,
     @Relation(
         parentColumn = MatoPattern.COL_CODE,
         entityColumn = PatternStop.COL_PATTERN_ID,
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/MapFragment.java b/app/src/main/java/it/reyboz/bustorino/fragments/MapFragment.java
--- a/app/src/main/java/it/reyboz/bustorino/fragments/MapFragment.java
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/MapFragment.java
@@ -47,6 +47,7 @@
 import it.reyboz.bustorino.backend.gtfs.GtfsPositionUpdate;
 import it.reyboz.bustorino.backend.gtfs.GtfsUtils;
 import it.reyboz.bustorino.backend.utils;
+import it.reyboz.bustorino.data.gtfs.MatoPattern;
 import it.reyboz.bustorino.data.gtfs.TripAndPatternWithStops;
 import it.reyboz.bustorino.map.*;
 import org.osmdroid.api.IGeoPoint;
@@ -346,7 +347,7 @@
             //mapViewModel.testCascade();
             mapViewModel.getTripsGtfsIDsToQuery().observe(this, dat -> {
                 Log.i(DEBUG_TAG, "Have these trips IDs missing from the DB, to be queried: "+dat);
-                mapViewModel.downloadandInsertTripsInDB(dat);
+                mapViewModel.downloadTripsFromMato(dat);
             });
         }
     }
@@ -637,11 +638,11 @@
                 if(tripWithPatternStops == null){
                     noPatternsTrips.add(tripID);
                 }
-                marker.setInfoWindow(new BusInfoWindow(map, update, tripWithPatternStops != null ? tripWithPatternStops.getPattern() : null, new BusInfoWindow.onTouchUp() {
-                    @Override
-                    public void onActionUp() {
+                MatoPattern markerPattern = null;
+                if(tripWithPatternStops != null && tripWithPatternStops.getPattern()!=null)
+                    markerPattern = tripWithPatternStops.getPattern();
+                marker.setInfoWindow(new BusInfoWindow(map, update, markerPattern , () -> {
 
-                    }
                 }));
 
                 updateBusMarker(marker, update, true);
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/MapViewModel.kt b/app/src/main/java/it/reyboz/bustorino/fragments/MapViewModel.kt
--- a/app/src/main/java/it/reyboz/bustorino/fragments/MapViewModel.kt
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/MapViewModel.kt
@@ -20,15 +20,11 @@
 import android.app.Application
 import android.util.Log
 import androidx.lifecycle.*
-import androidx.work.*
 import com.android.volley.Response
-import com.google.transit.realtime.GtfsRealtime.VehiclePosition
 import it.reyboz.bustorino.backend.NetworkVolleyManager
-import it.reyboz.bustorino.backend.Result
 import it.reyboz.bustorino.backend.gtfs.GtfsPositionUpdate
 import it.reyboz.bustorino.backend.gtfs.GtfsRtPositionsRequest
 import it.reyboz.bustorino.data.*
-import it.reyboz.bustorino.data.gtfs.GtfsTrip
 import it.reyboz.bustorino.data.gtfs.TripAndPatternWithStops
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
@@ -39,10 +35,7 @@
  */
 class MapViewModel(application: Application): AndroidViewModel(application) {
     private val gtfsRepo = GtfsRepository(application)
-    private val executor = Executors.newFixedThreadPool(2)
 
-    private val oldRepo= OldDataRepository(executor, NextGenDB.getInstance(application))
-    private val matoRepository = MatoRepository(application)
     private val netVolleyManager = NetworkVolleyManager.getInstance(application)
 
 
@@ -107,13 +100,23 @@
         //add "gtt:" prefix because it's implicit in GTFS Realtime API
         return@map it.map { pos -> "gtt:"+pos.tripID  }
     }
-    // trips that are in the DB
-    val gtfsTripsInDB = tripsIDsInUpdates.switchMap {
+    //this holds the trips that have been downloaded but for which we have no pattern
+    /*private val gtfsTripsInDBMissingPattern = tripsIDsInUpdates.map { tripsIDs ->
+        val tripsInDB = gtfsRepo.gtfsDao.getTripsFromIDs(tripsIDs)
+        val tripsPatternCodes = tripsInDB.map { tr -> tr.patternId }
+        val codesInDB = gtfsRepo.gtfsDao.getPatternsCodesInTheDB(tripsPatternCodes)
+
+        tripsInDB.filter { !(codesInDB.contains(it.patternId)) }
+    }*/
+    //private val patternsCodesInDB = tripsDBPatterns.map { gtfsRepo.gtfsDao.getPatternsCodesInTheDB(it) }
+
+    // trips that are in the DB, together with the pattern. If the pattern is not present in the DB, it's null
+    val gtfsTripsPatternsInDB = tripsIDsInUpdates.switchMap {
         //Log.i(DEBUG_TI, "tripsIds in updates changed: ${it.size}")
         gtfsRepo.gtfsDao.getTripPatternStops(it)
     }
     //trip IDs to query, which are not present in the DB
-     val tripsGtfsIDsToQuery: LiveData<List<String>> = gtfsTripsInDB.map { tripswithPatterns ->
+     val tripsGtfsIDsToQuery: LiveData<List<String>> = gtfsTripsPatternsInDB.map { tripswithPatterns ->
         val tripNames=tripswithPatterns.map { twp-> twp.trip.tripID }
         Log.i(DEBUG_TI, "Have ${tripswithPatterns.size} trips in the DB")
         if (tripsIDsInUpdates.value!=null)
@@ -124,14 +127,21 @@
         }
     }
 
-    val updatesWithTripAndPatterns = gtfsTripsInDB.map { tripPatterns->
+    val updatesWithTripAndPatterns = gtfsTripsPatternsInDB.map { tripPatterns->
         Log.i(DEBUG_TI, "Mapping trips and patterns")
         val mdict = HashMap<String,Pair<GtfsPositionUpdate, TripAndPatternWithStops?>>()
+        //missing patterns
+        val routesToDownload = HashSet<String>()
         if(positionsLiveData.value!=null)
             for(update in positionsLiveData.value!!){
                 val trID = update.tripID
                 var found = false
                 for(trip in tripPatterns){
+                    if (trip.pattern == null){
+                        //pattern is null, which means we have to download
+                        // the pattern data from MaTO
+                        routesToDownload.add(trip.trip.routeID)
+                    }
                     if (trip.trip.tripID == "gtt:$trID"){
                         found = true
                         //insert directly
@@ -140,10 +150,17 @@
                     }
                 }
                 if (!found){
+                    //Log.d(DEBUG_TI, "Cannot find pattern ${tr}")
                     //give the update anyway
                     mdict[trID] = Pair(update,null)
                 }
             }
+        //have to request download of missing Patterns
+        if (routesToDownload.size > 0){
+            Log.d(DEBUG_TI, "Have ${routesToDownload.size} missing patterns from the DB: $routesToDownload")
+            downloadMissingPatterns(ArrayList(routesToDownload))
+        }
+
         return@map mdict
     }
     /*
@@ -152,38 +169,17 @@
      1 -> wait until they are all queried to insert in the DB
      2 -> after each request finishes, insert it into the DB
 
-     Keep in mind that trips do not change very often, so they might only need to be inserted once every two months
-     TODO: find a way to avoid trips getting scrubbed (check if they are really scrubbed)
+     Keep in mind that trips DO CHANGE often, and so do the Patterns
      */
+    fun downloadTripsFromMato(trips: List<String>): Boolean{
+        return MatoTripsDownloadWorker.downloadTripsFromMato(trips,getApplication(), DEBUG_TI)
+    }
+    fun downloadMissingPatterns(routeIds: List<String>): Boolean{
+        return MatoPatternsDownloadWorker.downloadPatternsForRoutes(routeIds, getApplication())
+    }
 
     init {
-        /*
-        //what happens when the trips to query with Mato are determined
-        tripsIDsQueried.addSource(tripsGtfsIDsToQuery) { tripList ->
-            // avoid infinite loop of querying to Mato, insert in DB and
-            // triggering another query update
-
-            val tripsToQuery =
-            Log.d(DEBUG_TI, "Querying trips IDs to Mato: $tripsToQuery")
-            for (t in tripsToQuery){
-                matoRepository.requestTripUpdate(t,matoTripReqErrorList, matoTripCallback)
-                }
-            tripsIDsQueried.value = tripsToQuery
-            }
 
-        tripsToInsert.addSource(tripsFromMato) { matoTrips ->
-            if (tripsIDsQueried.value == null) return@addSource
-            val tripsIdsToInsert = matoTrips.map { trip -> trip.tripID }
-
-            //val setTripsToInsert = HashSet(tripsIdsToInsert)
-            val tripsRequested = HashSet(tripsIDsQueried.value!!)
-            val insertInDB = tripsRequested.containsAll(tripsIdsToInsert)
-            if(insertInDB){
-                gtfsRepo.gtfsDao.insertTrips(matoTrips)
-            }
-            Log.d(DEBUG_TI, "MatoTrips: ${matoTrips.size}, total trips req: ${tripsRequested.size}, inserting: $insertInDB")
-        }
-         */
         Log.d(DEBUG_TI, "MapViewModel created")
         Log.d(DEBUG_TI, "Observers of positionsLiveData ${positionsLiveData.hasActiveObservers()}")
 
@@ -198,36 +194,12 @@
             ))
         positionsLiveData.value = n
     }
-    private val queriedMatoTrips = HashSet<String>()
 
-    private val downloadedMatoTrips = ArrayList<GtfsTrip>()
-    private val failedMatoTripsDownload = HashSet<String>()
 
     /**
      * Start downloading missing GtfsTrips and Insert them in the DB
      */
-    fun downloadandInsertTripsInDB(trips: List<String>): Boolean{
-        if (trips.isEmpty()) return false
-        val workManager = WorkManager.getInstance(getApplication())
-        val info = workManager.getWorkInfosForUniqueWork(MatoDownloadTripsWorker.WORK_TAG).get()
-
-        val runNewWork = if(info.isEmpty()){
-            true
-        } else info[0].state!=WorkInfo.State.RUNNING && info[0].state!=WorkInfo.State.ENQUEUED
-        val addDat = if(info.isEmpty())
-            null else  info[0].state
-        Log.d(DEBUG_TI, "Request to download and insert ${trips.size} trips, proceed: $runNewWork, workstate: $addDat")
-        if(runNewWork) {
-            val tripsArr = trips.toTypedArray()
-            val data = Data.Builder().putStringArray(MatoDownloadTripsWorker.TRIPS_KEYS, tripsArr).build()
-            val requ = OneTimeWorkRequest.Builder(MatoDownloadTripsWorker::class.java)
-                .setInputData(data).setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
-                .addTag(MatoDownloadTripsWorker.WORK_TAG)
-                .build()
-            workManager.enqueueUniqueWork(MatoDownloadTripsWorker.WORK_TAG, ExistingWorkPolicy.KEEP, requ)
-        }
-        return true
-    }
+
 
     companion object{
         const val DEBUG_TI="BusTO-MapViewModel"
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/SettingsFragment.java b/app/src/main/java/it/reyboz/bustorino/fragments/SettingsFragment.java
--- a/app/src/main/java/it/reyboz/bustorino/fragments/SettingsFragment.java
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/SettingsFragment.java
@@ -31,13 +31,11 @@
 import androidx.lifecycle.Observer;
 import androidx.preference.*;
 import androidx.work.OneTimeWorkRequest;
-import androidx.work.OutOfQuotaPolicy;
 import androidx.work.WorkInfo;
 import androidx.work.WorkManager;
 import it.reyboz.bustorino.R;
 import it.reyboz.bustorino.data.DatabaseUpdate;
 import it.reyboz.bustorino.data.GtfsMaintenanceWorker;
-import it.reyboz.bustorino.data.MatoDownloadTripsWorker;
 import org.jetbrains.annotations.NotNull;
 
 import java.lang.ref.WeakReference;