#!/usr/bin/env python
import logging
import os
import unittest

import pytest

import pdfplumber

logging.disable(logging.ERROR)

HERE = os.path.abspath(os.path.dirname(__file__))


class Test(unittest.TestCase):
    @classmethod
    def setup_class(self):
        path = os.path.join(HERE, "pdfs/nics-background-checks-2015-11.pdf")
        self.pdf = pdfplumber.open(path)
        # via http://www.pdfill.com/example/pdf_drawing_new.pdf
        path_2 = os.path.join(HERE, "pdfs/pdffill-demo.pdf")
        self.pdf_2 = pdfplumber.open(path_2)

    @classmethod
    def teardown_class(self):
        self.pdf.close()
        self.pdf_2.close()

    def test_metadata(self):
        metadata = self.pdf.metadata
        assert isinstance(metadata["Producer"], str)

    def test_pagecount(self):
        assert len(self.pdf.pages) == 1

    def test_page_number(self):
        assert self.pdf.pages[0].page_number == 1
        assert str(self.pdf.pages[0]) == "<Page:1>"

    def test_objects(self):
        assert len(self.pdf.chars)
        assert len(self.pdf.rects)
        assert len(self.pdf.lines)
        assert len(self.pdf.rect_edges)
        assert len(self.pdf_2.curve_edges)
        # Ensure that caching is working:
        assert id(self.pdf._rect_edges) == id(self.pdf.rect_edges)
        assert id(self.pdf_2._curve_edges) == id(self.pdf_2.curve_edges)
        assert id(self.pdf.pages[0]._layout) == id(self.pdf.pages[0].layout)

    def test_annots(self):
        pdf = self.pdf_2
        assert len(pdf.annots)
        assert len(pdf.hyperlinks) == 17
        uri = "http://www.pdfill.com/pdf_drawing.html"
        assert pdf.hyperlinks[0]["uri"] == uri

        path = os.path.join(HERE, "pdfs/annotations.pdf")
        with pdfplumber.open(path) as pdf:
            assert len(pdf.annots)

    def test_annots_cropped(self):
        pdf = self.pdf_2
        page = pdf.pages[0]
        assert len(page.annots) == 13
        assert len(page.hyperlinks) == 1

        cropped = page.crop(page.bbox)
        assert len(cropped.annots) == 13
        assert len(cropped.hyperlinks) == 1

        h0_bbox = pdfplumber.utils.obj_to_bbox(page.hyperlinks[0])
        cropped = page.crop(h0_bbox)
        assert len(cropped.annots) == len(cropped.hyperlinks) == 1

    def test_annots_rotated(self):
        def get_annot(filename, n=0):
            path = os.path.join(HERE, "pdfs", filename)
            with pdfplumber.open(path) as pdf:
                return pdf.pages[0].annots[n]

        a = get_annot("annotations.pdf", 3)
        b = get_annot("annotations-rotated-180.pdf", 3)
        c = get_annot("annotations-rotated-90.pdf", 3)
        d = get_annot("annotations-rotated-270.pdf", 3)

        assert (
            int(a["width"]) == int(b["width"]) == int(c["height"]) == int(d["height"])
        )
        assert (
            int(a["height"]) == int(b["height"]) == int(c["width"]) == int(d["width"])
        )
        assert int(a["x0"]) == int(c["top"]) == int(d["y0"])
        assert int(a["x1"]) == int(c["bottom"]) == int(d["y1"])
        assert int(a["top"]) == int(b["y0"]) == int(d["x0"])
        assert int(a["bottom"]) == int(b["y1"]) == int(d["x1"])

    def test_crop_and_filter(self):
        def test(obj):
            return obj["object_type"] == "char"

        bbox = (0, 0, 200, 200)
        original = self.pdf.pages[0]
        cropped = original.crop(bbox)
        assert id(cropped.chars) == id(cropped._objects["char"])
        assert cropped.width == 200
        assert len(cropped.rects) > 0
        assert len(cropped.chars) < len(original.chars)

        within_bbox = original.within_bbox(bbox)
        assert len(within_bbox.chars) < len(cropped.chars)
        assert len(within_bbox.chars) > 0

        filtered = cropped.filter(test)
        assert id(filtered.chars) == id(filtered._objects["char"])
        assert len(filtered.rects) == 0

    def test_outside_bbox(self):
        original = self.pdf.pages[0]
        outside_bbox = original.outside_bbox(original.find_tables()[0].bbox)
        assert outside_bbox.extract_text() == "Page 1 of 205"
        assert outside_bbox.bbox == original.bbox

    def test_relative_crop(self):
        page = self.pdf.pages[0]
        cropped = page.crop((10, 10, 40, 40))
        recropped = cropped.crop((10, 15, 20, 25), relative=True)
        target_bbox = (20, 25, 30, 35)
        assert recropped.bbox == target_bbox

        recropped_wi = cropped.within_bbox((10, 15, 20, 25), relative=True)
        assert recropped_wi.bbox == target_bbox

        # via issue #245, should not throw error when using `relative=True`
        bottom = page.crop((0, 0.8 * float(page.height), page.width, page.height))
        bottom.crop((0, 0, 0.5 * float(bottom.width), bottom.height), relative=True)
        bottom.crop(
            (0.5 * float(bottom.width), 0, bottom.width, bottom.height), relative=True
        )

        # An extra test for issue #914, in which relative crops were
        # using the the wrong bboxes for cropping, leading to empty object-lists
        crop_right = page.crop((page.width / 2, 0, page.width, page.height))
        crop_right_again_rel = crop_right.crop(
            (0, 0, crop_right.width / 2, page.height), relative=True
        )
        assert len(crop_right_again_rel.chars)

    def test_invalid_crops(self):
        page = self.pdf.pages[0]
        with pytest.raises(ValueError):
            page.crop((0, 0, 0, 0))

        with pytest.raises(ValueError):
            page.crop((0, 0, 10000, 10))

        with pytest.raises(ValueError):
            page.crop((-10, 0, 10, 10))

        with pytest.raises(ValueError):
            page.crop((100, 0, 0, 100))

        with pytest.raises(ValueError):
            page.crop((0, 100, 100, 0))

        # via issue #245
        bottom = page.crop((0, 0.8 * float(page.height), page.width, page.height))
        with pytest.raises(ValueError):
            bottom.crop((0, 0, 0.5 * float(bottom.width), bottom.height))
        with pytest.raises(ValueError):
            bottom.crop((0.5 * float(bottom.width), 0, bottom.width, bottom.height))

        # via issue #421, testing strict=True/False
        with pytest.raises(ValueError):
            page.crop((0, 0, page.width + 10, page.height + 10))

        page.crop((0, 0, page.width + 10, page.height + 10), strict=False)

    def test_rotation(self):
        assert self.pdf.pages[0].width == 1008
        assert self.pdf.pages[0].height == 612
        path = os.path.join(HERE, "pdfs/nics-background-checks-2015-11-rotated.pdf")
        with pdfplumber.open(path) as rotated:
            assert rotated.pages[0].width == 612
            assert rotated.pages[0].height == 1008

            assert rotated.pages[0].cropbox != self.pdf.pages[0].cropbox
            assert rotated.pages[0].bbox != self.pdf.pages[0].bbox

    def test_password(self):
        path = os.path.join(HERE, "pdfs/password-example.pdf")
        with pdfplumber.open(path, password="test") as pdf:
            assert len(pdf.chars) > 0

    def test_unicode_normalization(self):
        path = os.path.join(HERE, "pdfs/issue-905.pdf")

        with pdfplumber.open(path) as pdf:
            page = pdf.pages[0]
            print(page.extract_text())
            assert ord(page.chars[0]["text"]) == 894

        with pdfplumber.open(path, unicode_norm="NFC") as pdf:
            page = pdf.pages[0]
            assert ord(page.chars[0]["text"]) == 59
            assert page.extract_text() == ";;"

    def test_colors(self):
        rect = self.pdf.pages[0].rects[0]
        assert rect["non_stroking_color"] == (0.8, 1, 1)

    def test_text_colors(self):
        char = self.pdf.pages[0].chars[3358]
        assert char["non_stroking_color"] == (1, 0, 0)

    def test_load_with_custom_laparams(self):
        # See https://github.com/jsvine/pdfplumber/issues/168
        path = os.path.join(HERE, "pdfs/cupertino_usd_4-6-16.pdf")
        laparams = dict(line_margin=0.2)
        with pdfplumber.open(path, laparams=laparams) as pdf:
            assert round(pdf.pages[0].chars[0]["top"], 3) == 66.384

    def test_loading_pathobj(self):
        from pathlib import Path

        path = os.path.join(HERE, "pdfs/nics-background-checks-2015-11.pdf")
        path_obj = Path(path)
        with pdfplumber.open(path_obj) as pdf:
            assert len(pdf.metadata)

    def test_loading_fileobj(self):
        path = os.path.join(HERE, "pdfs/nics-background-checks-2015-11.pdf")
        with open(path, "rb") as f:
            with pdfplumber.open(f) as pdf:
                assert len(pdf.metadata)
            assert not f.closed

    def test_bad_fileobj(self):
        path = os.path.join(HERE, "pdfs/empty.pdf")
        with pytest.raises(pdfplumber.utils.exceptions.PdfminerException):
            pdfplumber.open(path)

        f = open(path)
        with pytest.raises(pdfplumber.utils.exceptions.PdfminerException):
            pdfplumber.open(f)
        # File objects passed to pdfplumber should not be auto-closed
        assert not f.closed
        f.close()

    def test_uncommon_boxes(self):
        path = os.path.join(HERE, "pdfs/page-boxes-example.pdf")
        with pdfplumber.open(path) as pdf:
            page = pdf.pages[0]
            assert page.artbox == (42.51969, 70.86613999999997, 552.75591, 827.71653)
            assert page.bleedbox == (0, 0.0, 623.62205, 870.23622)
            assert page.trimbox == (28.34646, 56.69290999999998, 566.92913, 841.88976)
