"Test importing DjVu"

import os
import re
import subprocess
import tempfile
import shutil
import datetime
from unittest.mock import MagicMock
import pytest
from gi.repository import GLib
from document import Document
from bboxtree import VERSION
from page import Page


@pytest.mark.skipif(
    shutil.which("cjb2") is None, reason="Please install cjb2 to enable test"
)
def test_import_djvu(rose_jpg, temp_djvu, temp_txt, clean_up_files, temp_db):
    "Test importing DjVu"

    subprocess.run(["c44", rose_jpg.name, temp_djvu.name], check=True)
    text = """(page 0 0 2236 3185
  (column 157 3011 1725 3105
    (para 157 3014 1725 3101
      (line 157 3014 1725 3101
        (word 157 3030 241 3095 "Füß—")
        (word 533 3033 645 3099 "LA")
        (word 695 3014 1188 3099 "MARQUISE")
        (word 1229 3034 1365 3098 "DE")
        (word 1409 3031 1725 3101 "GANGE")))))
"""
    temp_txt.write(text)
    temp_txt.flush()
    text = """(maparea "" "()" (rect 157 3030 84 65) (hilite #cccf00) (xor))
"""
    with open("ann.txt", "w", encoding="utf-8") as fhd:
        fhd.write(text)
    subprocess.run(
        [
            "djvused",
            temp_djvu.name,
            "-e",
            f"select 1; set-txt {temp_txt.name}; set-ant ann.txt",
            "-s",
        ],
        check=True,
    )
    text = """Author	"Authör"
Keywords	"Keywörds"
Title	"Titleß"
Subject	"Sübject"
CreationDate	"2018-12-31 13:00:00+01:00"
"""
    with open("text.txt", "w", encoding="utf-8") as fhd:
        fhd.write(text)
    subprocess.run(
        ["djvused", temp_djvu.name, "-e", "set-meta text.txt", "-s"], check=True
    )

    slist = Document(db=temp_db.name)

    mlp = GLib.MainLoop()

    asserts = 0

    def started_cb(response):
        nonlocal asserts
        assert response.request.process in ["get_file_info", "import_file"]
        asserts += 1

    def metadata_cb(response):
        assert response["datetime"] == datetime.datetime(
            2018, 12, 31, 13, 0, tzinfo=datetime.timezone(datetime.timedelta(hours=1))
        ), "datetime"
        assert response["author"] == "Authör", "author"
        assert response["subject"] == "Sübject", "subject"
        assert response["keywords"] == "Keywörds", "keywords"
        assert response["title"] == "Titleß", "title"
        nonlocal asserts
        asserts += 1

    slist.import_files(
        paths=[temp_djvu.name],
        started_callback=started_cb,
        metadata_callback=metadata_cb,
        finished_callback=lambda response: mlp.quit(),
    )
    GLib.timeout_add(2000, mlp.quit)  # to prevent it hanging
    mlp.run()

    assert asserts == 3, "callbacks all run"

    page = slist.thread.get_page(id=1)
    assert page.image_object.mode == "RGB", "DjVu imported correctly"
    expected = f"""<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
 <head>
  <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
  <meta name='ocr-system' content='scantpaper {VERSION}' />
  <meta name='ocr-capabilities' content='ocr_page ocr_carea ocr_par ocr_line ocr_word'/>
 </head>
 <body>
  <div class='ocr_page' title='bbox 0 0 2236 3185'>
   <div class='ocr_carea' title='bbox 157 80 1725 174'>
    <p class='ocr_par' title='bbox 157 84 1725 171'>
     <span class='ocr_line' title='bbox 157 84 1725 171'>
      <span class='ocrx_word' title='bbox 157 90 241 155'>Füß—</span>
      <span class='ocrx_word' title='bbox 533 86 645 152'>LA</span>
      <span class='ocrx_word' title='bbox 695 86 1188 171'>MARQUISE</span>
      <span class='ocrx_word' title='bbox 1229 87 1365 151'>DE</span>
      <span class='ocrx_word' title='bbox 1409 84 1725 154'>GANGE</span>
     </span>
    </p>
   </div>
  </div>
 </body>
</html>
"""
    assert page.export_hocr() == expected, "text layer"
    expected = """(maparea "" "()" (rect 157 3030 84 65) (hilite #cccf00) (xor))
"""
    assert page.export_djvu_ann() == expected, "annotation layer"

    #########################

    clean_up_files(
        slist.thread.db_files
        + [
            "text.txt",
            "ann.txt",
        ]
    )


