/*
 * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.javafx.util;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Iterator;

/**
 * This is a helper class for handling weak references across all devices.
 * We tried to use WeakHashMap, but it isn't available on mobile. We tried to
 * add it to mobile, but it requires ReferenceQueue and it appears that
 * ReferenceQueue requires support from the VM which we don't know that we
 * have on mobile. So this class attempts to lesson the likelyhood of
 * memory leaks.
 *
 * As we abandoned mobile, we considered removal of this class. But replacement
 * by WeakHashMap is not always possible as we use mutable elements. At least
 * it was now possible to optimize this class using the ReferenceQueue.
 */
public class WeakReferenceQueue<E> {
    /**
     * Reference queue for cleared weak references
     */
    private final ReferenceQueue garbage = new ReferenceQueue();

    /**
     * Strongly referenced list head
     */
    private Object strongRef = new Object();
    private ListEntry head = new ListEntry(strongRef, garbage);

    /**
     * Size of the queue
     */
    int size = 0;

    @SuppressWarnings("unchecked")
    public void add(E obj) {
        cleanup();
        size++;
        new ListEntry(obj, garbage).insert(head.prev);
    }

    public void remove(E obj) {
        cleanup();

        ListEntry entry = head.next;
        while (entry != head) {
            Object other = entry.get();
            if (other == obj) {
                size--;
                entry.remove();
                return;
            }
            entry = entry.next;
        }
    }

    public void cleanup() {
        ListEntry entry;
        while ((entry = (ListEntry) garbage.poll()) != null) {
            size--;
            entry.remove();
        }
    }

    public Iterator<? super E> iterator() {
        return new Iterator() {
            private ListEntry index = head;
            private Object next = null;

            public boolean hasNext() {
                next = null;
                while (next == null) {
                    ListEntry nextIndex = index.prev;
                    if (nextIndex == head) {
                        break;
                    }
                    next = nextIndex.get();
                    if (next == null) {
                        size--;
                        nextIndex.remove();
                    }
                }

                return next != null;
            }

            public Object next() {
                hasNext(); // forces us to clear out crap up to the next
                           // valid spot
                index = index.prev;
                return next;
            }

            public void remove() {
                if (index != head) {
                    ListEntry nextIndex = index.next;
                    size--;
                    index.remove();
                    index = nextIndex;
                }
            }
        };
    }

    private static class ListEntry extends WeakReference {
        ListEntry prev, next;

        public ListEntry(Object o, ReferenceQueue queue) {
            super(o, queue);
            prev = this;
            next = this;
        }

        public void insert(ListEntry where) {
            prev = where;
            next = where.next;
            where.next = this;
            next.prev = this;
        }

        public void remove() {
            prev.next = next;
            next.prev = prev;
            next = this;
            prev = this;
        }
    }

}
