# Copyright (c) 2019-2020 Manfred Moitzi
# License: MIT License
import pytest
from ezdxf.units import DrawingUnits, PaperSpaceUnits, conversion_factor, unit_name
from math import isclose


def test_msp_1_m():
    # 1 drawing unit = 1 m
    u = DrawingUnits(1, "m")
    assert 7 * u("m") == 7, "should be 7 drawing units"
    # rounding errors!
    assert isclose(7 * u("dm"), 0.7), "should be 0.7 drawing units"
    assert 7 * u("cm") == 0.07, "should be 0.07 drawing units"
    assert 7 * u("mm") == 0.007, "should be 0.007 drawing units"
    assert 7 * u("km") == 7000, "should be 7000 drawing units"
    assert isclose(7 * u("ft"), 2.1336), "should be 2.1336 drawing units"
    assert isclose(70 * u("in"), 1.778), "should be 1.778 drawing units"
    assert isclose(7 * u("yd"), 6.4008), "should be 6.4008 drawing units"


def test_msp_easy_usage():
    u = DrawingUnits(1, "m")
    cm = u("cm")
    assert 100 * cm == 1


def test_msp_2_m():
    # 1 drawing unit = 2 m
    u = DrawingUnits(2, "m")
    assert 1 * u("m") == 2, "should be 2 drawing units"
    assert 7 * u("m") == 14, "should be 14 drawing units"
    assert 7 * u("cm") == 0.14, "should be 0.14 drawing units"
    assert 7 * u("mm") == 0.014, "should be 0.014 drawing units"
    assert 7 * u("km") == 14000, "should be 14000 drawing units"


def test_msp_1_ft():
    # 1 drawing unit = 1 ft
    u = DrawingUnits(1, "ft")
    assert isclose(1 * u("ft"), 1), "should be 1 drawing unit"
    assert isclose(10 * u("yd"), 30), "should be 30 drawing unit"
    assert isclose(
        100 * u("in"), 8.3333333333
    ), "should be 8.33333 drawing unit"
    assert round(u("mi")) == 5280, "should be 5280 drawing unit"
    assert isclose(100 * u("m"), 328.0839895), "should be 328.084 drawing unit"
    assert isclose(u("km"), 3280.839895), "should be 3280.8399 drawing unit"


def test_msp_2_ft():
    # 1 drawing unit = 2 ft
    u = DrawingUnits(2, "ft")
    assert isclose(1 * u("ft"), 2), "should be 2 drawing unit"
    assert isclose(10 * u("yd"), 60), "should be 60 drawing unit"


def test_msp_1_in():
    # 1 drawing unit = 1 in
    u = DrawingUnits(1, "in")
    assert isclose(1 * u("ft"), 12), "should be 12 drawing units"
    assert isclose(1 * u("yd"), 36), "should be 36 drawing units"
    assert round(u("mi")) == 63360, "should be 63360 drawing units"
    assert isclose(
        100 * u("m"), 3937.007874
    ), "should be 3937.0079 drawing units"
    assert isclose(u("km"), 39370.07874)
    assert isclose(100 * u("dm"), 393.7007874)
    assert isclose(100 * u("cm"), 39.37007874)
    assert isclose(100 * u("mm"), 3.937007874)
    assert isclose(100 * u("µm"), 0.003937007874)


def test_paper_space_scale_1_1():
    psu = PaperSpaceUnits(DrawingUnits(1, "m"), unit="mm", scale=1.0)
    assert psu.from_msp(1, "m") == 1000, "should be 1000 mm in paper space"
    assert psu.from_msp(10, "cm") == 100
    assert psu.from_msp(10, "mm") == 10

    psu = PaperSpaceUnits(DrawingUnits(1, "yd"), unit="in", scale=1.0)
    assert (
        round(psu.from_msp(1, "yd")) == 36
    ), "should be 36 inch in paper space"
    assert (
        round(psu.from_msp(1, "ft")) == 12
    ), "should be 12 inch in paper space"


def test_paper_space_scale_1_100():
    psu = PaperSpaceUnits(DrawingUnits(1, "m"), unit="mm", scale=100.0)
    assert (
        psu.from_msp(1, "m") == 10
    ), "1m in msp, should be 10 mm in paper space 1:100"
    assert psu.from_msp(500, "cm") == 50

    psu = PaperSpaceUnits(DrawingUnits(1, "yd"), unit="in", scale=100.0)
    assert (
        round(psu.from_msp(1, "yd"), 2) == 0.36
    ), "1 yd in msp, should be .36 inch in paper space 1:100"
    assert (
        round(psu.from_msp(1, "ft"), 2) == 0.12
    ), "1 ft in msp, should be .12 inch in paper space 1:100"


def test_paper_space_default():
    # Default: 1 drawing unit = 1 m, paperspace units = 'mm', scale = 1:1
    psu = PaperSpaceUnits()
    assert psu.from_msp(1, "m") == 1000

    psu = PaperSpaceUnits(scale=100)
    assert psu.from_msp(1, "m") == 10


def test_paper_space_to_model_space_metric():
    psu = PaperSpaceUnits(msp=DrawingUnits(1, "m"), scale=100)
    assert (
        psu.to_msp(10, "mm") == 1
    ), "1mm in PSP 1:100 => should be 1 drawing unit in model space"
    assert (
        psu.to_msp(5, "cm") == 5
    ), "5cm in PSP 1:100 => should be 5 drawing unit in model space"


def test_paper_space_to_model_space_us():
    psu = PaperSpaceUnits(msp=DrawingUnits(1, "yd"), scale=100)
    assert isclose(
        psu.to_msp(1, "in"), 2.77777777777
    ), "1 inch in PSP 1:100 => should be 2.777 drawing unit in model space"


@pytest.mark.parametrize(
    "source, target, factor",
    [
        [6, 4, 1000],  # m in mm
        [4, 6, 0.001],  # mm in m
        [6, 5, 100],  # m in cm
        [5, 6, 0.01],  # cm in m
        [5, 4, 10],  # cm in mm
        [4, 5, 0.1],  # mm in cm
    ],
)
def test_conversion_factor(source, target, factor):
    assert conversion_factor(source, target) == pytest.approx(factor)


@pytest.mark.parametrize(
    "source, target",
    [
        [0, 6],  # 0 = unitless
        [6, 0],
        [0, 0],
    ],
)
def test_conversion_type_error(source, target):
    with pytest.raises(TypeError):
        conversion_factor(source, target)


def test_conversion_invalid_units():
    with pytest.raises(ValueError):
        conversion_factor(25, 6)


def test_unit_name_valid_input():
    assert unit_name(0) == "Unitless"
    assert unit_name(1) == "Inches"


def test_unit_name_invalid_input():
    assert unit_name(-1) == "unitless"
