@startuml
skinparam shadowing false
package Interface {

    class Tamra {
        {static} -deviceChecker: DeviceChecker
        {static} -historyManager: HistoryManager
        {static} -config: TamraConfig
        {static} +status: TamraStatus
        {static} +locationStatus: TamraLocationStatus
        {static} +bluetoothStatus: TamraBluetoothStatus
        {static} +internetStatus: TamraInternetStatus
        {static} profile: TamraProfile
        {static} appId: String
        {static} appKey: String
        {static} automaticMonitoring: Bool
        {static} usingSimulation: Bool
        {static} deviceName: String
        {static} +configure(config: TamraConfig)
        {static} +histories(spot: TamraSpot) -> TamraHistories
        {static} +histories(n: Int) -> TamraHistories
        {static} +histories(period: TamraHistoryPeriod) -> TamraHistories
        {static} +logHistory(spot: TamraSpot)
    }

    Tamra --> System.DeviceChecker
    Tamra --> Data.HistoryManager

    class TamraConfig {
        appId: String
        uuid: NSUUID
        appKey: String
        profile: TamraProfile
        simulation: Bool
        automaticMonitoring: Bool
        deviceName: String
    }

    enum TamraProfile {
        DEVELOP
        TEST
        PRODUCTION
    }

    enum TamraStatus {
        NOT_INITIALIZED
        INITIALIZED
        INIT_FAILED
    }

    enum TamraLocationStatus {
        Available
        Unavailable
        Unknown
    }

    enum TamraBluetoothStatus {
        On
        Off
        Unavailable
        Unknown
    }

    enum TamraInternetStatus {
        WiFi
        Celluar
        NotReachable
        Unknown
    }

    class TamraManager {
        -repository: DataRepository
        -oreumBeaconManager: OreumBeaconManager
        -recentlyNearbySpots: TamraNearbySpots
        +delegate: TamraManagerDelegate
        +ready()
        +startMonitoring()
        +startMonitoring(forId: Int)
        +stopMonitoring()
        +stopMonitoring(forId: Int)
        +spots(): TamraNearbySpots
        oreumBeaconManagerDelegate(didEnterRegion: RegionData)
        oreumBeaconManagerDelegate(didExitRegion: RegionData)
        oreumBeaconManagerDelegate(didRangeBeacons: [RangedBeacon])
    }

    TamraManager --|> Integration.OreumBeaconManagerDelegate
    TamraManager --> Data.DataRepository
    TamraManager --> Integration.OreumBeaconManager
    TamraManager --> System.RangedBeacon

    class TamraManagerDelegate << (P, yellow) >> {
        tamraManagerDelegate(didEnterRegion: TamraRegion)
        tamraManagerDelegate(didExitRegion: TamraRegion)
        tamraManagerDelegate(didRangeSpots: TamraNearbySpots)
        tamraManagerDelegate(didReadyState: TamraManagerState)
    }

    enum TamraManagerState {
        Loaded
        LoadingFailed
        Ignored
    }

    class TamraRegion {
        -regionData: RegionData
        +id: Int
        +name: String
        isMeta: Bool
        uuid: NSUUID
    }

    TamraRegion --> Data.RegionData

    class TamraNearbySpots {
        {static} -repository: DataRepository
        -spots: [TamraSpot]
        +size: Int
        {static} fetch(rangedBeacons: [RangedBeacon], done: TamraNearbySpots -> Void)
    }

    TamraNearbySpots --> Data.DataRepository
    TamraNearbySpots --> System.RangedBeacon

    class TamraSpot {
        {static} dataRepository: DataRepository
        -beaconData: BeaconData
        -region: Region
        +proximity: TamraProximity
        +accuracy: Double
        +id: Int
        +description: String
        +indoor: Bool
        +movable: Bool
        +latitude: Double
        +longitude: Double
        +geohash: String
        +regionName: String
        from(rangedBeacon: RangedBeacon, done: TamraSpot -> Void)
        +data(done: String -> Void)
    }

    TamraSpot --> Data.DataRepository
    TamraSpot --> Data.BeaconData
    TamraSpot --> System.Region

    enum TamraProximity {
        Immediate
        Near
        Far
        Unknown
    }

    class TamraHistories {
        dataArray: [TamraHistory]
    }

    class TamraHistory {
        +spotId: Int
        +description: String
        +issuedAt: NSDate
    }

    class TamraHistoryPeriod {
        from: NSDate
        to: NSDate
    }
}

