from tkinter import Canvas, PhotoImage
import tkinter as tk
from typing import overload
import cv2
from PIL import Image, ImageTk
import time

ITEM_TYPE_NONE = 0
# simple items
ITEM_TYPE_IMAGE = 1
ITEM_TYPE_STATUS = 2
ITEM_TYPE_ANIMATIONGIF = 3
ITEM_TYPE_VIDEO = 4
# complex items
ITEM_TYPE_ITEMSGROUP = 101

# region OOCanvas


class OOCanvas(Canvas):
    def __init__(self, master=None, image=None, command=None, **kw):
        super(OOCanvas, self).__init__(
            master=master, image=image, command=command, **kw)
        self.items_list = list()

    def create_ooImage(self, x=0, y=0):
        img = OOCanvasImage(self, int(x), int(y))
        self.items_list.append(img)

    def create_ooStatus(self, x=0, y=0):
        st = OOCanvasMultiStatus(self, int(x), int(y))
        self.items_list.append(st)
        return st

    def create_ooVideo(self, x=0, y=0, video_source=0):
        st = OOCanvasVideo(self, int(x), int(y), int(video_source))
        self.items_list.append(st)
        return st

    def create_ooItemsGroup(self):
        return OOCanvasItemsGroup(self)

# endregion

# region CanvasItem abstract classes


class OOCanvasItem:
    """Abstract class"""

    def __init__(self, canvas, x, y):
        self.canv = canvas
        self.objectType = ITEM_TYPE_NONE
        self.isVisible = True
        self.x = x
        self.y = y
        self.anchor = tk.CENTER

    def make_visible(self):
        self.isVisible = True

    def make_hidden(self):
        self.isVisible = False

    def set_location(self, x, y):
        self.x = x
        self.y = y

    def move(self, xmovement, ymovement):
        self.x += xmovement
        self.y += ymovement

    def set_anchor(self, anchor):
        self.anchor = anchor

    def raise_to_top(self):
        raise NotImplementedError("Please Implement this method")

    def dispose(self):
        raise NotImplementedError("Please Implement this method")

    # region events
    def add_left_mouse_button_down(self, callback):
        raise NotImplementedError("Please Implement this method")

    def add_left_mouse_button_up(self, callback):
        raise NotImplementedError("Please Implement this method")

    def add_mouse_double_click(self, callback):
        raise NotImplementedError("Please Implement this method")

    def add_right_mouse_button_down(self, callback):
        raise NotImplementedError("Please Implement this method")

    def add_right_mouse_button_up(self, callback):
        raise NotImplementedError("Please Implement this method")

    def remove_left_mouse_button_down(self, callback):
        raise NotImplementedError("Please Implement this method")

    def remove_left_mouse_button_up(self, callback):
        raise NotImplementedError("Please Implement this method")

    def remove_mouse_double_click(self, callback):
        raise NotImplementedError("Please Implement this method")

    def remove_right_mouse_button_down(self, callback):
        raise NotImplementedError("Please Implement this method")

    def remove_right_mouse_button_up(self, callback):
        raise NotImplementedError("Please Implement this method")
    # endregion


