/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 */
package org.apache.causeway.commons.internal.debug.xray;

import java.util.Optional;
import java.util.Stack;
import java.util.UUID;

import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.MutableTreeNode;

import org.jspecify.annotations.Nullable;

import org.apache.causeway.commons.internal.base._Strings;

public interface XrayModel {

    enum Stickiness {
        CAN_DELETE_NODE,
        CANNOT_DELETE_NODE;

        boolean isCanDeleteNode() { return this == CAN_DELETE_NODE; }
    }

    MutableTreeNode rootNode();

    default MutableTreeNode getThreadNode(final ThreadMemento threadMemento) {
        return lookupNode(threadMemento.id())
                .orElseGet(()->addContainerNode(
                        rootNode(),
                        threadMemento.label(),
                        threadMemento.id(),
                        Stickiness.CAN_DELETE_NODE));
    }

    MutableTreeNode addContainerNode(MutableTreeNode parent, String name, String id, Stickiness stickiness);

    default MutableTreeNode addContainerNode(
            final MutableTreeNode parent, final String name, final Stickiness stickiness) {
        return addContainerNode(parent, name, UUID.randomUUID().toString(), stickiness);
    }

    <T extends XrayDataModel> T addDataNode(MutableTreeNode parent, T dataModel);

    Optional<MutableTreeNode> lookupNode(String id);

    void remove(MutableTreeNode node);

    // -- DATA LOOKUP

    default Optional<XrayDataModel.Sequence> lookupSequence(final @Nullable String sequenceId) {
        return _Strings.isNullOrEmpty(sequenceId)
                ? Optional.empty()
                : lookupNode(sequenceId)
                    .map(node->
                        (XrayDataModel.Sequence)((DefaultMutableTreeNode) node).getUserObject());
    }

    // -- STACKS

    Stack<MutableTreeNode> getNodeStack(String id);

    // -- ID AND LABEL

    abstract class HasIdAndLabel {
        public abstract String getId();
        public abstract String getLabel();
        public abstract Stickiness getStickiness();

        @Override
        public final String toString() {
            return getLabel();
        }

    }

    // -- THREAD UTIL

    public record ThreadMemento(
            String id,
            String label,
            String multilinelabel) {

        public static ThreadMemento fromCurrentThread() {
            var ct = Thread.currentThread();
            return new ThreadMemento(
                    String.format("thread-%d-%s", ct.getId(), ct.getName()),
                    String.format("Thread-%d [%s]", ct.getId(), ct.getName()),
                    String.format("Thread-%d\n%s", ct.getId(), ct.getName()));
        }

    }

}
