text: import logging
  from typing import List
  
  import cv2
  import imutils
  
  from fishy.engine.common import window_server
  from fishy.engine.common.window_server import Status, WindowServer
  from fishy.helper import helper
  
  
  class WindowClient:
      clients: List['WindowClient'] = []
  
      def __init__(self, crop=None, color=None, scale=None, show_name=None):
          """
          create a window instance with these pre process
          :param crop: [x1,y1,x2,y2] array defining the boundaries to crop
          :param color: color to use example cv2.COLOR_RGB2HSV
          :param scale: scaling the window
          """
          self.color = color
          self.crop = crop
          self.scale = scale
          self.show_name = show_name
  
          if len(WindowClient.clients) == 0:
              window_server.start()
          WindowClient.clients.append(self)
  
      def destory(self):
          if self in WindowClient.clients:
              WindowClient.clients.remove(self)
          if len(WindowClient.clients) == 0:
              window_server.stop()
  
      @staticmethod
      def running():
          return WindowServer.status == Status.RUNNING
  
      def get_capture(self):
          """
          copies the recorded screen and then pre processes its
          :return: game window image
          """
          if WindowServer.status == Status.CRASHED:
              return None
  
          if not window_server.screen_ready():
              print("waiting for screen...")
              helper.wait_until(window_server.screen_ready)
              print("screen ready, continuing...")
  
          temp_img = WindowServer.Screen
  
          if temp_img is None or temp_img.size == 0:
              return None
  
          if self.color is not None:
              temp_img = cv2.cvtColor(temp_img, self.color)
  
          if self.crop is not None:
              temp_img = temp_img[self.crop[1]:self.crop[3], self.crop[0]:self.crop[2]]
  
          if self.scale is not None:
              temp_img = cv2.resize(temp_img, (self.scale[0], self.scale[1]), interpolation=cv2.INTER_AREA)
  
          return temp_img
  
      def processed_image(self, func=None):
          """
          processes the image using the function provided
          :param func: function to process image
          :return: processed image
          """
          if WindowServer.status == Status.CRASHED:
              return None
  
          img = self.get_capture()
  
          if img is None:
              return None
  
          if func is None:
              return img
          else:
              return func(img)
  
      def show(self, to_show, resize=None, func=None):
          """
          Displays the processed image for debugging purposes
          :param ready_img: send ready image, just show the `ready_img` directly
          :param resize: scale the image to make small images more visible
          :param func: function to process the image
          """
          if WindowServer.status == Status.CRASHED:
              return
  
          if not self.show_name:
              logging.warning("You need to assign a name first")
              return
  
          if not to_show:
              cv2.destroyWindow(self.show_name)
              return
  
          img = self.processed_image(func)
  
          if img is None:
              return
  
          if resize is not None:
              img = imutils.resize(img, width=resize)
          cv2.imshow(self.show_name, img)
          cv2.waitKey(25)
  