+typedef enum {
+ UNDO_ADD,
+ UNDO_DELETE,
+ UNDO_UPDATE,
+ UNDO_SWAP
+} UndoType;
+
+// Delete, Update
+typedef struct {
+ UndoType type;
+ RectLayer *layer;
+ size_t index;
+ Rect rect;
+ Color color;
+ Action action;
+ char id[ENTITY_MAX_ID_SIZE];
+} UndoElementContext;
+
+// Add
+typedef struct {
+ UndoType type;
+ RectLayer *layer;
+ size_t index;
+} UndoAddContext;
+
+// Swap
+typedef struct {
+ UndoType type;
+ RectLayer *layer;
+ size_t index1;
+ size_t index2;
+} UndoSwapContext;
+
+typedef union {
+ UndoType type;
+ UndoAddContext add;
+ UndoElementContext element;
+ UndoSwapContext swap;
+} UndoContext;
+
+static
+UndoContext create_undo_add_context(RectLayer *layer, size_t index)
+{
+ trace_assert(layer);
+ trace_assert(index < dynarray_count(layer->rects));
+
+ UndoContext undo_context;
+ undo_context.add.type = UNDO_ADD;
+ undo_context.add.layer = layer;
+ undo_context.add.index = index;
+ return undo_context;
+}
+
+static
+UndoContext create_undo_element_context(RectLayer *layer)
+{
+ trace_assert(layer);
+ size_t index = (size_t) layer->selection;
+ trace_assert(index < dynarray_count(layer->rects));
+
+ UndoContext undo_context;
+ undo_context.element.layer = layer;
+ undo_context.element.index = index;
+ dynarray_copy_to(layer->rects, &undo_context.element.rect, index);
+ dynarray_copy_to(layer->colors, &undo_context.element.color, index);
+ dynarray_copy_to(layer->ids, undo_context.element.id, index);
+ dynarray_copy_to(layer->actions, &undo_context.element.action, index);
+ return undo_context;
+}
+
+static
+UndoContext create_undo_update_context(RectLayer *rect_layer)
+{
+ UndoContext undo_context = create_undo_element_context(rect_layer);
+ undo_context.type = UNDO_UPDATE;
+ return undo_context;
+}
+
+static
+UndoContext create_undo_delete_context(RectLayer *rect_layer)
+{
+ UndoContext undo_context = create_undo_element_context(rect_layer);
+ undo_context.type = UNDO_DELETE;
+ return undo_context;
+}
+
+static
+UndoContext create_undo_swap_context(RectLayer *rect_layer, size_t index1, size_t index2)
+{
+ UndoContext undo_context;
+ undo_context.swap.type = UNDO_SWAP;
+ undo_context.swap.layer = rect_layer;
+ undo_context.swap.index1 = index1;
+ undo_context.swap.index2 = index2;
+ return undo_context;
+}
+
+static
+void rect_layer_undo(void *context, size_t context_size)
+{
+ trace_assert(context);
+ trace_assert(sizeof(UndoContext) == context_size);
+
+ UndoContext *undo_context = context;
+
+ switch (undo_context->type) {
+ case UNDO_ADD: {
+ RectLayer *layer = undo_context->add.layer;
+ dynarray_delete_at(layer->rects, undo_context->add.index);
+ dynarray_delete_at(layer->colors, undo_context->add.index);
+ dynarray_delete_at(layer->ids, undo_context->add.index);
+ dynarray_delete_at(layer->actions, undo_context->add.index);
+ layer->selection = -1;
+ } break;
+
+ case UNDO_DELETE: {
+ RectLayer *layer = undo_context->element.layer;
+ dynarray_insert_before(layer->rects, undo_context->element.index, &undo_context->element.rect);
+ dynarray_insert_before(layer->colors, undo_context->element.index, &undo_context->element.color);
+ dynarray_insert_before(layer->ids, undo_context->element.index, &undo_context->element.id);
+ dynarray_insert_before(layer->actions, undo_context->element.index, &undo_context->element.action);
+ layer->selection = -1;
+ } break;
+
+ case UNDO_UPDATE: {
+ RectLayer *layer = undo_context->element.layer;
+ dynarray_replace_at(layer->rects, undo_context->element.index, &undo_context->element.rect);
+ dynarray_replace_at(layer->colors, undo_context->element.index, &undo_context->element.color);
+ dynarray_replace_at(layer->ids, undo_context->element.index, &undo_context->element.id);
+ dynarray_replace_at(layer->actions, undo_context->element.index, &undo_context->element.action);
+ } break;