/*  Copyright 2007 Niclas Hedhman.
 *
 * Licensed 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.ops4j.pax.logging.log4j2.internal;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.osgi.service.log.LogEntry;
import org.osgi.service.log.LogListener;
import org.osgi.service.log.LogReaderService;

/**
 * @noinspection SynchronizeOnNonFinalField
 */
public class LogReaderServiceImpl
    implements LogReaderService
{

    private List m_listeners;
    private final LinkedList m_entries;
    private int m_maxEntries;

    public LogReaderServiceImpl(int maxEntries)
    {
        m_maxEntries = maxEntries;
        m_entries = new LinkedList();
    }

    public void addLogListener( LogListener logListener )
    {
        synchronized( this )
        {
            // Do not update the list directly to avoid
            // the cost of a synchronized block when iterating
            // listeners in fireEvent()
            ArrayList clone;
            if( m_listeners == null )
            {
                clone = new ArrayList();
            }
            else
            {
                clone = new ArrayList( m_listeners );
            }
            clone.add( logListener );
            m_listeners = clone;
        }
    }

    public void removeLogListener( LogListener logListener )
    {
        synchronized( this )
        {
            // Do not update the list directly to avoid
            // the cost of a synchronized block when iterating
            // listeners in fireEvent()
            if( m_listeners == null )
            {
                return;
            }
            ArrayList clone = new ArrayList( m_listeners );
            clone.remove( logListener );
            if( clone.size() == 0 )
            {
                m_listeners = null;
            }
            else
            {
                m_listeners = clone;
            }
        }
    }

    public Enumeration getLog()
    {
        // Need to do a copy to avoid a ConcurrentModificationException if
        // a new event is logged while the enumeration is iterated.
        synchronized (m_entries)
        {
            return Collections.enumeration( new ArrayList( m_entries ) );
        }
    }

    private void cleanUp()
    {
        // caller must synchronize on m_entries
        while( m_entries.size() > m_maxEntries )
        {
            m_entries.removeLast();
        }
    }

    final void fireEvent( LogEntry entry )
    {
        synchronized( m_entries )
        {
            m_entries.addFirst( entry );
            cleanUp();
        }
        final List listeners = m_listeners;
        if( listeners == null )
        {
            return;
        }
        Iterator iterator = listeners.iterator();
        while( iterator.hasNext() )
        {
            LogListener listener = (LogListener) iterator.next();
            fire( listener, entry );
        }
    }

    private void fire( LogListener listener, LogEntry entry )
    {
        try
        {
            listener.logged( entry );
        }
        catch( Throwable e )
        {
            //TODO: Log that we are removing the LogListener, since it is throwing exception. For now System.err
            System.err.println( "'" + listener + "' is removed as a LogListener, since it threw an exception." );
            removeLogListener( listener );
        }
    }

    /**
     * Sets the max number of entries that should be allowed in the LogReader buffer.
     *
     * @param maxSize the maximum number of entries in the LogReader buffer.
     */
    final void setMaxEntries( int maxSize )
    {
        synchronized (m_entries)
        {
            m_maxEntries = maxSize;
        }
    }
}

@@ -719,27 +719,21 @@
 va.util.
-Enumeration
+Deque
 ;%0Aimport
@@ -743,24 +743,27 @@
 va.util.
-It
+Enum
 erat
-or
+ion
 ;%0Aimport
@@ -807,16 +807,66 @@
 il.List;
+%0Aimport java.util.concurrent.CopyOnWriteArrayList;
 %0A%0Aimport
@@ -1122,24 +1122,85 @@
 ate 
-List m_listeners
+final List%3CLogListener%3E m_listeners = new CopyOnWriteArrayList%3CLogListener%3E()
 ;%0A  
@@ -1215,26 +1215,31 @@
 e final 
-LinkedList
+Deque%3CLogEntry%3E
  m_entri
@@ -1392,24 +1392,34 @@
 w LinkedList
+%3CLogEntry%3E
 ();%0A    %7D%0A%0A 
@@ -1493,456 +1493,24 @@
     
-synchronized( this )%0A        %7B%0A            // Do not update the list directly to avoid%0A            // the cost of a synchronized block when iterating%0A            // listeners in fireEvent()%0A            ArrayList clone;%0A            if( m_listeners == null )%0A            %7B%0A                clone = new ArrayList();%0A            %7D%0A            else%0A            %7B%0A                clone = new ArrayList( m_listeners );%0A            %7D%0A            clone
+m_listeners
 .add(
- 
 logL
@@ -1520,55 +1520,11 @@
 ener
- 
 );%0A
-            m_listeners = clone;%0A        %7D%0A
     
@@ -1605,373 +1605,27 @@
     
-synchronized( this )%0A        %7B%0A            // Do not update the list directly to avoid%0A            // the cost of a synchronized block when iterating%0A            // listeners in fireEvent()%0A            if( m_listeners == null )%0A            %7B%0A                return;%0A            %7D%0A            ArrayList clone = new ArrayList( m_listeners );%0A            clone
+m_listeners
 .remove(
  log
@@ -1620,17 +1620,16 @@
 .remove(
- 
 logListe
@@ -1635,204 +1635,11 @@
 ener
- 
 );%0A
-            if( clone.size() == 0 )%0A            %7B%0A                m_listeners = null;%0A            %7D%0A            else%0A            %7B%0A                m_listeners = clone;%0A            %7D%0A        %7D%0A
     
@@ -1922,16 +1922,26 @@
 rrayList
+%3CLogEntry%3E
 ( m_entr
@@ -2348,214 +2348,60 @@
 List
- l
+%3CLogL
 istener
-s = m_listeners;%0A        if( listeners == null )%0A        %7B%0A            return;%0A        %7D%0A        Iterator iterator = listeners.iterator();%0A        while( iterator.hasNext() )%0A        %7B%0A            
+%3E listeners = m_listeners;%0A        for (
 LogL
@@ -2421,40 +2421,22 @@
 ner 
-= (LogL
+: l
 istener
+s
 ) 
-iterator.next();
+%7B
 %0A   
@@ -2449,17 +2449,16 @@
    fire(
- 
 listener
@@ -2460,25 +2460,24 @@
 tener, entry
- 
 );%0A        %7D

Use generics and concurrent classes.