// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.2 OR LicenseRef-Slint-commercial

import { ListItem, ScrollView } from "std-widgets-impl.slint";

export component ListView inherits ScrollView {
    @children
    accessible-role: list;
}

component StandardListViewBase inherits ListView {
    in property <[StandardListViewItem]> model;
    in-out property <int> current-item: -1;

    callback current-item-changed(/* current-item */ int);
    callback item-pointer-event( /* item-index */ int, /* event */ PointerEvent,  /* absolute mouse position */ Point);

    public function set-current-item(index: int) {
        if (index < 0 || index >= model.length) {
            return;
        }

        bring-into-view(index);

        current-item = index;
        focus-item = index;
        current-item-changed(current-item);
    }

    private property <length> item-height: self.viewport-height / self.model.length;
    private property <int> into-view-item: 0;
    private property <length> into-view-item-y: root.item-y(root.into-view-item);
    private property <length> current-item-y: root.item-y(root.focus-item);
    private property <int> focus-item: 0;

    pure function first-visible-item() -> int {
        return min(root.model.length - 1, max(0, round(-root.viewport-y / root.item-height)));
    }

    pure function item-y(index: int) -> length {
        return root.viewport-y + index * root.item-height;
    }

    function bring-into-view(index: int) {
        if (index < 0 || index >= model.length) {
            return;
        }

        into-view-item = index;

        if (into-view-item-y < 0) {
            self.viewport-y += 0 - into-view-item-y;
        }

        if (into-view-item-y + item-height > self.visible-height) {
            self.viewport-y -= into-view-item-y + item-height - self.visible-height;
        }
    }

    protected function focus-up() {
        root.set-focus-item(root.focus-item - 1);
    }

    protected function focus-down() {
       root.set-focus-item(root.focus-item + 1);
    }

    protected function select-focus-item() {
        root.set-current-item(root.focus-item);
    }

    protected function focus-current-item() {
        root.focus-item = max(0, root.current-item);

        if (root.current-item-y + root.item-height < 0
            || root.current-item-y > root.height) {
                root.focus-item = root.first-visible-item();
        }
    }

    protected function set-focus-item(index: int) {
        root.focus-item = min(root.model.length - 1, max(0, index));
        root.bring-into-view(root.focus-item);
    }

    for item[index] in root.model : ListItem {
        height: self.min-height;
        item: item;
        index: index;
        is-selected: index == root.current-item;
        has-focus: root.has-focus && index == root.focus-item;
        has-hover: i-touch-area.has-hover;
        pressed: i-touch-area.pressed;
        pressed-x: i-touch-area.pressed-x;
        pressed-y: i-touch-area.pressed-y;

        i-touch-area := TouchArea {
            clicked => {
                root.set-current-item(index);
            }

            pointer-event(pe) => {
                root.item-pointer-event(index, pe, {
                    x: self.absolute-position.x + self.mouse-x - root.absolute-position.x,
                    y: self.absolute-position.y + self.mouse-y - root.absolute-position.y,
                });
            }
        }
    }
}

export component StandardListView inherits StandardListViewBase {
    forward-focus: i-focus-scope;

    i-focus-scope := FocusScope {
        x: 0;
        width: 0;  // Do not react on clicks

        focus-changed-event => {
            root.focus-current-item();
            root.has-focus = self.has-focus;
        }

        key-pressed(event) => {
            if (event.text == Key.UpArrow) {
                root.focus-up();
                return accept;
            } else if (event.text == Key.DownArrow) {
                root.focus-down();
                return accept;
            }else if (event.text == Key.Return) {
                root.select-focus-item();
                return accept;
            }
            reject
        }
    }
}
