@startuml
!theme plain
!define LANDSCAPE
!define LAYOUT_AS_SKETCH
skinparam classAttributeIconSize 0
skinparam direction left to right
skinparam classFontSize 16
skinparam classBackgroundColor white
skinparam classBorderColor black
skinparam classArrowColor black
skinparam classFontColor black
skinparam backgroundColor white
skinparam shadowing false
skinparam ranksep 400
skinparam nodesep 60
skinparam linetype ortho
skinparam classPadding 10
skinparam classMargin 15
skinparam classMinWidth 120
skinparam classMinHeight 200
skinparam classMaxWidth 250
skinparam classMaxHeight 400
skinparam classBorderThickness 2
skinparam classArrowThickness 1
skinparam classArrowHeadSize 10
skinparam classArrowTailSize 10
skinparam classArrowFontSize 10
skinparam classArrowFontStyle normal
skinparam classStereotypeFontSize 0
skinparam classStereotypeFontColor transparent
skinparam packageStyle rectangle
skinparam packageBackgroundColor transparent
skinparam packageBorderColor transparent

class NMEAParser
class AisTargetsAdapter
class MainActivity
class SettingsActivity
class AisTargetsActivity
class Repository
class AISVesselMapper
class AISVesselEntity
class CompassSensor
class UDPListener
class MapController
class NetworkController
class CompassController
class VesselPathController
class DataController
class DefaultControllersFactory
class NMEAParser
class NotificationController
class GPSLocationListener
class NMEAController
class AppCoordinator
interface ControllersFactory
class MenuBinder
interface UIDataChangeNotifier
class BottomSheetsBinder
class BottomSheetsManager
class UIRenderingCoordinator
interface MapInterface
class YandexMarkerWrapper
interface MapInterfaceChangeListener
class MapLibreMapImpl
class MarkerWrapper
class MapForgeImpl
interface MarkerManager
class YandexMarkerManager
class YandexMapImpl
class NotificationService
class GeoUtils
class CoordinatesDockWidget
class BaseDockWidget
class CompassView
class CursorOverlay

package "Utility Classes" {
  class TestAISDecoding
  class ExampleUnitTest
  class ExampleInstrumentedTest
  class AppDatabase
  class VesselEntity
  interface AISVesselDao
  interface VesselDao
  class AndroidNMEAListener
  class PermissionsBinder
  class VesselPathTracker
  class AISForegroundService
  class MIDToCountry
  class NavigationUtils
  class LogSender
  class SettingsManager
  class AISVessel
  class Vessel
  class VesselPathPoint
}

