#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import pytest

from warg.data_structures.named_ordered_dictionary import IllegalAttributeKey, NOD

__author__ = "Christian Heider Nielsen"


def test_attribute_assignment():
  nodict = NOD()
  nodict.paramA = "str_parameter"
  nodict.paramB = 10
  assert nodict.paramA == "str_parameter"
  assert nodict.paramB == 10


def test_dict_arg_construction():
  nodict = NOD({"paramA":"str_parameter", "paramB":10})
  assert nodict.paramA == "str_parameter"
  assert nodict.paramB == 10


def test_update_and_set_attr_update():
  nodict = NOD()
  nodict.update({"paramA":20, "paramB":"other_param", "paramC":5.0})
  nodict.paramA = 10
  assert nodict.paramA == 10
  assert nodict.paramB == "other_param"


def test_set_item_update():
  nodict = NOD()
  nodict["paramA"] = 10
  assert nodict.paramA == 10


def test_as_list_args_and_kwargs():
  vals = [1, 3, 5]
  nodict = NOD(10, val2=2, *vals)
  nolist = nodict.as_list()
  assert nolist[0] == 10
  assert nodict.val2 == 2
  assert nolist[-2] == vals[-1]


def test_kv_unmap():
  nodict = NOD("str_parameter", 10)
  odict = {**nodict}
  assert nodict.as_list()[0] == list(odict.values())[0]


def test_update_attr_with_kwargs():
  nodict = NOD("str_parameter", 10)
  nodict.update(arg1=20, arg0="other_param")
  assert nodict.arg0 == "other_param"
  assert nodict.arg1 == 20


def test_update_attr_with_dict_arg():
  nodict = NOD("str_parameter", 10)
  nodict.update({"arg1":20, "arg0":"other_param"})
  assert nodict.arg0 == "other_param"
  assert nodict.arg1 == 20


def test_no_existing_attr():
  nodict = NOD(paramA="str_parameter", paramB=10)
  nodict.update(20, "other_param")
  assert nodict.paramB == "other_param"
  assert nodict.paramA == 20
  assert nodict.get("paramC") is None


def test_existing_map_unpacking():
  nodict = NOD(paramA="str_parameter", paramB=10)
  nodict.update(20, "other_param")
  nodict = NOD(paramC="str_parameter", **nodict)
  assert nodict.paramB == "other_param"
  assert nodict.paramA == 20
  assert nodict.get("paramC") is not None


def test_as_list():
  arg0, arg1 = NOD("str_parameter", 10).as_list()
  assert arg0 == "str_parameter"
  assert arg1 == 10


def test_access_operators_NOD():
  arg0 = "str_parameter"
  arg1 = 10
  l = NOD(arg0, arg1)
  assert l[id(arg1)] == arg1
  assert l.__getattr__(id(arg1)) == arg1
  assert l[id(arg0)] == arg0
  assert l / id(arg0) == arg0
  assert l @ id(arg0) == arg0


def test_new_attr_on_existing():
  a = NOD(4, 2)
  a.list = "a"
  assert a.list == "a"


def test_addition_arithmetic():
  a = NOD(4, 2)
  b = a + a
  assert b.as_list() == [8, 4]


def test_illegal_overwrite():
  with pytest.raises(IllegalAttributeKey) as exc_info:
    NOD(update=2)

  assert exc_info.type is IllegalAttributeKey


def test_slice_get():
  a = NOD(4, 2)
  b = a + a
  assert b[:1] == [8]
  assert b[-1:] == [4]


def test_slice_get_adv():
  a = NOD(4, 2)
  b = a[:1] + a[0:]
  assert b[:1] == [4]
  assert b[-1:] == [2]


def test_slice_set():
  a = NOD(4, 2)
  a[:1] = [8]
  assert a.as_list() == [8, 2]


def test_slice_all():
  a = NOD(4, 2)
  b = a + a + a
  b[:1] = [8]
  assert b.as_list() == [8, 6]


def test_sorcery():
  from sorcery import dict_of

  def ret1():
    return 1

  arg1 = ret1()
  columns = dict_of(arg1)
  assert columns["arg1"] == arg1


