#!/usr/bin/env python
# -*- coding: utf-8 -*-

import codecs
import json
import os
import re
from urllib.parse import urljoin
from urllib.request import urlretrieve

from ply import lex

from chgksuite.common import init_logger, DummyLogger
from chgksuite.typotools import recursive_typography as rt

re_list = re.compile(r"^\s{3}\d+\.\s(.+)$", re.I | re.U)

tokens = (
    "TITLE",
    "URL",
    "DATE",
    "EDITOR",
    "INFO",
    "TOUR",
    "QUESTION",
    "HANDOUT",
    "PIC",
    "ANSWER",
    "ZACHET",
    "NEZACHET",
    "COMMENT",
    "SOURCE",
    "AUTHOR",
    "TEXT",
)

states = (
    ("title", "exclusive"),
    ("url", "exclusive"),
    ("date", "exclusive"),
    ("editor", "exclusive"),
    ("info", "exclusive"),
    ("tour", "exclusive"),
    ("question", "exclusive"),
    ("handout", "exclusive"),
    ("pic", "exclusive"),
    ("answer", "exclusive"),
    ("zachet", "exclusive"),
    ("nezachet", "exclusive"),
    ("comment", "exclusive"),
    ("source", "exclusive"),
    ("author", "exclusive"),
)

DB_PIC_BASE_URL = "http://db.chgk.info/images/db/"
DB_AUD_BASE_URL = "http://db.chgk.info/sounds/db/"

logger = DummyLogger()


def append_question(lexer):
    if lexer.question:
        # remove empty values
        question = dict((k, v) for k, v in iter(lexer.question.items()) if v)
        lexer.structure.append(["Question", question])


def init_question(lexer):
    # save old values
    append_question(lexer)
    lexer.question_num += 1
    lexer.question = {
        "number": lexer.question_num,
        "question": [],
        "answer": [],
        "comment": [],
        "source": [],
    }


def t_TITLE(t):
    r"Чемпионат:\n"
    t.lexer.begin("title")
    t.lexer.text = ""


def t_URL(t):
    r"URL:\n"
    t.lexer.begin("url")
    t.lexer.text = ""


def t_DATE(t):
    r"Дата:\n"
    t.lexer.begin("date")
    t.lexer.text = ""


def t_EDITOR(t):
    r"Редактор:\n"
    t.lexer.begin("editor")
    t.lexer.text = ""


def t_INFO(t):
    r"Инфо:\n"
    t.lexer.begin("info")
    t.lexer.text = ""


def t_TOUR(t):
    r"Тур:\n"
    t.lexer.begin("tour")
    init_question(t.lexer)
    t.lexer.text = ""


def t_QUESTION(t):
    r"Вопрос\s+[\d]+:\n"
    t.lexer.begin("question")
    init_question(t.lexer)
    t.lexer.text = ""


def t_ANSWER(t):
    r"Ответ:\n"
    t.lexer.begin("answer")
    t.lexer.text = ""
    if t.lexer.question["answer"]:
        logger.warning(
            "Bad format: several Answer fields. " "Previous Answer was:" " '%s'",
            t.lexer.question["answer"],
        )


def t_ZACHET(t):
    r"Зачет:\n"
    t.lexer.begin("zachet")
    t.lexer.text = ""


def t_NEZACHET(t):
    r"Незачет:\n"
    t.lexer.begin("nezachet")
    t.lexer.text = ""


def t_COMMENT(t):
    r"Комментарий:\n"
    t.lexer.begin("comment")
    t.lexer.text = ""
    if t.lexer.question["comment"]:
        logger.warning(
            "Bad format: several Comment fields. " "Previous Comment was:" " '%s'",
            t.lexer.question["comment"],
        )


def t_SOURCE(t):
    r"Источник:\n"
    t.lexer.begin("source")
    t.lexer.text = ""
    if t.lexer.question["source"]:
        logger.warning(
            "Bad format: several Source fields. " "Previous Source was:" " '%s'",
            t.lexer.question["source"],
        )


def t_AUTHOR(t):
    r"Автор:\n"
    t.lexer.begin("author")
    t.lexer.text = ""


