# -*- coding: utf-8 -*-
#
import rdflib

from . import RDF_first, RDFS_Resource, stringify_node

def compare_blank_node(graph1, bnode1, graph2, bnode2, recursion=0):
    assert isinstance(graph1, rdflib.Graph)
    assert isinstance(graph2, rdflib.Graph)
    assert isinstance(bnode1, rdflib.BNode)
    assert isinstance(bnode2, rdflib.BNode)
    if recursion >= 10:
        return 1  # Cannot compare this deep

    def compare_list(l_node1, l_node2):
        # TODO, determine if lists must be ordered
        list_1_items = list(graph1.items(l_node1))
        list_2_items = list(graph2.items(l_node2))
        if len(list_1_items) > len(list_2_items):
            return 1
        elif len(list_2_items) > len(list_1_items):
            return -1
        eq = 0
        for i1 in list_1_items:
            found = None
            for i2 in list_2_items:
                eq = compare_node(graph1, i1, graph2, i2, recursion=recursion+1)
                if eq == 0:
                    found = i2
                    break
            if found is not None:
                list_2_items.remove(found)
            else:
                eq = 1
                break
        return eq


    predicates1 = set(graph1.predicates(bnode1))
    predicates2 = set(graph2.predicates(bnode2))
    in_ps1_but_not_in_ps2 = list()
    in_ps2_but_not_in_ps1 = list()
    pred_objs_in_bnode1_but_not_bnode2 = list()
    pred_objs_in_bnode2_but_not_bnode1 = list()
    def return_eq(direction):
        nonlocal in_ps2_but_not_in_ps1
        nonlocal in_ps1_but_not_in_ps2
        if direction == 0:
            return direction
        if recursion <= 1:
            if direction < 0:
                print("BNode1 is smaller.")
            else:
                print("BNode1 is larger.")
            print("BNode1:")
            print(stringify_node(graph1, bnode1))
            print("BNode2:")
            print(stringify_node(graph2, bnode2))
            if len(in_ps1_but_not_in_ps2) > 0:
                print("In predicates of BNode1, but not in predicates of BNode2:")
                for p in in_ps1_but_not_in_ps2:
                    print("predicate: {}".format(stringify_node(graph1, p)))
            if len(in_ps2_but_not_in_ps1) > 0:
                print("In predicates of BNode2, but not in predicates of BNode1:")
                for p in in_ps2_but_not_in_ps1:
                    print("predicate: {}".format(stringify_node(graph2, p)))
            if len(pred_objs_in_bnode1_but_not_bnode2) > 0:
                print("In predicate/objects of BNode1, but not in predicate/objects of BNode2:")
                for p, o in pred_objs_in_bnode1_but_not_bnode2:
                    print("predicate: {} object: {}".format(stringify_node(graph1, p), stringify_node(graph1, o)))
            if len(pred_objs_in_bnode2_but_not_bnode1) > 0:
                print("In predicate/objects of BNode2, but not in predicate/objects of BNode1:")
                for p, o in pred_objs_in_bnode2_but_not_bnode1:
                    print("predicate: {} object: {}".format(stringify_node(graph2, p), stringify_node(graph2, o)))
        return direction

    if len(predicates1) < 1 and len(predicates2) < 1:
        return return_eq(0)
    elif len(predicates1) < 1:
        return return_eq(-1)
    elif len(predicates2) < 1:
        return return_eq(1)

    if RDF_first in predicates1 and RDF_first in predicates2:
        return compare_list(bnode1, bnode2)
    elif RDF_first in predicates1:
        return return_eq(1)
    elif RDF_first in predicates2:
        return return_eq(-1)

    p1s_compared = set()
    p2s_compared = set()
    bnode1_eq = 0
    for p1 in predicates1:
        if isinstance(p1, rdflib.URIRef):
            if p1 in predicates2:
                o1_list = list(graph1.objects(bnode1, p1))
                o2_list = list(graph2.objects(bnode2, p1))

                for o1 in o1_list:
                    if o1 == RDFS_Resource:
                        continue
                    found = None
                    for o2 in o2_list:
                        eq = compare_node(graph1, o1, graph2, o2, recursion=recursion+1)
                        if eq == 0:
                            found = o2
                            break
                    if found is not None:
                        o2_list.remove(found)
                    else:
                        pred_objs_in_bnode1_but_not_bnode2.append((p1, o1))

                if len(pred_objs_in_bnode1_but_not_bnode2) > 0:
                    bnode1_eq = 1
            else:
                in_ps1_but_not_in_ps2.append(p1)
                bnode1_eq = 1
        else:
            raise NotImplementedError(
                "Don't know to compare non-uri predicates on a blank node.")
    bnode2_eq = 0
    for p2 in predicates2:
        if isinstance(p2, rdflib.URIRef):
            if p2 in predicates1:
                o1_list = list(graph1.objects(bnode1, p2))
                o2_list = list(graph2.objects(bnode2, p2))

                for o2 in o2_list:
                    if o2 == RDFS_Resource:
                        continue
                    found = None
                    for o1 in o1_list:
                        eq = compare_node(graph2, o2, graph1, o1,
                                          recursion=recursion + 1)
                        if eq == 0:
                            found = o1
                            break
                    if found is not None:
                        o1_list.remove(found)
                    else:
                        pred_objs_in_bnode2_but_not_bnode1.append((p2, o2))

                if len(pred_objs_in_bnode2_but_not_bnode1) > 0:
                    bnode2_eq = 1
            else:
                in_ps2_but_not_in_ps1.append(p2)
                bnode2_eq = 1
        else:
            raise NotImplementedError(
                "Don't know to compare non-uri predicates on a blank node.")

    if bnode1_eq == 0 and bnode2_eq == 0:
        return return_eq(0)
    if bnode1_eq == 1 and bnode2_eq == 1:
        return return_eq(2)
    if bnode1_eq == -1 and bnode2_eq == -1:
        return return_eq(-2)
    if bnode1_eq == 1 and bnode2_eq == 0:
        return return_eq(1)
    if bnode1_eq == 0 and bnode2_eq == 1:
        return return_eq(-1)
    if bnode1_eq == 0 and bnode2_eq == -1:
        return return_eq(1)
    if bnode1_eq == -1 and bnode2_eq == 0:
        return return_eq(-1)
    return return_eq(bnode1_eq)


