@startuml
' Interfaces
interface JFrame
interface JPanel

interface Observer {
    + update(): void
}

interface Observable {
    + addObserver(observer: Observer): void
    + removeObserver(observer: Observer): void
    + notifyObservers(event: Event): void
}

' Event Classes
class Event {}
class UIEvent extends Event {}
class SaveEvent extends Event {}

' WindowState
enum WindowState {
    MENU
    GAME
}

' Main Frame and Panels
class MainFrame {
    - commandPanel: CommandPanel
    - graphicsPanel: GraphicsPanel
}

MainFrame -|> JFrame
MainFrame --> CommandPanel
MainFrame --> GraphicsPanel

class CommandPanel implements Observer {
    - commandPanelHandler: CommandPanelHandler
    + setHandler(handler: CommandPanelHandler): void
    awsHandler
}



CommandPanel -|> JPanel
CommandPanel --- CommandPanelHandler

class CommandPanelHandler {
    - commandPanel: CommandPanel
    + handleCommand(command: Command): void
}

CommandPanelHandler ------> AppHandler

class GraphicsPanel implements Observer {
    + draw(): void
}

GraphicsPanel -|> JPanel

' Facade and Game State
class AppFacade {
    - appHandler: AppHandler
    - awsHandler: AWSHandler
    - mainFrame: MainFrame
    + start(): void
}

class AppHandler implements Observable {
    - AppHandler instance
    + {static} getInstance(): AppHandler
    + getAppState(): AppState
    + startNewGame(): void
    + startSavedGame(gameID: int): void
    + getSavedGames(): int
    + exitToMenu(): void
    + move(locationID: int): void
    + pickUpItem(entityID: int): void
    + dropItem(entityID: int): void
    + getHelp(): String
    + getDialogue(entityID: int): String
    + viewEntity(entityID: int): String
    + notifyUI(): void
    + notifySave(): void
}

AppHandler --> AppState
AppFacade --> AWSHandler
AppFacade ---> MainFrame

class AppState {
    + {static} getInstance(): AppState
    - AppState instance
    - lastUserQueryResult: UserQueryResult
    - gameState: GameState
    - currentWindow: WindowState
    + getGameState(): void
    + getCurrentWindow(): WindowState
}


AppFacade ------> AppHandler
AppState --> WindowState
AppState --> GameState

class UserQueryResult {
    - result: String
    - success: boolean
    + getResult(): String
    + getSuccess(): boolean
    + setResult(result: String): void
    + setSuccess(success: boolean): void
}

AppState --> UserQueryResult


class GameState {
    - pirate: Pirate
    - map: Map
    + getPirate(): Pirate
    + getMap(): Map
}

class AWSHandler implements Observer {
    + saveToS3(json: String): void
    + loadFromS3(): String
}

AWSHandler ------> AppHandler

' Translator
class GameStateTranslator {
    + {static} jsonToGameState(json: String): GameState
    + {static} gameStateToJson(gameState: GameState): String
}

AppHandler --> GameStateTranslator
AWSHandler --> GameStateTranslator

' Game Entities
class Pirate {
    - lives: int
    - backpack: Backpack
    + getMaxLives(): int
    + getcurrentLives(): int
    + getBackpack(): Backpack
}

Pirate --> Backpack

class Backpack {
    - maxWeight: int
    - items: ArrayList<CollectableItem>
    + addItem(item: CollectableItem): void
    + removeItem(id: int): void
    + getMaxWeight(): int
    + getTotalWeight(): int
}

class Map {
    - locations: ArrayList<Location>
    - obstacles: Map<Pair<int, int>, Obstacle>
    - currentPirateLocationID: int
    + getLocationById(id: int): Location
    + getObstacleByLocationsID(int, int): Obstacle
    + getPirateLocationID(id: int): int
}

Map--> Obstacle

GameState --> Pirate
GameState --> Map

class Location {
    - id: int
    - name: String
    - description: String
    - entities: ArrayList<Entity>
    - adjacentLocations: ArrayList<int>
    + getID(): int
    + getName(): String
    + getDescription(): String
    + getEntities(): List<Entity>
    + getAdjacentLocationIDs(): List<int>
}

Map --> Location

interface Entity {
    - ID: int
    - name: String
    - description: String
    + getID(): int
    + getName(): String
    + getDescription(): String
}

class CollectableItem implements Entity {
    - weight: int
    - requiredEntityID: int
    + getWeight(): int
    + getRequiredEntityID(): int
}

class ViewableItem implements Entity {
}

class NPC implements Entity {
    - dialogue: String
    + getDialogue(): int
}

class Obstacle implements Entity {
    - itemToDefeatID: int
    + getItemToDefeatID(): int
}

Location --> Entity

class InvalidRequestException implements Exception {
}
@enduml