def test_access_operators_NOD_of():
  list_rep = NOD("str_parameter", 10).as_list()
  arg0 = list_rep[0]
  arg1 = list_rep[1]
  columns = NOD.nod_of(arg1, dsa=arg0)
  assert columns["arg1"] == arg1
  assert columns.arg1 == arg1
  assert columns["dsa"] == arg0
  assert columns / "dsa" == arg0
  assert columns @ "dsa" == arg0
  assert id(columns / "dsa") == id(columns["dsa"])


def test_multiple_return_assignment_to_contstruction():
  arg0, arg1 = ("sta", 1)
  columns = NOD.nod_of(arg1, arg0)
  assert columns.arg1 == 1
  assert columns.arg0 == "sta"


def test_access_operators_single_explicit_naming():
  arg0 = "str_parameter"
  columns = NOD.nod_of(dsa=arg0)
  assert columns["dsa"] == arg0
  assert columns / "dsa" == arg0
  assert id(columns / "dsa") == id(columns["dsa"])


def test_access_operators_single_no_naming():
  dsa = "str_parameter"
  columns = NOD.nod_of(dsa)
  assert columns["dsa"] == dsa
  assert columns / "dsa" == dsa
  assert id(columns / "dsa") == id(columns["dsa"])


def test_access_operators_no_multi_return_no_variable_name_direct_inference():
  arg0 = "str_parameter"
  columns = NOD.nod_of("sas", dsa=arg0)
  print(columns)
  assert columns[:1] == ["sas"]
  assert columns.as_list()[0] == "sas"
  assert columns.as_list()[1] == arg0
  assert columns["dsa"] == arg0
  assert columns / "dsa" == arg0
  assert id(columns / "dsa") == id(columns["dsa"])


def test_nested():
  cfg = NOD()

  cfg.MODEL = NOD()

  cfg.MODEL.DEVICE = "cuda"
  # match default boxes to any ground truth with jaccard overlap higher than a threshold (0.5)
  cfg.MODEL.THRESHOLD = 0.5
  cfg.MODEL.NUM_CLASSES = 21
  # Hard negative mining
  cfg.MODEL.NEG_POS_RATIO = 3
  cfg.MODEL.CENTER_VARIANCE = 0.1
  cfg.MODEL.SIZE_VARIANCE = 0.2

  cfg.ADSA = 203182

  print(cfg)


def test_copy():
  from copy import deepcopy, copy

  nodict = NOD()
  nodict.paramA = "str_parameter"
  nodict.paramB = 10

  b = copy(nodict)
  print(b)
  assert b.paramB == 10
  assert b.paramB == nodict.paramB

  a = deepcopy(nodict)
  print(a)
  assert a.paramB == 10
  assert a.paramB == nodict.paramB


def test_distribute_keys_view():
  nodict = NOD()
  nodict.paramA = 2
  nodict.paramB = 10

  nodict.paramC = 5
  nodict[nodict.keys()] = 4

  assert nodict.paramA == 4
  assert nodict.paramB == 4
  assert nodict.paramC == 4


def test_distribute_slice():
  nodict = NOD()
  nodict.paramA = 2
  nodict.paramB = 10
  nodict.paramC = 5
  nodict.paramD = 5

  nodict[:3] = 4

  assert nodict.paramA == 4
  assert nodict.paramB == 4
  assert nodict.paramC == 4
  assert nodict.paramD == 5


def test_multi_key_index():
  nodict = NOD()
  nodict.paramA = 2
  nodict.paramB = 10
  nodict.paramC = 5
  nodict.paramD = 5

  nodict["paramA", "paramB"] = 4

  assert nodict.paramA == 2
  assert nodict.paramB == 10
  assert nodict.paramC == 5
  assert nodict.paramD == 5

  assert nodict["paramA", "paramB"] == 4


def test_call_index():
  nodict = NOD()
  nodict.paramA = 2
  nodict.paramB = 10
  nodict.paramC = 5
  nodict.paramD = 5
  nodict[6] = 1
  nodict[1] = 1
  nodict[2] = 11

  assert nodict.paramA == 2
  assert nodict.paramB == 10
  assert nodict.paramC == 5
  assert nodict.paramD == 5

  assert nodict("paramA", "paramB", 6) == [2, 10, 1]
  assert nodict("paramC") == [5]
  assert nodict(6, "paramD", 1) == [1, 5, 1]
  assert nodict(2) == [11]