def t_title_end(t):
    r"\n\n"
    t.lexer.structure.append(["heading", rt(t.lexer.text)])
    t.lexer.structure.append(["ljheading", rt(t.lexer.text)])
    t.lexer.begin("INITIAL")


def t_url_end(t):
    r"\n\n"
    t.lexer.structure.append(["meta", t.lexer.text])
    t.lexer.begin("INITIAL")


def t_date_end(t):
    r"\n\n"
    t.lexer.structure.append(["date", t.lexer.text])
    t.lexer.begin("INITIAL")


def t_info_end(t):
    r"\n\n"
    t.lexer.structure.append(["meta", rt(t.lexer.text)])
    t.lexer.begin("INITIAL")


def t_editor_end(t):
    r"\n\n"
    t.lexer.structure.append(["editor", rt(t.lexer.text)])
    t.lexer.begin("INITIAL")


def t_tour_end(t):
    r"\n\n"
    t.lexer.structure.append(["tour", rt(t.lexer.text)])
    t.lexer.begin("INITIAL")


def t_question_HANDOUT(t):
    r"\s{3}<раздатка>\n"
    t.lexer.text += "[Раздаточный материал:"
    t.lexer.begin("handout")


def t_handout_end(t):
    r"\s{3}</раздатка>\n"
    t.lexer.text += "\n]"
    t.lexer.begin("question")


def t_question_PIC(t):
    r"(?:\((?:img|aud)\s(?:[\d\.\w]+)\)\s*)+\n"
    t.lexer.text += "[Раздаточный материал:%s]" % t.value.strip()


def t_question_TEXT(t):
    r".+"
    match_list = re_list.search(t.value)
    if match_list:
        if t.lexer.text:
            multi_question_num = len(t.lexer.question["question"])
            if multi_question_num == 0:
                t.lexer.question["question"].append(t.lexer.text)
            elif multi_question_num == 1:
                t.lexer.question["question"].append([])
                t.lexer.question["question"][1].append(t.lexer.text)
            else:
                t.lexer.question["question"][1].append(t.lexer.text)
        t.lexer.text = match_list.group(1)
    else:
        if t.value[0:3] == "   ":
            t.lexer.text += "\n" + t.value[3:]
        else:
            t.lexer.text += t.value


def t_question_end(t):
    r"\n\n"
    if len(t.lexer.question["question"]) == 2:
        t.lexer.question["question"][1].append(t.lexer.text)
    else:
        t.lexer.question["question"] = t.lexer.text
    t.lexer.question["question"] = rt(t.lexer.question["question"])
    t.lexer.begin("INITIAL")


def t_answer_TEXT(t):
    r".+"
    match_list = re_list.search(t.value)
    if match_list:
        if t.lexer.text:
            t.lexer.question["answer"].append(t.lexer.text)
        t.lexer.text = match_list.group(1)
    else:
        if t.value[0:3] == "   ":
            t.lexer.text += "\n" + t.value[3:]
        else:
            t.lexer.text += t.value


def t_answer_end(t):
    r"\n\n"
    if t.lexer.question["answer"]:
        if isinstance(t.lexer.question["answer"], list):
            t.lexer.question["answer"].append(t.lexer.text)
        else:
            # bad format: several Answer fields for given question
            t.lexer.question["answer"] += "\n" + t.lexer.text
    else:
        t.lexer.question["answer"] = t.lexer.text
    t.lexer.question["answer"] = rt(t.lexer.question["answer"])
    t.lexer.begin("INITIAL")


def t_zachet_end(t):
    r"\n\n"
    t.lexer.question["zachet"] = rt(t.lexer.text)
    t.lexer.begin("INITIAL")


def t_nezachet_end(t):
    r"\n\n"
    t.lexer.question["nezachet"] = rt(t.lexer.text)
    t.lexer.begin("INITIAL")