class OOCanvasSimpleItem(OOCanvasItem):
    """Abstract class"""

    def __init__(self, canvas, x, y):
        super(OOCanvasSimpleItem, self).__init__(canvas, x=x, y=y)
        self.__id = None

    def make_visible(self):
        self.canv.itemconfigure(self.__id, state=tk.NORMAL)
        OOCanvasItem.make_visible()
        return self

    def make_hidden(self):
        self.canv.itemconfigure(self.__id, state=tk.HIDDEN)
        OOCanvasItem.make_hidden()
        return self

    def set_location(self, x, y):
        self.canv.moveto(self.__id, x, y)
        OOCanvasItem.set_location(x, y)
        return self

    def move(self, xmovement, ymovement):
        self.canv.move(self.__id, xmovement, ymovement)
        OOCanvasItem.move(xmovement, ymovement)
        return self

    def set_anchor(self, anchor):
        self.canv.itemconfigure(self.__id, anchor=anchor)
        OOCanvasItem.set_anchor(anchor)
        return self

    def raise_to_top(self):
        self.canv.tag_raise(self.__id)
        return self

    def dispose(self):
        self.canv.delete(self.__id)
        return self

    # region events
    def add_left_mouse_button_down(self, callback):
        """A method with an arguments (event)"""
        self.canv.tag_bind(self.__id,
                           '<Button-1>', callback)
        return self

    def add_left_mouse_button_up(self, callback):
        """A method with an arguments (event)"""
        self.canv.tag_bind(self.__id,
                           '<ButtonRelease-1>', callback)
        return self

    def add_mouse_double_click(self, callback):
        """A method with an arguments (event)"""
        self.canv.tag_bind(self.__id,
                           '<Double-Button-1>', callback)
        return self

    def add_right_mouse_button_down(self, callback):
        """A method with an arguments (event)"""
        self.canv.tag_bind(self.__id,
                           '<Button-3>', callback)
        return self

    def add_right_mouse_button_up(self, callback):
        """A method with an arguments (event)"""
        self.canv.tag_bind(self.__id,
                           '<ButtonRelease-3>', callback)
        return self

    def remove_left_mouse_button_down(self, callback):
        """A method with an arguments (event)"""
        self.canv.tag_unbind(self.__id,
                             '<Button-1>', callback)
        return self

    def remove_left_mouse_button_up(self, callback):
        """A method with an arguments (event)"""
        self.canv.tag_unbind(self.__id,
                             '<ButtonRelease-1>', callback)
        return self

    def remove_mouse_double_click(self, callback):
        """A method with an arguments (event)"""
        self.canv.tag_unbind(self.__id,
                             '<Double-Button-1>', callback)
        return self

    def remove_right_mouse_button_down(self, callback):
        """A method with an arguments (event)"""
        self.canv.tag_unbind(self.__id,
                             '<Button-3>', callback)
        return self

    def remove_right_mouse_button_up(self, callback):
        """A method with an arguments (event)"""
        self.canv.tag_unbind(self.__id,
                             '<ButtonRelease-3>', callback)
        return self
    # endregion


class OOCanvasComplexItem(OOCanvasItem):
    """Abstract class"""

    def __init__(self, canvas, x, y):
        super(OOCanvasComplexItem, self).__init__(canvas, x=x, y=y)
        self.__items = list()

    def _add_item(self, item):
        self.__items.append(item)
        return self

    def make_visible(self):
        for item in self.__items:
            item.make_visible()
        super.make_visible()
        return self

    def make_hidden(self):
        for item in self.__items:
            item.make_hidden()
        super.make_hidden()
        return self

    def set_location(self, x, y):
        for item in self.__items:
            item.set_location(x, y)
        super.set_location(x, y)
        return self

    def move(self, xmovement, ymovement):
        for item in self.__items:
            item.move(xmovement, ymovement)
        super.move(xmovement, ymovement)
        return self

    def set_anchor(self):
        raise NotImplementedError("Please Implement this method")

    def raise_to_top(self):
        for item in self.__items:
            item.raise_to_top()
        return self

    # region events
    def add_left_mouse_button_down(self, callback):
        """A method with an arguments (event)"""
        for item in self.__items:
            item.add_left_mouse_button_down(callback)
        return self

    def add_left_mouse_button_up(self, callback):
        """A method with an arguments (event)"""
        for item in self.__items:
            item.add_left_mouse_button_up(callback)
        return self

    def add_mouse_double_click(self, callback):
        """A method with an arguments (event)"""
        for item in self.__items:
            item.add_mouse_double_click(callback)
        return self

    def add_right_mouse_button_down(self, callback):
        """A method with an arguments (event)"""
        for item in self.__items:
            item.add_right_mouse_button_down(callback)
        return self

    def add_right_mouse_button_up(self, callback):
        """A method with an arguments (event)"""
        for item in self.__items:
            item.add_right_mouse_button_up(callback)
        return self

    def remove_left_mouse_button_down(self, callback):
        """A method with an arguments (event)"""
        for item in self.__items:
            item.remove_left_mouse_button_down(callback)
        return self

    def remove_left_mouse_button_up(self, callback):
        """A method with an arguments (event)"""
        for item in self.__items:
            item.remove_left_mouse_button_up(callback)
        return self

    def remove_mouse_double_click(self, callback):
        """A method with an arguments (event)"""
        for item in self.__items:
            item.remove_mouse_double_click(callback)
        return self

    def remove_right_mouse_button_down(self, callback):
        """A method with an arguments (event)"""
        for item in self.__items:
            item.remove_right_mouse_button_down(callback)
        return self

    def remove_right_mouse_button_up(self, callback):
        """A method with an arguments (event)"""
        for item in self.__items:
            item.remove_right_mouse_button_up(callback)
        return self
    # endregion
