#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#  ---------------------------------------------------------------------
#
#  _____    _      _              _         _____ _____
# | ____|__| | ___| |_      _____(_)___ ___|  ___| ____|
# |  _| / _` |/ _ \ \ \ /\ / / _ \ / __/ __| |_  |  _|
# | |__| (_| |  __/ |\ V  V /  __/ \__ \__ \  _| | |___
# |_____\__,_|\___|_| \_/\_/ \___|_|___/___/_|   |_____|
#
#
#  Unit of Strength of Materials and Structural Analysis
#  University of Innsbruck,
#  2017 - today
#
#  Alexander Dummer alexander.dummer@uibk.ac.at
#  Paul Hofer Paul.Hofer@uibk.ac.at
#
#  This file is part of EdelweissFE.
#
#  This library is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Lesser General Public
#  License as published by the Free Software Foundation; either
#  version 2.1 of the License, or (at your option) any later version.
#
#  The full text of the license can be found in the file LICENSE.md at
#  the top level directory of EdelweissFE.
#  ---------------------------------------------------------------------
from collections import UserDict
from collections.abc import Iterable
from types import MappingProxyType
[docs]class OrderedSet(UserDict):
    """An ordered set.
    Parameters
    ----------
    name
        A name for the object.
    items
        A list of items.
    """
    """ consider removing type checking! """
    def __init__(
        self,
        name: str,
        item_s,
    ):
        self.data = {}
        self.items = self.data.keys()
        self.keys = None
        self.values = None
        self.name = name
        # label is deprecated; use name instead!!
        self.label = name
        self.add(item_s)
[docs]    def checkObjectType(self, obj):
        """Checks if the object type is allowed in the OrderedSet"""
        return type(obj) in self.allowedObjectTypes 
[docs]    def forceIter(self, item_s):
        """Return an iterator object for item_s even if item_s itself is not iterable"""
        if isinstance(item_s, Iterable):
            return iter(item_s)
        else:
            return iter([item_s]) 
[docs]    def add(self, item_s):
        """Add an item or an iterable of items to the OrderedSet"""
        # add items
        for item in self.forceIter(item_s):
            self.data.setdefault(item) 
    def __setitem__(self, item, value):
        if self.checkObjectType(item):
            self.data[item] = None
        else:
            raise TypeError(f"You tried to add an item with wrong type: {item} of type {type(item)}")
    def __getitem__(self, key):
        return list(self.items)[key]
    # define &
    def __and__(self, other):
        if type(self) is type(other):
            return self.items & other.items
        else:
            raise TypeError("You can only compare OrderedSets with matching types")
    # define ^
    def __xor__(self, other):
        if type(self) is type(other):
            return self.items ^ other.items
        else:
            raise TypeError("You can only compare OrderedSets with matching types")
    # define |
    def __or__(self, other):
        if type(self) is type(other):
            return self.items | other.items
        else:
            raise TypeError("You can only compare OrderedSets with matching types")
    def __hash__(self):
        return hash(self.name)
    def __eq__(self, other):
        if type(self) is type(other):
            return self.__dict__ == other.__dict__
        else:
            return False
    def __repr__(self):
        type_ = type(self)
        module = type_.__module__
        qualname = type_.__qualname__
        return (
            f'<{module}.{qualname} object at {hex(id(self))} with name "{self.name}">'
            # + f' "{self.name}"'
            # + "\n   ".join([""] + [item.__repr__() for item in self.data])
        ) 
[docs]class ImmutableOrderedSet(OrderedSet):
    """An immutable ordered set.
    Parameters
    ----------
    name
        A name for the object.
    items
        A list of items.
    """
    def __init__(
        self,
        name: str,
        item_s,
    ):
        filteredItems = dict()
        for item in self.forceIter(item_s):
            if self.checkObjectType(item):
                filteredItems.update({item: None})
            else:
                raise TypeError(f"You tried to add an item with wrong type: {item} of type {type(item)}")
        self.data = MappingProxyType(filteredItems)
        self.items = self.data.keys()
        self.keys = None
        self.values = None
        self.name = name
        self.name = name
[docs]    def add(self, item_s):
        raise TypeError(f"{type(self).__qualname__} items cannot be changed") 
    def __setitem__(self, item, value):
        raise TypeError(f"{type(self).__qualname__} items cannot be changed")
    def __ior__(self, other):
        raise TypeError(f"{type(self).__qualname__} items cannot be changed")