/*
 *  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.groovy.json.internal;

import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * List implementation that lazily converts overlay {@link Value} instances on access.
 */
public class ValueList extends AbstractList<Object> {

    /**
     * Backing storage for parsed elements.
     */
    List<Object> list = new ArrayList<Object>(5);

    private final boolean lazyChop;
    /**
     * Tracks whether all entries have already been materialized.
     */
    boolean converted = false;

    /**
     * Creates a lazy list wrapper.
     *
     * @param lazyChop whether nested values should be chopped when touched
     */
    public ValueList(boolean lazyChop) {
        this.lazyChop = lazyChop;
    }

    /**
     * Converts the indexed element on demand and optionally chops nested containers.
     *
     * @param index element index
     * @return the materialized element
     */
    @Override
    public Object get(int index) {
        Object obj = list.get(index);

        if (obj instanceof Value) {
            obj = convert((Value) obj);
            list.set(index, obj);
        }

        chopIfNeeded(obj);
        return obj;
    }

    private static Object convert(Value value) {
        return value.toValue();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int size() {
        return list.size();
    }

    /**
     * Returns an iterator after materializing all pending {@link Value} entries.
     *
     * @return iterator over hydrated elements
     */
    @Override
    public Iterator<Object> iterator() {
        convertAllIfNeeded();
        return list.iterator();
    }

    private void convertAllIfNeeded() {
        if (!converted) {
            converted = true;
            for (int index = 0; index < list.size(); index++) {
                this.get(index);
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void clear() {
        list.clear();
    }

    /**
     * Adds a parsed element without forcing conversion.
     *
     * @param obj element to append
     * @return {@code true}
     */
    @Override
    public boolean add(Object obj) {
        return list.add(obj);
    }

    /**
     * Chops every overlay-backed element currently stored in the list.
     */
    public void chopList() {
        for (Object obj : list) {
            if (obj == null) continue;

            if (obj instanceof Value value) {
                if (value.isContainer()) {
                    chopContainer(value);
                } else {
                    value.chop();
                }
            }
        }
    }

    private void chopIfNeeded(Object object) {
        if (lazyChop) {
            if (object instanceof LazyValueMap m) {
                m.chopMap();
            } else if (object instanceof ValueList list) {
                list.chopList();
            }
        }
    }

    /**
     * Chops a nested container value without forcing unrelated branches.
     *
     * @param value container value to chop
     */
    static void chopContainer(Value value) {
        Object obj = value.toValue();
        if (obj instanceof LazyValueMap map) {
            map.chopMap();
        } else if (obj instanceof ValueList list) {
            list.chopList();
        }
    }

    /**
     * Exposes the raw backing list used during lazy parsing.
     *
     * @return the mutable backing list
     */
    public List<Object> list() {
        return this.list;
    }
}