def t_comment_TEXT(t):
    r".+"
    match_list = None

    # check if Comment already started, interpret list items as text
    if isinstance(t.lexer.question["comment"], list):
        match_list = re_list.search(t.value)

    if match_list:
        if t.lexer.text:
            t.lexer.question["comment"].append(t.lexer.text)
        t.lexer.text = match_list.group(1)
    else:
        if (
            isinstance(t.lexer.question["comment"], list)
            and not t.lexer.question["comment"]
            and not t.lexer.text
        ):
            # Comment started with some text, interpret it as text
            t.lexer.question["comment"] = ""
        if t.value[0:3] == "   ":
            t.lexer.text += "\n" + t.value[3:]
        else:
            t.lexer.text += t.value


def t_comment_end(t):
    r"\n\n"
    if t.lexer.question["comment"]:
        if isinstance(t.lexer.question["comment"], list):
            # multicomment (doublet, blitz, etc.)
            t.lexer.question["comment"].append(t.lexer.text)
        else:
            # bad format: several Comment fields for given question
            t.lexer.question["comment"] += "\n" + t.lexer.text
    else:
        t.lexer.question["comment"] = t.lexer.text
    t.lexer.question["comment"] = rt(t.lexer.question["comment"])
    t.lexer.begin("INITIAL")


def t_source_TEXT(t):
    r".+"
    match_list = re_list.search(t.value)
    if match_list:
        if not isinstance(t.lexer.question["source"], list):
            # bad format: several Source fields for given question
            t.lexer.question["source"] = [t.lexer.question["source"]]

        if t.lexer.text:
            t.lexer.question["source"].append(t.lexer.text)
        t.lexer.text = match_list.group(1)
    else:
        if t.value[0:3] == "   ":
            t.lexer.text += "\n" + t.value[3:]
        else:
            t.lexer.text += t.value


def t_source_end(t):
    r"\n\n"
    if t.lexer.question["source"]:
        if isinstance(t.lexer.question["source"], list):
            # list of sources
            t.lexer.question["source"].append(t.lexer.text)
        else:
            # bad format: several Source fields for given question
            t.lexer.question["source"] += "\n" + t.lexer.text
    else:
        t.lexer.question["source"] = t.lexer.text
    t.lexer.question["source"] = rt(t.lexer.question["source"])
    t.lexer.begin("INITIAL")


def t_author_end(t):
    r"\n\n"
    t.lexer.question["author"] = rt(t.lexer.text)
    t.lexer.begin("INITIAL")


def t_ANY_TEXT(t):
    r".+"
    if t.value[0:3] == "   ":
        # new line start
        t.lexer.text += "\n" + t.value[3:]
    else:
        t.lexer.text += t.value
    return t


def t_ANY_ENDLINE(t):
    r"\n"
    t.lexer.text += " "


def t_ANY_error(t):
    logger.warning("Illegal character '%s'", t.value[0])
    t.lexer.skip(1)


def replace_handouts(match_handout):
    handout_type = "img" if match_handout.group(1) == "pic" else "aud"
    db_base_url = (
        DB_PIC_BASE_URL if match_handout.group(1) == "pic" else DB_AUD_BASE_URL
    )
    handout_name = match_handout.group(2)
    handout_path = os.path.abspath(handout_name)
    if not os.path.exists(handout_path):
        handout_url = urljoin(db_base_url, handout_name)
        try:
            urlretrieve(handout_url, handout_path)
        except Exception as e:
            logger.warning(
                "Can't get file from %s to %s: %s", handout_url, handout_path, str(e)
            )
    return "(%s %s)" % (handout_type, handout_name)


def chgk_parse_db(text, debug=False, logger=False):

    if not logger:
        logger = init_logger("parser_db", debug=debug)

    re_handout = re.compile(r"\((pic|aud):\s([\d\.\w]+)\)", re.I | re.U)
    text = re_handout.sub(replace_handouts, text)

    lexer = lex.lex(reflags=re.I | re.U)
    lexer.text = ""
    lexer.structure = []
    lexer.question_num = 0
    lexer.question = {}
    lexer.input(text)
    for _ in iter(lexer.token, None):
        pass

    # save last question
    append_question(lexer)

    if debug:
        with codecs.open("debug_final.json", "w", "utf8") as f:
            f.write(json.dumps(lexer.structure, ensure_ascii=False, indent=4))

    return lexer.structure


def main():
    print("This program was not designed to run standalone.")


if __name__ == "__main__":
    main()