package Data {
    class BeaconData {
        id: Int
        regionId: Int
        beaconType: String
        uuid: NSUUID
        major: Int
        minor: Int
        indoor: Bool
        movable: Bool
        txPower: Int
        latitude: Double
        longitude: Double
        geohash: String
        desc: String
        beaconKey: BeaconKey
    }

    class RegionData {
        id: Int
        name: String
        regionType: String
        uuid: NSUUID
        latitude: Double
        longitude: Double
        geohash: String
        radius: Int
        desc: String
    }

    class BeaconKey << (S, white) >> {
        uuid: NSUUID
        major: Int
        minor: Int
    }

    BeaconKey --> System.Beacon

    class DataRepository {
        -deviceChecker: DeviceChecker
        -dataSource: DataSource
        -dataCache: DataCache
        acceptable: Bool
        reload(done: Bool -> Void)
        regionDatas(done: [RegionData] -> Void)
        regionData(ofId: Int, done: RegionData -> Void)
        regionData(ofUUID: NSUUID, done: RegionData -> Void)
        regionData(forMetaBeaconKey: BeaconKey, done: RegionData -> Void)
        beaconDatas(ofIds: [Int], done: [BeaconData] -> Void)
        beaconDatas(ofKeys: [BeaconKey], done: [BeaconData] -> Void)
        beaconData(ofId: Int, done: BeaconData -> Void)
        beaconData(ofKey: beaconKey, done: BeaconData -> Void)
        spots(ofBeaconId: Int, done: String -> Void)
        -updateRegionDatas(done: Bool -> Void)
        -updateBeaconDatas(done: Bool -> Void)
    }

    DataRepository --> System.DeviceChecker

    class DataSource << (P, yellow) >> {
        regionDatas(done: [RegionData] -> Void)
        beaconDatas(done: [BeaconData] -> Void)
        spot(byBeaconId: Int, done: String -> Void)
        simulatedSignals(deviceName: String, done: [SimulatedSignal] -> Void)
    }

    DataSource --> System.SimulatedSignal
    DataSource <|-- NetworkDataSource

    class NetworkDataSource {
        -deviceChecker: DeviceChecker
        storedToken: Token?
        storeToken(token: Token)
        regionDatas(done: [RegionData] -> Void)
        beaconDatas(done: [BeaconData] -> Void)
        spot(byBeaconId: Int, done: String -> Void)
        simulatedSignals(deviceName: String, done -> [SimulatedSignal] -> Void)
    }

    NetworkDataSource --> System.DeviceChecker
    NetworkDataSource --> System.SimulatedSignal

    class DataCache << (P, yellow) >> {
        regionDatas: [RegionData]
        beaconDatas: [BeaconData]
        acceptable: Bool
        regionData(byId: Int) -> RegionData
        regionData(byUUID: NSUUID) -> RegionData
        beaconData(byId: Int) -> BeaconData
        beaconData(byKey: BeaconKey) -> BeaconData
        beaconDatas(byIds: [Int]) -> [BeaconData]
        beaconDatas(byKeys: [BeaconKey]) -> [BeaconData]
    }

    DataCache <|-- RealmDataCache

    class RealmDataCache {
        regionDatas: [RegionData]
        beaconDatas: [BeaconData]
        acceptable: Bool
        regionData(byId: Int) -> RegionData
        regionData(byUUID: NSUUID) -> RegionData
        beaconData(byId: Int) -> BeaconData
        beaconData(byKey: BeaconKey) -> BeaconData
        beaconDatas(byIds: [Int]) -> [BeaconData]
        beaconDatas(byKeys: [BeaconKey]) -> [BeaconData]
    }

    class RegionForRealm {
        id: Int
        name: String
        regionType: String
        uuid: String
        latitude: Double
        longitude: Double
        geohash: String
        radius: Int
        desc: String
        {static} fromData(data: RegionData) -> RegionForRealm
    }

    class BeaconForRealm {
        id: Int
        regionId: Int
        beaconType: String
        uuid: String
        major: Int
        minor: Int
        indoor: Bool
        movable: Bool
        txPower: Int
        latitude: Double
        longitude: Double
        geohash: String
        desc: String
        toData: BeaconData
        key: BeaconKey
        {static} fromData(data: BeaconData) -> BeaconForRealm
    }

    class HistoryManager << (P, yellow) >> {
        fetch(period: TamraHistoryPeriod) -> TamraHistories
        last(n: Int) -> TamraHistories
        hasVisited(spot: TamraSpot) -> TamraHistories
        log(spot: TamraSpot)
    }

    HistoryManager <|-- BasicHistoryManager

    class BasicHistoryManager {
        -realm: Realm
        fetch(period: TamraHistoryPeriod) -> TamraHistories
        last(n: Int) -> TamraHistories
        hasVisited(spot: TamraSpot) -> TamraHistories
        log(spot: TamraSpot)
    }

    class HistoryForRealm {
        spotId: Int
        desc: String
        proximity: String
        accuracy: Double
        latitude: Double
        longitude: Double
        issuedAt: NSDate
        tamraHistory: TamraHistory
        {static} create(forSpot: TamraSpot): HistoryForRealm
    }
}

