# Copyright (c) 2018-2020, NVIDIA CORPORATION.  All rights reserved.
#
# NVIDIA CORPORATION and its licensors retain all intellectual property
# and proprietary rights in and to this software, related documentation
# and any modifications thereto.  Any use, reproduction, disclosure or
# distribution of this software and related documentation without an express
# license agreement from NVIDIA CORPORATION is strictly prohibited.
#
import sys, os
import omni.ui as ui


class StringQueueItem(ui.AbstractItem):
    def __init__(self, value: str):
        super().__init__()
        self._model = ui.SimpleStringModel(value)

    @property
    def model(self):
        return self._model

    @property
    def value(self):
        return self._model.get_value_as_string()


class StringQueueModel(ui.AbstractItemModel):
    def __init__(self, max_items: int = 4, value_changed_fn=None):
        super().__init__()
        self._value_changed_fn = value_changed_fn
        self._max_items = max_items
        self._items = []
        self._selected_index = ui.SimpleIntModel(0)
        # TODO: There's a bug that if the item selected doesn't have a different
        # index, then it doesn't trigger this callback. It's better to trigger on
        # mouse pressed but we don't have this option.
        self._selected_index.add_value_changed_fn(self._on_selection_changed)

    def __getitem__(self, idx: int) -> StringQueueItem:
        if idx < len(self._items):
            return self._items[idx]
        return None

    @property
    def selected_index(self) -> int:
        return self._selected_index.get_value_as_int()

    @selected_index.setter
    def selected_index(self, index: int):
        self._selected_index.set_value(index)

    def get_selected_item(self) -> StringQueueItem:
        index = self._selected_index.get_value_as_int()
        if index >= 0 and index < len(self._items):
            return self._items[index]
        return None

    def get_item_children(self, item) -> [StringQueueItem]:
        if item is None:
            return self._items
        return []

    def get_item_value_model(self, item, column_id) -> ui.AbstractValueModel:
        if item is None:
            return self._selected_index
        return item.model

    def find_item(self, value: str) -> StringQueueItem:
        for item in self._items:
            if item.value == value:
                return item
        return None

    def size(self) -> int:
        return len(self._items)

    def peek(self) -> StringQueueItem:
        if self._items:
            return self._items[0]
        return None

    def enqueue(self, value: str):
        if not value:
            return
        found = self.find_item(value)
        if not found:
            item = StringQueueItem(value)
            self._items.insert(0, item)
            while len(self._items) > self._max_items:
                self.dequeue()
            self.selected_index = 0
            self._item_changed(None)

    def dequeue(self):
        if self._items:
            self._items.pop(-1)
            self.selected_index = min(self.selected_index, self.size()-1)
            self._item_changed(None)

    def _on_selection_changed(self, model: ui.AbstractValueModel):
        if self._value_changed_fn:
            self._value_changed_fn(model)
        self._item_changed(None)

    def destroy(self):
        self._items = None
        self._selected_index = None


class VisitedHistory():
    def __init__(self, max_items: int = 100):
        self._max_items = max_items
        self._items = []
        self._selected_index = 0
        # the activation state for the visited history; when we are jumping between history entries, the visited history
        # should not change; for example, when we click the prev/next button, the path field would update to that entry
        # but those should not be counted in visited history.
        self._active = True

    def __getitem__(self, idx: int) -> str:
        if idx < len(self._items):
            return self._items[idx]
        return None

    def activate(self):
        self._active = True

    def deactivate(self):
        self._active = False

    @property
    def selected_index(self) -> int:
        return self._selected_index

    @selected_index.setter
    def selected_index(self, index: int):
        self._selected_index = index

    def get_selected_item(self) -> str:
        index = self._selected_index
        if index >= 0 and index < len(self._items):
            return self._items[index]
        return None

    def size(self) -> int:
        return len(self._items)

    def insert(self, value: str):
        if self._active is False:
            return
        if not value:
            return
        # avoid adding the same entry twice
        # this could happen when actions on top sets the browser bar path to the same path twice
        if self.size() > 0 and self._items[0] == value:
            return
        self._items.insert(0, value)
        while self.size() > self._max_items:
            self.pop()
        self.selected_index = 0

    def pop(self):
        if self._items:
            self._items.pop()
            self.selected_index = min(self.selected_index, self.size()-1)

    def destroy(self):
        self._items.clear()
