from __future__ import annotations
from typing import Union
from enum import Enum
from abc import ABC, abstractmethod
import threading
import multiprocessing
from contextlib import contextmanager


class Synchronizer(object):

    @contextmanager
    def read(self) -> ContextManager:
        try:
            yield self.acquire_read()
        finally:
            self.release_read()

    @contextmanager
    def write(self) -> ContextManager:
        try:
            yield self.acquire_write()
        finally:
            self.release_write()

    def acquire_read(self) -> bool:
        raise NotImplementedError()

    def release_read(self):
        raise NotImplementedError()

    def acquire_write(self) -> bool:
        raise NotImplementedError()

    def release_write(self):
        raise NotImplementedError()

    def is_locked_read(self) -> bool:
        raise NotImplementedError()

    def is_locked_write(self) -> bool:
        raise NotImplementedError()


class StubLock:
    def acquire(self) -> bool:
        return True

    def release(self) -> None:
        pass

    def locked(self) -> bool:
        return False


class DefaultSynchronizer(Synchronizer):
    _lock: Union[threading.Lock, multiprocessing.synchronize.Lock, StubLock]

    def __init__(self, lock: Union[threading.Lock, multiprocessing.synchronize.Lock, StubLock]  ) -> None:
        self._lock = lock

    def acquire_read(self) -> bool:
        return self._lock.acquire()

    def release_read(self):
        self._lock.release()

    def acquire_write(self) -> bool:
        return self._lock.acquire()

    def release_write(self):
        self._lock.release()

    def is_locked_read(self) -> bool:
        return self._lock.locked()

    def is_locked_write(self) -> bool:
        return self._lock.locked()


class SynchronizerFactory:
    def create(self) -> Synchronizer:
        raise NotImplementedError()

    def __ceil__(self) -> Synchronizer:
        return self.create()


class EnvironemtType(Enum):
    SINGLE = 1
    MULTITHREADED = 2
    MULTIPROCESS = 3


class DefaultSynchronizerFactory:
    _environemt_type: EnvironemtType

    def __init__(self, environemt_type: EnvironemtType) -> None:
      self._environemt_type: EnvironemtType = environemt_type

    def create(self) -> Synchronizer:
        if self._environemt_type == EnvironemtType.SINGLE:
            return DefaultSynchronizer(StubLock())
        elif self._environemt_type == EnvironemtType.MULTITHREADED:
            return DefaultSynchronizer(threading.Lock())
        else:
            return DefaultSynchronizer(multiprocessing.Lock())

    def __ceil__(self) -> Synchronizer:
        return self.create()