# endregion

# region complex items


class OOCanvasItemsGroup(OOCanvasComplexItem):
    """Group of canvas items to manage group situations"""

    def __init__(self, canvas):
        super(OOCanvasItemsGroup, self).__init__(canvas, x=0, y=0)
        self.objectType = ITEM_TYPE_ITEMSGROUP

    def add_item(self, item):
        self._add_item(item)
        return self

# endregion

# region simple items


class OOCanvasImage(OOCanvasSimpleItem):
    """OOImage for OOCanvas"""

    def __init__(self, canvas, x, y, imageId=None):
        super(OOCanvasImage, self).__init__(canvas, x=x, y=y)
        self.objectType = ITEM_TYPE_IMAGE
        if imageId == None:
            self.__id = self.canv.create_image(x, y)
        else:
            self.set_location(x, y)

    def set_image(self, image):
        self.canv.itemconfig(self.__id, image=image)
        return self


class OOCanvasMultiStatus(OOCanvasSimpleItem):
    """OOStatus for OOCanvas"""

    def __init__(self, canvas, x, y):
        super(OOCanvasMultiStatus, self).__init__(canvas, x=x, y=y)
        self.objectType = ITEM_TYPE_STATUS
        self.__id = self.canv.create_image(x, y)
        self.__statuses = dict()
        self.__selected_statusKey = None

    def add_status_image(self, statusKey, image):
        item = OOCanvasImage(self.canv, self.x, self.y, self.__id)\
            .set_image(image)
        self.__statuses[statusKey] = item
        return self

    @overload
    def add_status_gif(self, statusKey, image, gifFramesArray, nextFrameAfter_ms=100):
        item = OOCanvasAnimationGif(self.canv, self.x, self.y, self.__id)\
            .set_image(image, gifFramesArray, nextFrameAfter_ms)
        self.__statuses[statusKey] = item
        return self

    @overload
    def add_status_gif(self, statusKey, imageAddress, nextFrameAfter_ms=100):
        item = OOCanvasAnimationGif(self.canv, self.x, self.y, self.__id)\
            .set_image(imageAddress, nextFrameAfter_ms)
        self.__statuses[statusKey] = item
        return self

    def add_status_video(self, statusKey, videoSource):
        item = OOCanvasVideo(self.canv, self.x, self.y, self.__id)\
            .set_video_source(videoSource)
        self.__statuses[statusKey] = item
        return self

    def select_status(self, statusKey):
        self.__selected_statusKey = statusKey
        st = self.__statuses.get(statusKey)
        if st == None:
            self.canv.itemconfig(self.__id, image=None)
        else:
            if self.__loop_active:
                self.__loop_active = False
                self.canv.master.after_cancel(self.__loop)
            self.canv.itemconfig(self.__id, image=st['image'])
            self.canv.tag_raise(self.__id)
            if st['gifFramesArray'] is not None:
                self.__frames = st['gifFramesArray']
                self.__frames_len = len(self.__frames)
                self.__current_frame_index = 0
                self.__nextFrameAfter_ms = st['nextFrameAfter_ms']
                self.__loop_active = True
                self.__loop = self.canv.master.after(
                    st['nextFrameAfter_ms'], self.__frame_manager)
        return self

    def get_selected_statusKey(self):
        return self.__selected_statusKey


