#!/usr/bin/python3
import json
import requests
import sys
import os
import re
from bs4 import BeautifulSoup

alphabet = [
    "A", "Ą", "B", "C", "Ć", "D", "E", "Ę", "F", "G", "H", "I",
    "J", "K", "L", "Ł", "M", "N", "Ń", "O", "Ó", "P", "Q", "R",
    "S", "Ś", "T", "U", "V", "W", "X", "Y", "Z", "Ż", "Ź",
]

# make a blank puzzle
def make_blank_puzzle(width, height):
    puzzle = []
    for row in range(height):
        puzzle.append(["#"] * width)
    return puzzle


# get the puzzle layout
def get_layout(soup: BeautifulSoup):
    crossword = soup.find("table", id="krzyzowka")
    width = len(crossword.find_all("tr")[0].find_all("td"))
    height = len(crossword.find_all("tr"))

    puzzle = make_blank_puzzle(width, height)

    y = -1
    for tr in crossword.find_all("tr"):
        y = y + 1
        x = -1
        for td in tr.find_all("td"):
            x = x + 1
            td_class = td.get_attribute_list("class")[0]
            if td_class == "black":
                puzzle[y][x] = "#"
            elif td_class == "white":
                puzzle[y][x] = 0
                wnum = td.find_all("p", class_="wnum")
                if len(wnum):
                    num = int(wnum[0].get_text(strip=True))
                    puzzle[y][x] = num

    return puzzle


# get the clues from the entry
def get_clues(soup: BeautifulSoup):

    clues = dict()
    clues["Across"] = []
    clues["Down"] = []

    for direction_pair in [["poziomo_ul", "Across"], ["pionowo_ul", "Down"]]:
        direction_class = direction_pair[0]
        direction_name = direction_pair[1]
        for li in soup.find("ul", class_=direction_class).find_all("li"):
            a = li.find("a")
            number = int(a.get_attribute_list("id")[0].lstrip("q"))
            text = re.sub(r"^[0-9]+.\s*", "", a.get_text(strip=True))
            clues[direction_name].append([number, text])

    return clues


# get the solution from the entries
def get_solution(puzzle, crossword_id: int):
    height = len(puzzle)
    width = len(puzzle[0])

    solution_puzzle = make_blank_puzzle(width, height)

    # This is really wack, but we'll have to brute-force the solution
    # by trying all alphabet letters and making 35 requests...
    input_nums = []

    for y in range(0, len(puzzle)):
        for x in range(0, len(puzzle[y])):
            cell = puzzle[y][x]
            if cell != "#":
                input_num = y * width + x + 1
                input_nums.append(input_num)

    URL_VALIDATE = "https://technopol.pl/index.php?page=krzyzowka_dnia&param=walidacja"
    for letter in alphabet:
        request_params = dict()
        request_params["solve"] = "true"
        request_params["crossword_id"] = str(crossword_id)
        for input_num in input_nums:
            request_params[f"input{input_num}"] = letter
        response = requests.post(URL_VALIDATE, data=request_params)
        bad_letters = response.json()["bad_letters"]
        for input_num in input_nums:
            if not (str(input_num) in bad_letters):
                # this is a good letter then
                y = (input_num - 1) // width
                x = (input_num - 1) % width
                solution_puzzle[y][x] = letter
        
    return solution_puzzle


def get_technopol_puzzle(filepath=None, download=True):
    URL = "https://technopol.pl/krzyzowka_dnia"
    page = requests.get(URL)
    if page.status_code != 200:
        print ("Puzzle couldn't be found or an error occured", file=sys.stderr)
        sys.exit(1)
    soup = BeautifulSoup(page.content, "html.parser")

    form = soup.find("form", id="validate_form")
    crossword_id = int(form.find("input", attrs={"name":"crossword_id"}).get_attribute_list("value")[0])

    puzzle = dict()
    puzzle["origin"] = "TECHNOPOL"
    puzzle["version"] = "http://ipuz.org/v2"
    puzzle["kind"] = ["http://ipuz.org/crossword"]
    puzzle["copyright"] = f"© Wydawnictwo Technopol"
    puzzle["publisher"] = "TECHNOPOL"
    puzzle["url"] = URL
    puzzle["title"] = f"Krzyżówka nr {crossword_id}"
    puzzle["charset"] = "".join(alphabet)

    puzzle["puzzle"] = get_layout(soup)

    height = len(puzzle["puzzle"])
    width = len(puzzle["puzzle"][0])
    puzzle["dimensions"] = dict(width=width, height=height)

    puzzle["clues"] = get_clues(soup)
    puzzle["solution"] = get_solution(puzzle["puzzle"], crossword_id)

    puzzle["org.gnome.libipuz:locale"] = "pl_PL"
    puzzle["org.gnome.libipuz:charset"] = alphabet

    filename = f"technopol_{crossword_id}.ipuz"
    puzzle["annotation"] = filename

    if not filepath:
        filepath = "."

    if download:
        filepath_full = f"{filepath}/{filename}"
        if not os.path.exists(filepath_full):
            with open(filepath_full, "w") as outfile:
                json.dump(puzzle, outfile)

    return puzzle


if __name__ == "__main__":
    puzzle = get_technopol_puzzle(download=False)
    json.dump(puzzle, sys.stdout)
