import re
import sys

__all__ = [
    'Part',
    'Library',
    'Bom',
    'Comp',
    'split_ref',
]


def split_ref(ref):
    """Split "C12" into a tuple that's useful for sorting by component reference.

    For example: "C12" => ("C", 12, None). "Cfoo" => ("C", sys.maxsize, "").
    """
    m = split_ref.r.match(ref)
    if not m:
        return (ref, sys.maxsize, "")
    groups = m.groups()
    ref = groups[0]
    val = groups[1]
    rest = groups[2]
    try:
        return (ref, int(val), rest)
    except ValueError:
        pass

        return (ref, val, rest)


split_ref.r = re.compile("([A-Za-z]+)([0-9]+)(.*)")


class Part:
    def __init__(self, name):
        self.name = name


class Library:
    def __init__(self, name):
        self.name = name
        self.parts = {}

    def add_part(self, part):
        try:
            return self.parts[part]
        except KeyError:
            p = Part(part)
            self.parts[part] = p
            return p


class Bom(object):
    def __init__(self):
        self.libraries = {}
        self._components = {}

    def add_component(self, component):
        self._components[component.ref] = component

    def get_component(self, name):
        return self._components[name]

    def get_components(self):
        return self._components

    def all_field_names(self):
        fields = {'ref', 'value'}
        for c in self._components.values():
            for f in c.fields:
                fields.add(f)
        return fields

    def find_library(self, name):
        try:
            return self.libraries[name]
        except KeyError:
            lib = Library(name)
            self.libraries[name] = lib
            return lib

    def to_pandas(self, ref_field_name=None, value_field_name=None):
        import pandas

        ref_field_name = ref_field_name or "ref"
        value_field_name = value_field_name or "value"

        fields = self.all_field_names()
        data = {k: [] for k in fields}
        refs = []
        values = []
        for ref, c in self.get_components().items():
            refs.append(c.ref)
            values.append(c.value)
            for field in fields:
                data[field].append(c[field] if field in c else None)

        data[ref_field_name] = refs
        data[value_field_name] = values
        return pandas.DataFrame(data=data, index=refs)


class Comp:
    def __init__(self, ref, value, library, part, footprint):
        self.ref = ref
        self.value = value
        self.footprint = footprint
        self.library = library
        self.part = part
        self.fields = {}

    def add_field(self, key, value):
        self.fields[key] = value

    def __contains__(self, key):
        if key == 'ref':
            return self.ref is not None
        elif key == 'value':
            return self.value is not None
        else:
            return key in self.fields

    def __getitem__(self, key):
        if key == 'ref':
            return self.ref
        elif key == 'value':
            return self.value
        else:
            return self.fields[key]