class OOCanvasAnimationGif(OOCanvasSimpleItem):
    """OOAnimationGif for OOCanvas"""

    def __init__(self, canvas, x, y, imageId=None):
        super(OOCanvasAnimationGif, self).__init__(canvas, x=x, y=y)
        self.objectType = ITEM_TYPE_ANIMATIONGIF
        if imageId == None:
            self.__id = self.canv.create_image(x, y)
        else:
            self.set_location(x, y)
        self.__first_image = None
        self.__loop_active = False
        self.__loop = None
        self.__frames = None
        self.__frames_len = 0
        self.__current_frame_index = 0
        self.__nextFrameAfter_ms = 1

    @overload
    def set_image(self, image, framesArray, nextFrameAfter_ms=100):
        self.canv.itemconfig(self.__id, image=image)
        self.__first_image = image
        self.__frames = framesArray
        self.__frames_len = len(framesArray)
        self.__nextFrameAfter_ms = nextFrameAfter_ms
        return self

    @overload
    def set_image(self, imageAddress, nextFrameAfter_ms=100):
        self.__first_image = PhotoImage(imageAddress)
        self.canv.itemconfig(self.__id, image=self.__first_image)
        self.__frames_len = self.__find_frames_count(Image.open(imageAddress))
        self.__frames = [PhotoImage(
            file=imageAddress, format='gif -index %i' % (i)) for i in range(self.__frames_len)]
        self.__nextFrameAfter_ms = nextFrameAfter_ms
        return self

    def start(self):
        if not self.__loop_active:
            self.__loop_active = True
            self.__loop = self.canv.master.after(
                self.__nextFrameAfter_ms, self.__frame_manager)
        return self

    def start_from_beginning(self):
        self.__current_frame_index = 0
        self.start()
        return self

    def stop(self):
        if self.__loop_active:
            self.__loop_active = False
            self.canv.master.after_cancel(self.__loop)
        return self

    def __frame_manager(self):
        self.canv.itemconfig(
            self.__id, image=self.__frames[self.__current_frame_index])
        if self.__current_frame_index >= self.__frames_len-1:
            self.__current_frame_index = 0
        else:
            self.__current_frame_index += 1
        self.__loop = self.canv.master.after(
            self.__nextFrameAfter_ms, self.__frame_manager)

    def __find_frames_count(gif_obj):
        gif_obj.seek(0)  # move to the start of the gif, frame 0
        count = 0
        # run a while loop to loop through the frames
        while True:
            try:
                count += 1
                # now move to the next frame of the gif
                # image.tell() => current frame
                gif_obj.seek(gif_obj.tell() + 1)
            except EOFError:
                return count


class OOCanvasVideo(OOCanvasSimpleItem):
    """OOVideo for OOCanvas"""

    def __init__(self, canvas, x, y, imageId=None):
        super(OOCanvasVideo, self).__init__(canvas, x=x, y=y)
        self.objectType = ITEM_TYPE_VIDEO
        if imageId == None:
            self.__id = self.canv.create_image(x, y)
        else:
            self.set_location(x, y)
        self.__delay = 40
        self.__speed = 1
        self.__repeat = False
        self.__loop_active = False
        self.__loop = None

    def set_video_source(self, video_source=0):
        # open video source (by default 0 will try to open the computer webcam)
        self.__video = _VideoCapture(video_source)
        self.__delay = 1000 / self.__video.get_frame_rate()
        return self

    def set_speed(self, speed):
        """a float bigger than zero - one for no change in speed"""
        if float(speed) > 0:
            self.__speed = float(speed)
        return self

    def start(self):
        if not self.__loop_active:
            self.__loop_active = True
            self.__loop = self.canv.master.after(
                self.__nextFrameAfter_ms, self.__update)
        return self

    def start_from_beginning(self):
        self.stop()
        self.__video.reset()
        self.start()
        return self

    def stop(self):
        if self.__loop_active:
            self.__loop_active = False
            self.canv.master.after_cancel(self.__loop)
        return self

    def forward(self, framesCount=1):
        self.__video.forward(framesCount)
        return self

    def backward(self, framesCount=1):
        self.__video.backward(framesCount)
        return self

    def repeat_video(self, repeat=True):
        self.__repeat = bool(repeat)
        return self

    def save_snapshot(self):
        # Get a frame from the video source
        ret, frame = self.__video.get_frame()

        if ret:
            cv2.imwrite("frame-" + time.strftime("%d-%m-%Y-%H-%M-%S") +
                        ".jpg", cv2.cvtColor(frame, cv2.COLOR_RGB2BGR))
        return self

    def __update(self):
        # Get a frame from the video source
        ret, frame = self.__video.get_frame()

        if ret:
            self.photo = ImageTk.PhotoImage(image=Image.fromarray(frame))
            self.canv.itemconfig(self.__id, image=self.photo)

        if self.__video.end_of_video and self.__repeat:
            self.__video.reset()
        if not self.__video.end_of_video:
            self.__loop = self.canv.master.after(
                self.__delay*self.__speed, self.__update)
