import unittest

from hamlpy.parser.core import (
    ParseException,
    Stream,
    peek_indentation,
    read_line,
    read_number,
    read_quoted_string,
    read_symbol,
    read_whitespace,
    read_word,
)
from hamlpy.parser.utils import html_escape


class ParserTest(unittest.TestCase):
    def test_read_whitespace(self):
        stream = Stream(" \t foo  \n  bar   ")

        assert read_whitespace(stream) == " \t "
        assert stream.text[stream.ptr :] == "foo  \n  bar   "

        stream.ptr += 3  # skip over foo

        assert read_whitespace(stream) == "  "
        assert stream.text[stream.ptr :] == "\n  bar   "

        assert read_whitespace(stream, include_newlines=True) == "\n  "
        assert stream.text[stream.ptr :] == "bar   "

        stream.ptr += 3  # skip over bar

        assert read_whitespace(stream) == "   "
        assert stream.text[stream.ptr :] == ""

    def test_peek_indentation(self):
        assert peek_indentation(Stream("content")) == 0
        assert peek_indentation(Stream("  content")) == 2
        assert peek_indentation(Stream("\n")) is None
        assert peek_indentation(Stream("    \n")) is None

    def test_quoted_string(self):
        stream = Stream("'hello'---")
        assert read_quoted_string(stream) == "hello"
        assert stream.text[stream.ptr :] == "---"

        stream = Stream('"this don\'t \\"x\\" hmm" not in string')
        assert read_quoted_string(stream) == 'this don\'t "x" hmm'
        assert stream.text[stream.ptr :] == " not in string"

        self.assertRaises(ParseException, read_quoted_string, Stream('"no end quote...'))

    def test_read_line(self):
        stream = Stream("line1\n line2\n\nline4\n\n")
        assert read_line(stream) == "line1"
        assert read_line(stream) == " line2"
        assert read_line(stream) == ""
        assert read_line(stream) == "line4"
        assert read_line(stream) == ""
        assert read_line(stream) is None

        assert read_line(Stream("last line  ")) == "last line  "

    def test_read_number(self):
        stream = Stream('123"')
        assert read_number(stream) == "123"
        assert stream.text[stream.ptr :] == '"'

        stream = Stream("123.4xx")
        assert read_number(stream) == "123.4"
        assert stream.text[stream.ptr :] == "xx"

        stream = Stream("0.0001   ")
        assert read_number(stream) == "0.0001"
        assert stream.text[stream.ptr :] == "   "

    def test_read_symbol(self):
        stream = Stream("=> bar")
        assert read_symbol(stream, ["=>", ":"]) == "=>"
        assert stream.text[stream.ptr :] == " bar"

        self.assertRaises(ParseException, read_symbol, Stream("foo"), ["=>"])

    def test_read_word(self):
        stream = Stream("foo_bar")
        assert read_word(stream) == "foo_bar"
        assert stream.text[stream.ptr :] == ""

        stream = Stream("foo_bar ")
        assert read_word(stream) == "foo_bar"
        assert stream.text[stream.ptr :] == " "

        stream = Stream("ng-repeat(")
        assert read_word(stream) == "ng"
        assert stream.text[stream.ptr :] == "-repeat("

        stream = Stream("ng-repeat(")
        assert read_word(stream, ("-",)) == "ng-repeat"
        assert stream.text[stream.ptr :] == "("

        stream = Stream("これはテストです...")
        assert read_word(stream) == "これはテストです"
        assert stream.text[stream.ptr :] == "..."


class UtilsTest(unittest.TestCase):
    def test_html_escape(self):
        assert html_escape("") == ""
        assert html_escape("&<>\"'") == "&amp;&lt;&gt;&quot;&#39;"
        assert html_escape('{% trans "hello" %}') == '{% trans "hello" %}'
        assert html_escape('{{ foo|default:"hello" }}') == '{{ foo|default:"hello" }}'
        assert html_escape("{% }} & %}") == "{% }} & %}"

        result = html_escape('<>{% trans "hello" %}<>{{ foo|default:"hello" }}<>')
        assert result == '&lt;&gt;{% trans "hello" %}&lt;&gt;{{ foo|default:"hello" }}&lt;&gt;'
