The reason Emacs "starts to undo after all of the possible redos are done" is that, after you interrupted the original undo sequence with C-g, every change you had made up until that point became part of the tree to be undone. Those changes include not only the initial text you entered ("aa bb cc") but also the undoing of those changes. As the manual explains: "Any command other than an undo command breaks the sequence of undo commands. Starting from that moment, the entire sequence of undo commands that you have just performed are themselves placed into the undo record."
This is not how undo works in most other text editors, and some people find this behavior counterintuitive. However, this type of undo is very powerful, since it allows you to recover any past state of a buffer. You may want to try the package undo-tree, which replaces the native undo Emacs feature with a more intuitive equivalent, without sacrificing its power. Its commands undo-tree-undo and undo-tree-redo will behave like the undo and redo you are used to, but you can also see a visualization of the undo tree branches (undo-tree-visualize), and switch branches if needed (undo-tree-switch-branch).
ADDED: Check the comments to this answer by ideasman42 and Phil Hudson for some issues with undo-tree, and note that as ideasman42 mentions in another answer, Emacs 28 offers the command undo-redo out of the box.