package org.altbeacon.beacon.service;

import org.altbeacon.beacon.Beacon;

import java.io.Serializable;
import java.util.HashMap;

/**
 * Keeps track of beacons that have ever been seen and
 * merges them together depending on configured beacon parsers
 * Created by dyoung on 5/5/15.
 */
public class ExtraDataBeaconTracker implements Serializable {
    private static final String TAG = "BeaconTracker";
    // This is a lookup table to find tracked beacons by the calculated beacon key
    private HashMap<String,HashMap<Integer,Beacon>> mBeaconsByKey = new HashMap<String,HashMap<Integer,Beacon>>();

    private boolean matchBeaconsByServiceUUID = true;
    public ExtraDataBeaconTracker() {
    }

    public ExtraDataBeaconTracker(boolean matchBeaconsByServiceUUID) {
        this.matchBeaconsByServiceUUID = matchBeaconsByServiceUUID;
    }

    /**
     * Tracks a beacon. For Gatt-based beacons, returns a merged copy of fields from multiple
     * frames.  Returns null when passed a Gatt-based beacon that has is only extra beacon data.
     *
     * @param beacon
     * @return
     */
    public synchronized Beacon track(Beacon beacon) {
        Beacon trackedBeacon = null;
        if (beacon.isMultiFrameBeacon() || beacon.getServiceUuid() != -1) {
            trackedBeacon = trackGattBeacon(beacon);
        }
        else {
            trackedBeacon = beacon;
        }
        return trackedBeacon;
    }

    // The following code is for dealing with merging data fields in beacons
    private Beacon trackGattBeacon(Beacon beacon) {
        Beacon trackedBeacon = null;
        HashMap<Integer,Beacon> matchingTrackedBeacons = mBeaconsByKey.get(getBeaconKey(beacon));
        if (matchingTrackedBeacons != null) {
            for (Beacon matchingTrackedBeacon: matchingTrackedBeacons.values()) {
                if (beacon.isExtraBeaconData()) {
                    matchingTrackedBeacon.setRssi(beacon.getRssi());
                    matchingTrackedBeacon.setExtraDataFields(beacon.getDataFields());
                }
                else {
                    beacon.setExtraDataFields(matchingTrackedBeacon.getExtraDataFields());
                    // replace the tracked beacon instance with this one so it has updated values
                    trackedBeacon = beacon;
                }
            }
        }
        if (!beacon.isExtraBeaconData()) {
            updateTrackingHashes(beacon, matchingTrackedBeacons);
        }

        if (trackedBeacon == null && !beacon.isExtraBeaconData()) {
            trackedBeacon = beacon;
        }
        return trackedBeacon;
    }

    private void updateTrackingHashes(Beacon trackedBeacon, HashMap<Integer,Beacon> matchingTrackedBeacons) {
        if (matchingTrackedBeacons == null) {
            matchingTrackedBeacons = new HashMap<Integer,Beacon>();
        }
        matchingTrackedBeacons.put(trackedBeacon.hashCode(), trackedBeacon);
        mBeaconsByKey.put(getBeaconKey(trackedBeacon), matchingTrackedBeacons);
    }

    private String getBeaconKey(Beacon beacon) {
        if (matchBeaconsByServiceUUID) {
            return beacon.getBluetoothAddress() + beacon.getServiceUuid();
        } else {
            return beacon.getBluetoothAddress();
        }
    }
}

@@ -32,16 +32,104 @@
 rvice;%0A%0A
+import android.support.annotation.NonNull;%0Aimport android.support.annotation.Nullable;%0A%0A
 import o
@@ -488,22 +488,31 @@
 acker%22;%0A
+%0A
     /
-/
+**%0A     *
  This is
@@ -580,16 +580,37 @@
 con key%0A
+     */%0A    @NonNull%0A
     priv
@@ -613,16 +613,22 @@
 private 
+final 
 HashMap%3C
@@ -691,38 +691,8 @@
 Map%3C
-String,HashMap%3CInteger,Beacon%3E
 %3E();
@@ -705,16 +705,22 @@
 private 
+final 
 boolean 
@@ -748,16 +748,10 @@
 UUID
- = true
 ;
+%0A
 %0A   
@@ -781,24 +781,44 @@
 Tracker() %7B%0A
+        this(true);%0A
     %7D%0A%0A    p
@@ -1074,17 +1074,16 @@
  frames.
- 
  Returns
@@ -1166,52 +1166,23 @@
    *
+/
 %0A    
- * @param beacon%0A     * @return%0A     */
+@Nullable
 %0A   
@@ -1215,16 +1215,25 @@
 n track(
+@NonNull 
 Beacon b
@@ -1520,17 +1520,25 @@
 %7D%0A%0A    /
-/
+**%0A     *
  The fol
@@ -1600,16 +1600,38 @@
 beacons%0A
+     */%0A    @Nullable%0A
     priv
@@ -1657,16 +1657,25 @@
 tBeacon(
+@NonNull 
 Beacon b
@@ -2776,16 +2776,25 @@
 gHashes(
+@NonNull 
 Beacon t
@@ -2806,16 +2806,26 @@
 dBeacon,
+ @Nullable
  HashMap
@@ -2962,30 +2962,16 @@
 HashMap%3C
-Integer,Beacon
 %3E();%0A   
@@ -3173,16 +3173,25 @@
 aconKey(
+@NonNull 
 Beacon b

Use annotations and final fields to express intent