def compare_literal(graph1, node1, graph2, node2):
    try:
        cmp = node1.eq(node2)
    except TypeError:
        # Type error means we cannot compare these literals
        cmp = False
    if cmp:
        return 0  # 0 = equal
    else:
        return 1  # 1 = not-equal


def compare_node(graph1, node1, graph2, node2, recursion=0):
    if isinstance(node1, rdflib.Literal) and isinstance(node2, rdflib.Literal):
        eq = compare_literal(graph1, node1, graph2, node2)
    elif isinstance(node1, rdflib.Literal):
        eq = 1 # node1 being a literal is greater
    elif isinstance(node2, rdflib.Literal):
        eq = -1 # node2 being a literal is greater
    elif isinstance(node1, rdflib.BNode) and isinstance(node2, rdflib.BNode):
        eq = compare_blank_node(graph1, node1, graph2, node2,
                                recursion=recursion+1)
    elif isinstance(node1, rdflib.BNode):
        eq = 1 # node1 being a BNode is greater
    elif isinstance(node2, rdflib.BNode):
        eq = -1  # node2 being a BNode is greater
    elif isinstance(node1, rdflib.URIRef) and isinstance(node2, rdflib.URIRef):
        s1 = str(node1)
        s2 = str(node2)
        if s1 > s2:
            eq = 1
        elif s2 > s1:
            eq = -1
        else:
            eq = 0
    else:
        s1 = str(node1)
        s2 = str(node2)
        if s1 > s2:
            eq = 1
        elif s2 > s1:
            eq = -1
        else:
            eq = 0
    return eq