package Integration {
    class OreumBeaconManager {
        -repository: DataRepository
        -beaconManager: BeaconManager
        -sdkLogger: SDKLogger
        -rangedBeaconStabilizer: RangedBeaconStabilizer
        delegate: OreumBeaconManagerDelegate
        automaticMonitoring: Bool
        usingSimulation: Bool
        deviceName: String
        notifyRangeBeacons()
        start()
        startMonitoring()
        startMonitoring(forId: Int)
        -startMonitoring(regionData: RegionData)
        stopMonitoring()
        stopMonitoring(forId: Int)
        -stopMonitoring(regionData: RegionData)
        beaconManagerDelegate(didEnterRegion: Region)
        beaconManagerDelegate(didExitRegion: Region)
        beaconManagerDelegate(didRangeBeacons: [Beacon], inRegion: Region)
        beaconManagerDelegate(didUpdateLocation: Location)
    }

    OreumBeaconManager --|> System.BeaconManagerDelegate
    OreumBeaconManager --> Data.DataRepository
    OreumBeaconManager --> System.BeaconManager
    OreumBeaconManager --> System.RangedBeaconStabilizer
    OreumBeaconManager --> System.LocationSimulator
    OreumBeaconManager --> Data.RegionData
    OreumBeaconManager --> System.Region
    OreumBeaconManager --> System.Beacon
    OreumBeaconManager --> System.Location
    OreumBeaconManager --> System.RangedBeacon
    OreumBeaconManager --> Data.BeaconKey

    class OreumBeaconManagerDelegate << (P, yellow) >> {
        oreumBeaconManagerDelegate(didEnterRegion: Region)
        oreumBeaconManagerDelegate(didExitRegion: Region)
        oreumBeaconManagerDelegate(didRangeBeacons: [RangedBeacon])
    }

    OreumBeaconManagerDelegate --> Data.RegionData
    OreumBeaconManagerDelegate --> System.RangedBeacon
}

package System {
    class BeaconManager {
        -locationManager: CLLocationManager
        monitoringRegions: [CLBeaconRegion]
        delegate: BeaconManagerDelegate
        startMonitoring(uuid: NSUUID, identifier: String)
        stopMonitoring(uuid: NSUUID, identifier: String)
        startUpdateLocation()
        triggerRanging(forBeacons: [CLBeacon], inRegion: CLBeaconRegion)
        isOnMonitor(uuid: NSUUID)
        locationManager(manager: CLLocationManager, didStartMonitoringForRegion: CLRegion)
        locationManager(manager: CLLocationManager, didDetermineState: CLRegionState, forRegion: CLRegion)
        locationManager(manager: CLLocationManager, didEnterRegion: CLRegion)
        locationManager(manager: CLLocationManager, didExitRegion: CLRegion)
        locationManager(manager: CLLocationManager, didRangeBeacons: [CLBeacon], inRegion: CLBeaconRegion)
        locationManager(manager: CLLocationManager, didUpdateLocations: [CLLocation])
    }

    class BeaconManagerDelegate << (P, yellow) >> {
        beaconManagerDelegate(didEnterRegion: Region)
        beaconManagerDelegate(didExitRegion: Region)
        beaconManagerDelegate(didRangeBeacons: [Beacon], inRegion: [Region])
        beaconmanagerDelegate(didUpdateLocation: Location)
    }

    class Beacon {
        uuid: NSUUID
        major: Int
        minor: Int
        accuracy: Double
        proximity: Proximity
    }

    class Region {
        uuid: NSUUID
        identifier: String
        simulated: Bool
    }

    class Location {
        latitude: Double
        longitude: Double
        distanceFromLocation(location: Location): Double
    }

    enum Proximity {
        Immediate
        Near
        Far
        Unknown
    }

    class DeviceChecker {
        -locationManager: CLLocationManager
        -bluetoothManager: CBPeripheralManager
        -reachabaility: Reachability
        location: TamraLocationStatus
        bluetooth: TamraBluetoothStatus
        internet: TamraInterfaceStatus
        internetConnectable: Bool
        -initLocationChecking()
        -initBluetoothChecking()
        -initInternetChecking()
        locationManager(manager: CLLocationManager, didChangeAuthorizationStatus: CLAuthorizationStatus)
        peripheralManagerDidUpdateState(peripheral: CBPeripheralManager)
    }

    class LocationSimulator {
        -dataSource: DataSource
        -beaconManager: BeaconManager
        -deviceName: String
        start(deviceName: String)
        stop()
        getSignal()
    }

    class SimulatedBeacon {
        proximityUUID: NSUUID
        major: NSNumber
        minor: NSNumber
        proximity: CLProximity
        accuracy: CLLocationAccuracy
        rssi: Int
    }

    class RangedBeacon {
        region: Region
        beacon: Beacon
        rangedAt: NSDate
        accuracy: Double
        proximity: String
        uuid: NSUUID
        major: Int
        minor: Int
    }

    class RangedBeaconStabilizer << (P, yellow) >> {
        ranged(rangedBeacons: [RangedBeacon])
        stabledBeacons(): [RangedBeacon]
    }

    RangedBeaconStabilizer <|-- TTLNearestRangedBeaconStabilizer

    class TTLNearestRangedBeaconStabilizer {
        -ttlSecond: Double
        -minRangeCount: Int
        -rangedBeacons: [RangedBeacon]
        -removeExpired()
        ranged(rangedBeacons: [RangedBeacon])
        stabledBeacons(): [RangedBeacon]
    }

    class SimulatedSignal << (S, white) >> {
        id: Int
        accuracy: Double
        uuid: NSUUID
        major: Int
        minor: Int
    }`
}

@enduml