Convert english to hangul. ('gksdudxk' -> '한영타')
    During processing, state interleaves INIT, LEAD_GIVEN, MEDI_GIVEN and TAIL_GIVEN

    :param string: english that actually represent hangul string
    :return: converted hangul string

def e2h(string):
    """
    Convert english to hangul. ('gksdudxk' -> '한영타')
    During processing, state interleaves INIT, LEAD_GIVEN, MEDI_GIVEN and TAIL_GIVEN

    :param string: english that actually represent hangul string
    :return: converted hangul string
    """
    ctx = Context(string)
    for char in ctx.input:
        hchar = E2H_MAPPING.get(char)

        if not char.isalpha() or not hchar:
            ctx.do_final()
            ctx.output.append(char)
            ctx.state = State.INIT
            continue

        if ctx.state is State.INIT:
            if hchar not in JA_LEAD:
                ctx.output.append(hchar)
            else:
                ctx.state = State.LEAD_GIVEN
                ctx.pending.append(hchar)

        elif ctx.state is State.LEAD_GIVEN:
            if hchar in MO:  # ㅇ + ㅏ
                ctx.state = State.MEDI_GIVEN
                ctx.pending.append(hchar)
            else:  # ㅇ + ㅇ
                ctx.state = State.INIT
                ctx.pending.append(hchar)
                ctx.flush()

        elif ctx.state is State.MEDI_GIVEN:
            if hchar in JA:  # ㅇ + ㅏ + ㅇ
                ctx.state = State.TAIL_GIVEN
                ctx.pending.append(hchar)
            else:
                compose_waiter = ctx.pending[-1]
                compose_tuple = JAMO_COMPOSE_TABLE.get(compose_waiter)

                if compose_tuple and hchar in compose_tuple:  # (ㅇ + ㅗ + ㅏ)
                    ctx.pending[-1] = chr((ord(compose_waiter) + compose_tuple.index(hchar) + 1))
                else:  # (ㅇ + ㅏ + ㅏ)
                    ctx.do_final()
                    ctx.state = State.INIT
                    ctx.output.append(hchar)

        elif ctx.state is State.TAIL_GIVEN:
            if hchar in MO:
                if len(ctx.composing_tails) == 2:  # (ㅇ + ㅓ + ㄵ + ㅔ ==> 언제)
                    ctx.pending.pop()  # drop 'ㄵ'
                    ctx.do_final(ctx.composing_tails.pop(0))  # (ㅇ + ㅓ + ㄴ)
                    ctx.pending.append(ctx.composing_tails.pop())  # (ㅈ)
                    ctx.pending.append(hchar)  # (ㅈ + ㅔ)
                    ctx.state = State.MEDI_GIVEN
                else:  # ㄱ,ㅏ,ㅅ  + ㅏ ==> 가,사
                    last_hchar = ctx.pending.pop()
                    ctx.do_final()
                    ctx.pending.append(last_hchar)
                    ctx.pending.append(hchar)
                    ctx.state = State.MEDI_GIVEN
            else:
                compose_waiter = ctx.pending[-1]
                compose_tuple = JAMO_COMPOSE_TABLE.get(compose_waiter)

                if compose_tuple and hchar in compose_tuple:  # (ㄷ + ㅏ + ㄹ + ㄱ)
                    ctx.pending[-1] = chr((ord(compose_waiter) + compose_tuple.index(hchar) + 1))

                    ctx.composing_tails.clear()
                    ctx.composing_tails.append(compose_waiter)
                    ctx.composing_tails.append(hchar)

                else:  # (ㄷ + ㅏ + ㅂ + ㄱ) or (ㄷ + ㅏ + ㄹ + ㄱ + ㄱ)
                    ctx.do_final()
                    ctx.state = State.LEAD_GIVEN
                    ctx.pending.append(hchar)
                    ctx.composing_tails.clear()

    ctx.do_final()  # finalize remain character if any
    return ''.join(ctx.output)