#!/usr/bin/python
# -*- coding: utf-8 -*-
import itertools
import math
import os
import string
from collections import defaultdict
from cStringIO import StringIO

import autonetkit.config
import autonetkit.exception
import autonetkit.log as log
import networkx as nx

settings = autonetkit.config.settings


# TODO: fallback to python if cStringIO version is not available

def load_graphml(input_data):

    # TODO: allow default properties to be passed in as dicts

    try:
        graph = nx.read_graphml(input_data)
    except IOError, e:
        acceptable_errors = set([2, 36, 63])  # no such file or directory
                                              # input string too long for filename
                                              # input string too long for filename
        if e.errno in acceptable_errors:
            from xml.etree.cElementTree import ParseError

            # try as data string rather than filename string

            try:
                input_pseduo_fh = StringIO(input_data)  # load into filehandle to networkx
                graph = nx.read_graphml(input_pseduo_fh)
            except IOError:
                raise autonetkit.exception.AnkIncorrectFileFormat
            except IndexError:
                raise autonetkit.exception.AnkIncorrectFileFormat
            except ParseError:
                raise autonetkit.exception.AnkIncorrectFileFormat
            except ParseError:
                raise autonetkit.exception.AnkIncorrectFileFormat
        else:
            raise e

    if graph.is_multigraph():
        log.info('Input graph is multigraph. Converting to single-edge graph'
                 )
        if graph.is_directed():
            graph = nx.DiGraph(graph)
        else:
            graph = nx.Graph(graph)

    # TODO: need to support edge index keying for multi graphs

    graph.remove_edges_from(edge for edge in graph.selfloop_edges())

# TODO: if selfloops then log that are removing

    letters_single = (c for c in string.lowercase)  # a, b, c, ... z
    letters_double = ('%s%s' % (a, b) for (a, b) in
        itertools.product(string.lowercase, string.lowercase))  # aa, ab, ... zz
    letters = itertools.chain(letters_single, letters_double)  # a, b, c, .. z, aa, ab, ac, ... zz

# TODO: need to get set of current labels, and only return if not in this set

    # TODO: add cloud, host, etc
    # prefixes for unlabelled devices, ie router -> r_a

    label_prefixes = {'router': 'r', 'switch': 'sw', 'server': 'se'}

    current_labels = set(graph.node[node].get('label') for node in
                         graph.nodes_iter())
    unique_label = (letter for letter in letters if letter
        not in current_labels)

# TODO: make sure device label set

    ank_graph_defaults = settings['Graphml']['Graph Defaults']
    for (key, val) in ank_graph_defaults.items():
        if key not in graph.graph:
            graph.graph[key] = val

    # handle yEd exported booleans: if a boolean is set, then only the nodes marked true have the attribute. need to map the remainder to be false to allow ANK logic
    # for node in graph.nodes(data=True):
        # print node

    all_labels = dict((n, d.get('label')) for (n, d) in
                      graph.nodes(data=True))
    label_counts = defaultdict(list)
    for (node, label) in all_labels.items():