# endregion

# region private helper classes


class _VideoCapture:
    def __init__(self, video_source=0):
        # Open the video source
        self.vid = cv2.VideoCapture(video_source)
        if not self.vid.isOpened():
            raise ValueError("Unable to open video source", video_source)

        self.__current_frame_index = 0
        self.__frames_count = self.vid.get(cv2.CAP_PROP_FRAME_COUNT)
        # Get video source width and height
        self.width = self.vid.get(cv2.CAP_PROP_FRAME_WIDTH)
        self.height = self.vid.get(cv2.CAP_PROP_FRAME_HEIGHT)
        self.end_of_video = False

    def reset(self):
        self.__current_frame_index = 0
        self.end_of_video = False

    def forward(self, frameCount):
        self.__current_frame_index += int(frameCount)
        if self.__current_frame_index >= self.__frames_count:
            self.__current_frame_index = self.__frames_count
            self.end_of_video = True

    def backward(self, frameCount):
        self.__current_frame_index -= int(frameCount)
        if self.__current_frame_index < 0:
            self.__current_frame_index = 0
            self.end_of_video = False

    def get_frame(self):
        if self.vid.isOpened():
            # set next frame index
            self.vid.set(cv2.CV_CAP_PROP_POS_FRAMES,
                         self.__current_frame_index)
            ret, frame = self.vid.read()
            self.__current_frame_index += 1
            if self.__current_frame_index >= self.__frames_count:
                self.end_of_video = True
            if ret:
                # Return a boolean success flag and the current frame converted to BGR
                return (ret, cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
            else:
                return (ret, None)
        else:
            return (False, None)

    def get_frame_rate(self):
        """frames per second"""
        return self.vid.get(cv2.CAP_PROP_FPS)

     # Release the video source when the object is destroyed
    def dispose(self):
        if self.vid.isOpened():
            self.vid.release()


class CanvasEventHandler(object):
    def __init__(self):
        self.__registeredMethods = []

    def __iadd__(self, Ehandler):
        self.__registeredMethods.append(Ehandler)
        return self

    def __isub__(self, Ehandler):
        self.__registeredMethods.remove(Ehandler)
        return self

    def __call__(self, *args, **keywargs):
        for method in self.__registeredMethods:
            method(*args, **keywargs)
# endregion


# region potential
    # def addtag_above(self, newtag):
    #     """Add tag NEWTAG to all items above TAGORID."""
    #     self.canvas.addtag_above(newtag, self.canvasItemId)

    # def addtag_all(self, newtag):
    #     """Add tag NEWTAG to all items."""
    #     self.canvas.addtag_all(newtag, self.canvasItemId)

    # def addtag_below(self, newtag):
    #     """Add tag NEWTAG to all items below TAGORID."""
    #     self.canvas.addtag_below(newtag, self.canvasItemId)

    # def addtag_closest(self, newtag, x, y, halo=None, start=None):
    #     """Add tag NEWTAG to item which is closest to pixel at X, Y.
    #     If several match take the top-most.
    #     All items closer than HALO are considered overlapping (all are
    #     closests). If START is specified the next below this tag is taken."""
    #     self.canvas.addtag_closest(self, newtag, x, y, halo=halo, start=start)

    # def addtag_enclosed(self, newtag, x1, y1, x2, y2):
    #     """Add tag NEWTAG to all items in the rectangle defined
    #     by X1,Y1,X2,Y2."""
    #     self.addtag(newtag, 'enclosed', x1, y1, x2, y2)

    # def addtag_overlapping(self, newtag, x1, y1, x2, y2):
    #     """Add tag NEWTAG to all items which overlap the rectangle
    #     defined by X1,Y1,X2,Y2."""
    #     self.addtag(newtag, 'overlapping', x1, y1, x2, y2)

    # def addtag_withtag(self, newtag, tagOrId):
    #     """Add tag NEWTAG to all items with TAGORID."""
    #     self.addtag(newtag, 'withtag', tagOrId)

    # def bbox(self, *args):
    #     """Return a tuple of X1,Y1,X2,Y2 coordinates for a rectangle
    #     which encloses all items with tags specified as arguments."""
    #     return self._getints(
    #         self.tk.call((self._w, 'bbox') + args)) or None

    # def tag_unbind(self, tagOrId, sequence, funcid=None):
    #     """Unbind for all items with TAGORID for event SEQUENCE  the
    #     function identified with FUNCID."""
    #     self.tk.call(self._w, 'bind', tagOrId, sequence, '')
    #     if funcid:
    #         self.deletecommand(funcid)

    # def tag_bind(self, tagOrId, sequence=None, func=None, add=None):
    #     """Bind to all items with TAGORID at event SEQUENCE a call to function FUNC.

    #     An additional boolean parameter ADD specifies whether FUNC will be
    #     called additionally to the other bound function or whether it will
    #     replace the previous function. See bind for the return value."""
    #     return self._bind((self._w, 'bind', tagOrId),
    #                       sequence, func, add)

    # def canvasx(self, screenx, gridspacing=None):
    #     """Return the canvas x coordinate of pixel position SCREENX rounded
    #     to nearest multiple of GRIDSPACING units."""
    #     return self.tk.getdouble(self.tk.call(
    #         self._w, 'canvasx', screenx, gridspacing))

    # def canvasy(self, screeny, gridspacing=None):
    #     """Return the canvas y coordinate of pixel position SCREENY rounded
    #     to nearest multiple of GRIDSPACING units."""
    #     return self.tk.getdouble(self.tk.call(
    #         self._w, 'canvasy', screeny, gridspacing))

    # def coords(self, *args):
    #     """Return a list of coordinates for the item given in ARGS."""
    #     # XXX Should use _flatten on args
    #     return [self.tk.getdouble(x) for x in
    #             self.tk.splitlist(
    #         self.tk.call((self._w, 'coords') + args))]

    # def dchars(self, *args):
    #     """Delete characters of text items identified by tag or id in ARGS (possibly
    #     several times) from FIRST to LAST character (including)."""
    #     self.tk.call((self._w, 'dchars') + args)

    # def delete(self, *args):
    #     """Delete items identified by all tag or ids contained in ARGS."""
    #     self.tk.call((self._w, 'delete') + args)

    # def dtag(self, *args):
    #     """Delete tag or id given as last arguments in ARGS from items
    #     identified by first argument in ARGS."""
    #     self.tk.call((self._w, 'dtag') + args)

    # def find(self, *args):
    #     """Internal function."""
    #     return self._getints(
    #         self.tk.call((self._w, 'find') + args)) or ()

    # def find_above(self, tagOrId):
    #     """Return items above TAGORID."""
    #     return self.find('above', tagOrId)

    # def find_all(self):
    #     """Return all items."""
    #     return self.find('all')

    # def find_below(self, tagOrId):
    #     """Return all items below TAGORID."""
    #     return self.find('below', tagOrId)

    # def find_closest(self, x, y, halo=None, start=None):
    #     """Return item which is closest to pixel at X, Y.
    #     If several match take the top-most.
    #     All items closer than HALO are considered overlapping (all are
    #     closest). If START is specified the next below this tag is taken."""
    #     return self.find('closest', x, y, halo, start)

    # def find_enclosed(self, x1, y1, x2, y2):
    #     """Return all items in rectangle defined
    #     by X1,Y1,X2,Y2."""
    #     return self.find('enclosed', x1, y1, x2, y2)

    # def find_overlapping(self, x1, y1, x2, y2):
    #     """Return all items which overlap the rectangle
    #     defined by X1,Y1,X2,Y2."""
    #     return self.find('overlapping', x1, y1, x2, y2)

    # def find_withtag(self, tagOrId):
    #     """Return all items with TAGORID."""
    #     return self.find('withtag', tagOrId)

    # def focus(self, *args):
    #     """Set focus to the first item specified in ARGS."""
    #     return self.tk.call((self._w, 'focus') + args)

    # def gettags(self, *args):
    #     """Return tags associated with the first item specified in ARGS."""
    #     return self.tk.splitlist(
    #         self.tk.call((self._w, 'gettags') + args))

    # def icursor(self, *args):
    #     """Set cursor at position POS in the item identified by TAGORID.
    #     In ARGS TAGORID must be first."""
    #     self.tk.call((self._w, 'icursor') + args)

    # def index(self, *args):
    #     """Return position of cursor as integer in item specified in ARGS."""
    #     return self.tk.getint(self.tk.call((self._w, 'index') + args))

    # def insert(self, *args):
    #     """Insert TEXT in item TAGORID at position POS. ARGS must
    #     be TAGORID POS TEXT."""
    #     self.tk.call((self._w, 'insert') + args)

    # def itemcget(self, tagOrId, option):
    #     """Return the resource value for an OPTION for item TAGORID."""
    #     return self.tk.call(
    #         (self._w, 'itemcget') + (tagOrId, '-'+option))

    # def itemconfigure(self, tagOrId, cnf=None, **kw):
    #     """Configure resources of an item TAGORID.

    #     The values for resources are specified as keyword
    #     arguments. To get an overview about
    #     the allowed keyword arguments call the method without arguments.
    #     """
    #     return self._configure(('itemconfigure', tagOrId), cnf, kw)

    # itemconfig = itemconfigure

    # # lower, tkraise/lift hide Misc.lower, Misc.tkraise/lift,
    # # so the preferred name for them is tag_lower, tag_raise
    # # (similar to tag_bind, and similar to the Text widget);
    # # unfortunately can't delete the old ones yet (maybe in 1.6)
    # def tag_lower(self, *args):
    #     """Lower an item TAGORID given in ARGS
    #     (optional below another item)."""
    #     self.tk.call((self._w, 'lower') + args)

    # lower = tag_lower

    # def postscript(self, cnf={}, **kw):
    #     """Print the contents of the canvas to a postscript
    #     file. Valid options: colormap, colormode, file, fontmap,
    #     height, pageanchor, pageheight, pagewidth, pagex, pagey,
    #     rotate, width, x, y."""
    #     return self.tk.call((self._w, 'postscript') +
    #                         self._options(cnf, kw))

    # def tag_raise(self, *args):
    #     """Raise an item TAGORID given in ARGS
    #     (optional above another item)."""
    #     self.tk.call((self._w, 'raise') + args)

    # lift = tkraise = tag_raise

    # def scale(self, *args):
    #     """Scale item TAGORID with XORIGIN, YORIGIN, XSCALE, YSCALE."""
    #     self.tk.call((self._w, 'scale') + args)

    # def scan_mark(self, x, y):
    #     """Remember the current X, Y coordinates."""
    #     self.tk.call(self._w, 'scan', 'mark', x, y)

    # def scan_dragto(self, x, y, gain=10):
    #     """Adjust the view of the canvas to GAIN times the
    #     difference between X and Y and the coordinates given in
    #     scan_mark."""
    #     self.tk.call(self._w, 'scan', 'dragto', x, y, gain)

    # def select_adjust(self, tagOrId, index):
    #     """Adjust the end of the selection near the cursor of an item TAGORID to index."""
    #     self.tk.call(self._w, 'select', 'adjust', tagOrId, index)

    # def select_clear(self):
    #     """Clear the selection if it is in this widget."""
    #     self.tk.call(self._w, 'select', 'clear')

    # def select_from(self, tagOrId, index):
    #     """Set the fixed end of a selection in item TAGORID to INDEX."""
    #     self.tk.call(self._w, 'select', 'from', tagOrId, index)

    # def select_item(self):
    #     """Return the item which has the selection."""
    #     return self.tk.call(self._w, 'select', 'item') or None

    # def select_to(self, tagOrId, index):
    #     """Set the variable end of a selection in item TAGORID to INDEX."""
    #     self.tk.call(self._w, 'select', 'to', tagOrId, index)

    # def type(self, tagOrId):
    #     """Return the type of the item TAGORID."""
    #     return self.tk.call(self._w, 'type', tagOrId) or None
# endregion
