@startuml

set namespaceSeparator ::

namespace shape {
    abstract Shape #lightgray
}


namespace model {
    class Stamp {
		+ Stamp(ns : Namespace, id : Id)
		+ get_id() : Id
		+ get_namespace() : Namespace
		+ set_id(new_id : Stamp::Id) : void
		+ set_namespace(new_namespace : Namespace) : void
		# {static} next_id : static Id
		# id : Id
		# ns : Namespace
		- {static} choose_id(id : Id) : Id
	}

    abstract class AbstractModel {
        + add_shape(shape_ptr : shape::Shape::) : void
        + add_shapes(shape_ptrs : shape::Shape::s) : void
        + as() : std::shared_ptr<ModelType>
        + collides_with(other_model : AbstractModel) : int
        + collides_with(other_shape : shape::Shape) : int
        + collides_with(other_shape_ptr : shape::Shape) : int
        + collides_with(other_shape_ptr : shape::Shape::) : int
        + collides_with(shared_ptr<AbstractModel> other_model_ptr) : int
        + get_angle() : shape::Angle
        + get_bounding_box() : shape::Rectangle
        + get_center() : shape::Point
        + get_is_active() : bool
        + get_ptr() :
        + get_shape_ptrs() : shape::Shape::s
        + get_type_name() : String
        + is() : bool
        + set_angle(new_angle : shape::Angle) : void
        + set_center(new_center : shape::Point) : void
        + {abstract} get_serializer_ptr() : jsonify::DictSerializer::
        + {abstract} serialize(std::vector<String> exclusive_field_names) : String
        # AbstractModel(type_name : String, center : shape::Point, ns : String, id : Stamp::Id)
        # angle : shape::Angle
        # center : shape::Point
        # shape_ptrs : shape::Shape::s
        # type_name : String
        - model_mutex : mutable std::recursive_mutex
        - set_shape_angles(new_angle : shape::Angle) : void
        - set_shape_centers(new_center : shape::Point) : void
    }

    class Model {
        + {static} create(type_name : String, center : shape::Point, world_ptr : ObservingWorld) : Model
        + get_is_active() : bool
        + get_serializer_ptr() : DictSerializer
        + get_world_ptr() : ObservingWorld
        + on_notification(Notification ) : void
        + serialize(exclusive_field_names : jsonify::FieldNames) : String
        + set_angle(new_angle : shape::Angle) : void
        + set_center(new_center : shape::Point) : void
        # Model(type_name : String, center : shape::Point, world_ptr : ObservingWorld)
        # logger_ptr : logger::Logger
        # notify_about_updated() : void
        # world_ptr : ObservingWorld
        - get_updated_event_type() : controller::NotificationType
    }

    class World {
		+ {static} create(ns : String) : 
		+ World(ns : String)
		+ serialize() : String
	}

    class ObservingWorld {
        + {static} create(ns : String) :
        + ObservingWorld(ns : String, size : shape::Size)
        + collides(model_ptr : AbstractModel::) : bool
        + collides(shape_ptr : shape::Shape) : bool
        + collides(shape_ptr : shape::Shape::) : bool
        + count_collisions(shape_ptr : shape::Shape::) : int
        + despawn_model(model_id : Stamp::Id, model_ns : Stamp::Namespace) : void
        + execute_reverse_command(commander : AbstractModel::) : void
        + get_model_ptr(id : AbstractModel::Id, ns : String) : AbstractModel::
        + get_model_ptrs() : AbstractModel::s
        + get_namespace() : String
        + get_robot_count() : size_t
        + get_size() : shape::Size
        + get_x_size() : int
        + get_y_size() : int
        + on_notification(notification : Notification) : void
        + operator=(other_world : ObservingWorld) : ObservingWorld
        + set_namespace(new_namespace : String) : void
        + set_size(new_size : shape::Size) : void
        + sort_model_ptrs() : void
        + spawn_model(new_model_ptr : AbstractModel::) : void
        + start_robots() : void
        + ~ObservingWorld()
        - apply_robot_hierarchy() : void
        - get_despawned_event_type(model_ptr : AbstractModel::) : controller::NotificationType
        - get_spawned_event_type(model_ptr : AbstractModel::) : controller::NotificationType
        - handle_notification(notification : Notification) : void
        - log_despawning_model(AbstractModel::) : void
        - log_spawning_model(model_ptr : AbstractModel::) : void
        - logger_ptr : logger::Logger::
        - notify_about_despawned(model_ptr : AbstractModel::) : void
        - notify_about_spawned(model_ptr : AbstractModel::) : void
        - on_model_added(notification : Notification) : void
        - on_model_removed(notification : Notification) : void
        - on_robot_go_command(notification : Notification) : void
        - on_robot_stop_command(notification : Notification) : void
        - size : shape::Size
        - spawn_model(model_json : String) : void
        - spawn_models(models_json : String) : void
    }

    class Robot {
        + <u>spawn(...)</u>
    }

    class Wall {
        + <u>spawn(...)</u>
    }

    Stamp <|-- AbstractModel
    ObservingWorld "0..1" -> "*" AbstractModel
    ObservingWorld <|-- World
    Model <|-- Robot
    Model <|-- Wall
    World "1" <- "*" Model
    AbstractModel <|-- Model
}

model::AbstractModel "1" -> "*" shape::Shape

@enduml