AISVesselMapper ..> AISVessel : toEntity()
AISVesselMapper ..> AISVessel : toModel()
AISVesselMapper ..> AISVesselEntity : toEntity()
AISVesselMapper ..> AISVesselEntity : toModel()
AisTargetsActivity ..> AISVesselEntity : matchesQuery()
AisTargetsActivity ..> AISVesselEntity : onChanged()
AisTargetsActivity ..> VesselEntity : onComplete()
AisTargetsActivity ..|> AisTargetsAdapter
AisTargetsActivity o-- "1" AisTargetsAdapter : adapter
AisTargetsActivity o-- "1" Repository : repository
AisTargetsAdapter --|> AISVesselEntity
AppCoordinator ..> AISVessel : addAISVesselPathPoint()
AppCoordinator ..> AISVessel : findAISVesselByMMSI()
AppCoordinator ..> AISVessel : getAISVessels()
AppCoordinator ..> AISVessel : getNearbyVessels()
AppCoordinator ..> AISVessel : onAISVesselClick()
AppCoordinator ..> AISVessel : onAISVesselUpdated()
AppCoordinator ..> AISVessel : onDataRestored()
AppCoordinator ..> MapController : setMapController()
AppCoordinator ..> MapInterface : onMapInterfaceChanged()
AppCoordinator ..> UIDataChangeNotifier : setUIDataChangeNotifier()
AppCoordinator ..> Vessel : getOwnVessel()
AppCoordinator ..> Vessel : onDataRestored()
AppCoordinator ..> Vessel : onGPSLocationUpdated()
AppCoordinator ..> Vessel : onOwnVesselClick()
AppCoordinator ..> Vessel : onVesselUpdated()
AppCoordinator ..> VesselPathController : getAISVesselPathController()
AppCoordinator ..> VesselPathController : getPathController()
AppCoordinator ..|> NMEAController
AppCoordinator o-- "1" CompassController : compassController
AppCoordinator o-- "1" DataController : dataController
AppCoordinator o-- "1" MapController : mapController
AppCoordinator o-- "1" NMEAController : nmeaController
AppCoordinator o-- "1" NetworkController : networkController
AppCoordinator o-- "1" NotificationController : notificationController
AppCoordinator o-- "1" SettingsManager : settingsManager
AppCoordinator o-- "1" UIDataChangeNotifier : uiDataNotifier
AppCoordinator o-- "1" Vessel : ownVessel
AppCoordinator o-- "1" VesselPathController : pathController
BottomSheetsBinder ..> AISVessel : showAISVesselSheet()
BottomSheetsBinder ..> AISVessel : updateAISUI()
BottomSheetsBinder o-- "1" AISVessel : currentAISVessel
BottomSheetsManager ..> AISVessel : showAISVessel()
BottomSheetsManager ..> AISVessel : updateAISBottomSheetUI()
BottomSheetsManager o-- "1" AISVessel : currentAISVessel
BottomSheetsManager o-- "1" AppCoordinator : appCoordinator
CompassController ..> AISVessel : updateCompassWithVesselData()
CompassController ..> Vessel : updateCompassWithVesselData()
CompassController ..|> CompassSensor
CompassController o-- "1" CompassSensor : compassSensor
CompassView --|> BaseDockWidget
CompassView ..> AISVessel : getVesselColor()
CompassView ..> AISVessel : updateNearbyVessels()
CompassView ..> Vessel : setOurVessel()
CompassView o-- "0..*" AISVessel : nearbyVessels
CompassView o-- "1" Vessel : ourVessel
CoordinatesDockWidget --|> BaseDockWidget
CoordinatesDockWidget ..> Vessel : updateVessel()
CoordinatesDockWidget o-- "1" Vessel : vessel
CursorOverlay ..> AISVessel : setAisVesselInfo()
CursorOverlay ..> Vessel : isValidPosition()
CursorOverlay ..> Vessel : setOwnVessel()
CursorOverlay o-- "1" AISVessel : currentAisVessel
CursorOverlay o-- "1" Vessel : ownVessel
DataController ..> AISVessel : saveAISVessel()
DataController ..> Repository : getRepository()
DataController ..> SettingsManager : getSettingsManager()
DataController ..> Vessel : saveVesselPosition()
DataController o-- "1" Repository : repository
DataController o-- "1" SettingsManager : settingsManager
DefaultControllersFactory ..> AppCoordinator : createAppCoordinator()
DefaultControllersFactory ..|> ControllersFactory
GPSLocationListener ..> Vessel : setSatellitesInVessel()
GeoUtils ..> AISVessel : calculateBearing()
GeoUtils ..> AISVessel : calculateDistance()
GeoUtils ..> Vessel : calculateBearing()
GeoUtils ..> Vessel : calculateDistance()
MainActivity ..> AISVessel : onShowAISVesselInfo()
MainActivity ..> AISVessel : onUpdateCompass()
MainActivity ..> Vessel : onGPSQualityUpdated()
MainActivity ..> Vessel : onVesselPositionUpdated()
MainActivity ..> Vessel : updateGPSQualityUI()
MainActivity ..> Vessel : updateVesselPositionUI()
MainActivity o-- "1" AppCoordinator : appCoordinator
MainActivity o-- "1" BottomSheetsBinder : bottomSheetsBinder
MainActivity o-- "1" BottomSheetsManager : bottomSheetsManager
MainActivity o-- "1" CompassController : compassController
MainActivity o-- "1" CompassView : compassView
MainActivity o-- "1" CoordinatesDockWidget : coordinatesWidget
MainActivity o-- "1" MapController : mapController
MainActivity o-- "1" MenuBinder : menuBinder
MainActivity o-- "1" PermissionsBinder : permissionsBinder
MainActivity o-- "1" SettingsManager : settingsManager
MainActivity o-- "1" UIRenderingCoordinator : uiCoordinator
MainActivity o-- "1" Vessel : lastCompassVessel
MainActivity o-- "1" Vessel : lastCoordinatesVessel
MapController ..> AppCoordinator : setAppCoordinator()
MapController ..> MapInterface : getCurrentMapInterface()
MapController ..> MapInterface : getMapInterface()
MapController ..> MapInterface : initializeMap()
MapController ..> MapInterface : initializeMapForge()
MapController ..> MapInterface : initializeMapLibre()
MapController ..> MapInterface : initializeYandexMaps()
MapController ..> MapInterface : notifyMapInterfaceChanged()
MapController ..> MapInterfaceChangeListener : addMapInterfaceChangeListener()
MapController ..> MapInterfaceChangeListener : removeMapInterfaceChangeListener()
MapController ..> MarkerManager : getMarkerManager()
MapController ..> VesselPathController : setVesselPathController()
MapController o-- "0..*" MapInterfaceChangeListener : listeners
MapController o-- "1" MapInterface : currentMapInterface
MapController o-- "1" MarkerManager : markerManager
MapForgeImpl ..> AISVessel : addAISVesselMarker()
MapForgeImpl ..> AISVessel : setAisVesselInfo()
MapForgeImpl ..> AISVessel : updateAISVesselPosition()
MapForgeImpl ..> Vessel : addOwnVesselMarker()
MapForgeImpl ..> Vessel : updateOwnVesselPosition()
MapForgeImpl ..|> MapInterface
MapForgeImpl o-- "1" CursorOverlay : cursorOverlay
MapForgeImpl o-- "1" Vessel : ownVessel
MapLibreMapImpl ..> AISVessel : addAISVesselMarker()
MapLibreMapImpl ..> AISVessel : getDisplayCourse()
MapLibreMapImpl ..> AISVessel : pickIconNameFor()
MapLibreMapImpl ..> AISVessel : setAisVesselInfo()
MapLibreMapImpl ..> AISVessel : updateAISVesselPosition()
MapLibreMapImpl ..> AISVessel : updateAISVesselPredictionSource()
MapLibreMapImpl ..> AppCoordinator : setAppCoordinator()
MapLibreMapImpl ..> Vessel : addOwnVesselMarker()
MapLibreMapImpl ..> Vessel : updateMapBatched()
MapLibreMapImpl ..> Vessel : updateMapThrottled()
MapLibreMapImpl ..> Vessel : updateOwnVesselPosition()
MapLibreMapImpl ..> Vessel : updateOwnVesselPredictionSource()
MapLibreMapImpl ..> VesselPathController : setVesselPathController()
MapLibreMapImpl ..> VesselPathController : updateAISVesselPath()
MapLibreMapImpl ..|> MapInterface
MapLibreMapImpl o-- "1" AppCoordinator : appCoordinator
MapLibreMapImpl o-- "1" CursorOverlay : cursorOverlay
MapLibreMapImpl o-- "1" SettingsManager : settingsManager
MapLibreMapImpl o-- "1" Vessel : lastOwnVessel
MapLibreMapImpl o-- "1" Vessel : ownVessel
MapLibreMapImpl o-- "1" Vessel : pendingVesselUpdate
MapLibreMapImpl o-- "1" VesselPathController : pathController
MenuBinder o-- "1" AppCoordinator : appCoordinator
MenuBinder o-- "1" SettingsManager : settingsManager
NMEAController ..> AISVessel : onAISVesselUpdated()
NMEAController ..> GPSLocationListener : getGPSLocationListener()
NMEAController ..> Vessel : onLocationUpdated()
NMEAController ..> Vessel : onVesselUpdated()
NMEAController ..|> NMEAParser
NMEAController o-- "1" AndroidNMEAListener : androidNmeaListener
NMEAController o-- "1" GPSLocationListener : gpsLocationListener
NMEAController o-- "1" NMEAParser : nmeaParser
NMEAParser ..> AISVessel : findOrCreateAISVessel()
NMEAParser ..> AISVessel : getAISVesselByMMSI()
NMEAParser ..> AISVessel : getAISVessels()
NMEAParser ..> GPSLocationListener : setGPSLocationListener()
NMEAParser ..> Vessel : getOwnVessel()
NMEAParser o-- "0..*" AISVessel : aisVessels
NMEAParser o-- "1" GPSLocationListener : gpsLocationListener
NMEAParser o-- "1" Vessel : ownVessel
NetworkController ..|> UDPListener
NetworkController o-- "1" UDPListener : udpListener
NotificationController ..> NotificationService : getNotificationService()
NotificationController o-- "1" NotificationService : notificationService
NotificationService o-- "1" SettingsManager : settingsManager
Repository ..> AISVesselEntity : getAISByMmsiSync()
Repository ..> AISVesselEntity : getAllAISSync()
Repository ..> AISVesselEntity : upsertAIS()
Repository ..> VesselEntity : getLatestOwnVesselAsync()
Repository ..> VesselEntity : getLatestOwnVesselSync()
Repository ..> VesselEntity : upsertOwnVessel()
Repository o-- "1" AISVesselDao : aisVesselDao
Repository o-- "1" VesselDao : vesselDao
SettingsActivity o-- "1" SettingsManager : settingsManager
UIRenderingCoordinator ..> AISVessel : onAISVesselChanged()
UIRenderingCoordinator ..> AISVessel : requestAISUpdate()
UIRenderingCoordinator ..> MapInterface : onMapInterfaceChanged()
UIRenderingCoordinator ..> Vessel : onGPSQualityChanged()
UIRenderingCoordinator ..> Vessel : onVesselPositionChanged()
UIRenderingCoordinator ..> Vessel : requestVesselUpdate()
UIRenderingCoordinator ..|> MapInterfaceChangeListener
UIRenderingCoordinator ..|> UIDataChangeNotifier
UIRenderingCoordinator o-- "1" MapInterface : mapInterface
UIRenderingCoordinator o-- "1" Vessel : pendingVesselUpdate
VesselPathController ..> VesselPathPoint : getLastPoint()
VesselPathController ..> VesselPathPoint : shouldAddPoint()
VesselPathController o-- "0..*" VesselPathPoint : pathPoints
VesselPathController o-- "1" SettingsManager : settingsManager
VesselPathController o-- "1" VesselPathPoint : lastPoint
YandexMapImpl ..> AISVessel : addAISVesselMarker()
YandexMapImpl ..> AISVessel : setAisVesselInfo()
YandexMapImpl ..> AISVessel : updateAISVesselPosition()
YandexMapImpl ..> Vessel : addOwnVesselMarker()
YandexMapImpl ..> Vessel : updateOwnVesselPosition()
YandexMapImpl ..|> MapInterface
YandexMapImpl o-- "1" CursorOverlay : cursorOverlay
YandexMapImpl o-- "1" Vessel : ownVessel
YandexMapImpl o-- "1" YandexMarkerManager : markerManager
YandexMarkerManager ..> AISVessel : getAISDisplayCourse()
YandexMarkerManager ..> AISVessel : isAISVesselMoving()
YandexMarkerManager ..> AISVessel : updateAISVesselMarker()
YandexMarkerManager ..> AISVessel : updateAISVesselPath()
YandexMarkerManager ..> Vessel : isVesselMoving()
YandexMarkerManager ..> Vessel : updateOwnVesselMarker()
YandexMarkerManager ..> Vessel : updateOwnVesselPath()
YandexMarkerManager ..|> MarkerManager
YandexMarkerManager o-- "1" VesselPathTracker : ownVesselPathTracker
YandexMarkerManager o-- "1" YandexMarkerWrapper : ownVesselMarker
YandexMarkerWrapper --|> MarkerWrapper
YandexMarkerWrapper ..> AISVessel : getAISVessel()
YandexMarkerWrapper ..> Vessel : getVessel()
YandexMarkerWrapper o-- "1" AISVessel : aisVessel
YandexMarkerWrapper o-- "1" Vessel : vessel
@enduml