@pytest.mark.skipif(
    shutil.which("cjb2") is None, reason="Please install cjb2 to enable test"
)
def test_import_djvu_with_error(rose_jpg, temp_djvu, clean_up_files):
    "Test importing DjVu"

    subprocess.run(["c44", rose_jpg.name, temp_djvu.name], check=True)

    with tempfile.TemporaryDirectory() as dirname:
        slist = Document(dir=dirname)

        mlp = GLib.MainLoop()

        asserts = 0

        def queued_cb(response):
            nonlocal asserts
            assert response.request.process == "get_file_info", "queued_cb"
            asserts += 1

            # inject error during import file
            os.chmod(dirname, 0o500)  # no write access

        def error_cb(_page, _process, message):
            nonlocal asserts
            assert re.search(r"^Error", message), "error_cb"
            asserts += 1

            # inject error during import file
            os.chmod(dirname, 0o700)  # no write access

        slist.import_files(
            paths=[temp_djvu.name],
            queued_callback=queued_cb,
            error_callback=error_cb,
            finished_callback=lambda response: mlp.quit(),
        )
        GLib.timeout_add(2000, mlp.quit)  # to prevent it hanging
        mlp.run()

        assert asserts == 2, "all callbacks run"

        #########################

        clean_up_files(slist.thread.db_files)


def mock_import_djvu_txt(self, _text):
    "mock import_djvu_txt method to test error handling"
    raise ValueError("Error parsing djvu text")


@pytest.mark.skipif(
    shutil.which("cjb2") is None, reason="Please install cjb2 to enable test"
)
def test_import_djvu_with_error2(
    monkeypatch, rose_jpg, temp_djvu, temp_db, clean_up_files
):
    "Test importing DjVu"

    subprocess.run(["c44", rose_jpg.name, temp_djvu.name], check=True)

    # apply the monkeypatch for Page.import_djvu_txt to mock_import_djvu_txt
    monkeypatch.setattr(Page, "import_djvu_txt", mock_import_djvu_txt)

    slist = Document(db=temp_db.name)

    mlp = GLib.MainLoop()

    asserts = 0

    def logger_cb(response):
        nonlocal asserts
        assert re.search(r"error", response.status), "error_cb"
        asserts += 1

    slist.import_files(
        paths=[temp_djvu.name],
        logger_callback=logger_cb,
        finished_callback=lambda response: mlp.quit(),
    )
    GLib.timeout_add(2000, mlp.quit)  # to prevent it hanging
    mlp.run()

    assert asserts == 1, "all callbacks run"
    page = slist.thread.get_page(id=1)
    assert page.image_object.mode == "RGB", "DjVu imported otherwise correctly"

    #########################

    clean_up_files(slist.thread.db_files)


@pytest.mark.skipif(
    shutil.which("cjb2") is None, reason="Please install cjb2 to enable test"
)
def test_import_multipage_djvu(rose_jpg, temp_djvu, temp_db, clean_up_files):
    "Test importing multipage DjVu"

    subprocess.run(["c44", rose_jpg.name, temp_djvu.name], check=True)
    with tempfile.NamedTemporaryFile(suffix=".djvu") as temp_djvu2:
        subprocess.run(
            ["djvm", "-c", temp_djvu2.name, temp_djvu.name, temp_djvu.name], check=True
        )

        slist = Document(db=temp_db.name)

        mlp = GLib.MainLoop()

        asserts = 0

        def started_cb(response):
            nonlocal asserts
            assert response.request.process in ["get_file_info", "import_file"]
            asserts += 1

        error_cb = MagicMock()

        slist.import_files(
            paths=[temp_djvu2.name],
            started_callback=started_cb,
            error_callback=error_cb,
            finished_callback=lambda response: mlp.quit(),
        )
        GLib.timeout_add(2000, mlp.quit)  # to prevent it hanging
        mlp.run()

        assert asserts == 2, "callbacks all run"
        error_cb.assert_not_called(), "no error callback called"
        assert len(slist.data) == 2, "imported 2 pages"

    clean_up_files(slist.thread.db_files)
