from typing import List
from typing import Optional


class TokenParser(object):
    """
    Parses tokens from a string passed to StringArgs.
    """

    def __init__(self):  # type: () -> None
        self._string = ""  # type: str
        self._cursor = 0  # type: int
        self._current = None  # type: Optional[str]
        self._next_ = None  # type: Optional[str]

    def parse(self, string):  # type: (str) -> List[str]
        self._string = string
        self._cursor = 0
        self._current = None
        if len(string) > 0:
            self._current = string[0]

        self._next_ = None
        if len(string) > 1:
            self._next_ = string[1]

        tokens = self._parse()

        return tokens

    def _parse(self):  # type: () -> List[str]
        tokens = []

        while self._is_valid():
            if self._current.isspace():
                # Skip spaces
                self._next()

                continue

            if self._is_valid():
                tokens.append(self._parse_token())

        return tokens

    def _is_valid(self):  # type: () -> bool
        return self._current is not None

    def _next(self):  # type: () -> None
        """
        Advances the cursor to the next position.
        """
        if not self._is_valid():
            return

        self._cursor += 1
        self._current = self._next_

        if self._cursor + 1 < len(self._string):
            self._next_ = self._string[self._cursor + 1]
        else:
            self._next_ = None

    def _parse_token(self):  # type: () -> str
        token = ""

        while self._is_valid():
            if self._current.isspace():
                self._next()

                break

            if self._current == "\\":
                token += self._parse_escape_sequence()
            elif self._current in ["'", '"']:
                token += self._parse_quoted_string()
            else:
                token += self._current
                self._next()

        return token

    def _parse_quoted_string(self):  # type: () -> str
        string = ""
        delimiter = self._current

        # Skip first delimiter
        self._next()
        while self._is_valid():
            if self._current == delimiter:
                # Skip last delimiter
                self._next()

                break

            if self._current == "\\":
                string += self._parse_escape_sequence()
            elif self._current == '"':
                string += '"{}"'.format(self._parse_quoted_string())
            elif self._current == "'":
                string += "'{}'".format(self._parse_quoted_string())
            else:
                string += self._current
                self._next()

        return string

    def _parse_escape_sequence(self):  # type: () -> str
        if self._next_ in ['"', "'"]:
            sequence = self._next_
        else:
            sequence = "\\" + self._next_

        self._next()
        self._next()

        return sequence
