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

from __future__ import annotations
import copy, sys, os
from types import MappingProxyType
from typing import Any, Iterable

class ConstantError(TypeError):
	"""Constant error"""
	pass

class Constant:
	__slots__ = ("_constant_mount", "_access_counter", "_recursion_limit", "_sealed_flag", "__weakref__") #__dict__を否定する
	
	def __init__(self, recursion_limit: int = 20) -> None:
		object.__setattr__(self, "_recursion_limit", max(0, int(recursion_limit))) #再帰関数
		object.__setattr__(self, "_sealed_flag", False) #ロックフラグ
		object.__setattr__(self, "_constant_mount", dict()) #定数保管辞書
		object.__setattr__(self, "_access_counter", dict()) #単純アクセスカウント辞書
	
	def _freeze(self, value: Any, depth: int | None = None) -> Any: #再帰凍結関数
		limit = self._recursion_limit if depth is None else max(0, int(depth))
		if limit == 0:
			return value
		try:
			if isinstance(value, MappingProxyType) or isinstance(value, tuple) or isinstance(value, frozenset): #再帰処理非該当
				return value
			if isinstance(value, dict): #辞書系
				frozen = {k: self._freeze(v, limit - 1) for k, v in value.items()}
				return MappingProxyType(frozen)
			if isinstance(value, (list, tuple)): #配列系
				frozen = tuple(self._freeze(v, limit - 1) for v in value)
				return frozen
			if isinstance(value, (set, frozenset)): #集合系
				frozen = frozenset(self._freeze(v, limit - 1) for v in value)
				return frozen
			return value
		except Exception:
			return value
	
	def _thaw(self, value: Any, depth: int | None = None) -> Any: #再帰解凍関数
		limit = self._recursion_limit if depth is None else max(0, int(depth))
		if limit == 0:
			try:
				return copy.deepcopy(value)
			except Exception:
				return value
		if isinstance(value, MappingProxyType): #辞書系
			return {k: self._thaw(v, limit - 1) for k, v in dict(value).items()}
		if isinstance(value, tuple): #配列系
			return [self._thaw(v, limit - 1) for v in value]
		if isinstance(value, frozenset): #集合系
			return set(self._thaw(v, limit - 1) for v in value)
		return copy.deepcopy(value)
	
	def __name__(self): #名前を提示する関数
		return "Constant"
	
	def __getattr__(self, name: str) -> Any: #取得補助関数
		data = object.__getattribute__(self, "_constant_mount")
		counter = object.__getattribute__(self, "_access_counter")
		if name in data:
			counter[name] += 1
			return data[name]
		raise AttributeError("'{0}' does not exist. You can't refer to something that doesn't exist.".format(name.replace("'", "\\'")))
	
	def __setattr__(self, name: str, value: Any) -> None: #設定補助関数
		if name.startswith("_"): #内部属性は拒否
			raise ConstantError("'{0}' is reserved and cannot be set.".format(name.replace("'", "\\'")))
		sealed = object.__getattribute__(self, "_sealed_flag")
		if sealed:
			raise ConstantError("Constant container is sealed. No more constants can be added.")
		data = object.__getattribute__(self, "_constant_mount")
		if name in data:
			raise ConstantError("The constant named '{0}' cannot be changed.".format(name.replace("'", "\\'")))
		data[name] = self._freeze(value)
		counter = object.__getattribute__(self, "_access_counter")
		counter[name] = 0 #カウンタ初期化
	
	def __delattr__(self, name: str) -> None: #削除補助関数
		if name.startswith("_"): #内部属性は拒否
			raise ConstantError("'{0}' is reserved and cannot be deleted.".format(name.replace("'", "\\'")))
		data = object.__getattribute__(self, "_constant_mount")
		if name in data:
			raise ConstantError("The constant named '{0}' cannot be deleted.".format(name.replace("'", "\\'")))
		raise AttributeError("'{0}' does not exist. You can't delete something that doesn't exist.".format(name.replace("'", "\\'")))
	
	def reset_counter(self) -> None: #アクセスカウンタのリセット関数
		counter = object.__getattribute__(self, "_access_counter")
		object.__setattr__(self, "_access_counter", {k : 0 for k in counter.keys()})
	
	def get_counter(self, name: str) -> int | None: #アクセスカウンタの値の取得関数
		counter = dict(object.__getattribute__(self, "_access_counter"))
		if name not in counter.keys():
			return None
		return int(counter[name])
		
	def items_counter(self, names: Iterable[str] | None = None) -> dict: #アクセスカウンタ辞書の取得関数
		counter = dict(object.__getattribute__(self, "_access_counter"))
		keys = list()
		if names is None:
			keys = list(counter.keys())
		else:
			keys = [n for n in names if n in counter.keys()]
		return {k: counter[k] for k in keys}
	
	def show_counter(self, names: Iterable[str] | None = None) -> None: #アクセスカウンタ一覧表示関数
		m = self.items_counter(names)
		print("[ Access count list of constant ({0}) ]".format(len(m)))
		kmax = max((len(str(k)) for k in m), default=0)
		vmax = max((len(str(v)) for v in m.values()), default=0)
		imax = len(str(max(len(m)-1, 0)))
		for i, k in enumerate(sorted(m)):
			v = m[k]
			print("{0:>{1}} : {2:<{3}} = {4:>{5}}".format(i, imax, k, kmax, v, vmax))
		print()
	
	def set(self, name: str, value: Any) -> None: #設定関数
		self.__setattr__(name, value)
	
	def get(self, name: str) -> Any: #取得関数
		return self.__getattr__(name)
	
	def items(self, names: Iterable[str] | None = None) -> dict[str, Any]: #辞書取得関数
		data = object.__getattribute__(self, "_constant_mount")
		keys = list()
		if names is None:
			keys = list(data.keys())
		else:
			keys = [n for n in names if n in data.keys()]
		return {k: self.get(k) for k in keys}
	
	def snapshot(self) -> dict[str, Any]: #データ一覧確認関数
		return self.items()
	
	def keys(self) -> Iterable[str]: #定数名称一覧確認関数
		data = object.__getattribute__(self, "_constant_mount")
		return tuple(sorted(data.keys()))
	
	def show(self, names: Iterable[str] | None = None) -> None: #一覧表示関数
		m = self.items(names)
		print("[ Constant list ({0}) ]".format(len(m)))
		kmax = max((len(str(k)) for k in m), default=0)
		vmax = max((len(str(v)) for v in m.values()), default=0)
		imax = len(str(max(len(m)-1, 0)))
		for i, k in enumerate(sorted(m)):
			v = m[k]
			vs = str(v).replace("\n", "\\n")
			if isinstance(v, str):
				vs = "'{0}'".format(vs.replace("'", "\\'"))
			print("{0:>{1}} : {2:<{3}} = {4:<{5}}".format(i, imax, k, kmax, vs, vmax))
		print()
	
	def seal(self) -> None: #ロック関数
		object.__setattr__(self, "_sealed_flag", True)
	
	def is_sealed(self) -> bool: #ロック確認関数
		return bool(object.__getattribute__(self, "_sealed_flag"))
	
	def get_recursion_limit(self) -> int: #再帰回数確認関数
		return int(object.__getattribute__(self, "_recursion_limit"))

CONST = Constant() #インスタンス生成
