/*
 * 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.felix.http.base.internal.service;

import static java.util.Collections.list;

import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Timer;
import java.util.TimerTask;

import org.apache.felix.http.base.internal.registry.HandlerRegistry;
import org.apache.felix.http.base.internal.runtime.dto.RequestInfoDTOBuilder;
import org.apache.felix.http.base.internal.runtime.dto.RuntimeDTOBuilder;
import org.apache.felix.http.base.internal.whiteboard.WhiteboardManager;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.http.runtime.HttpServiceRuntime;
import org.osgi.service.http.runtime.dto.RequestInfoDTO;
import org.osgi.service.http.runtime.dto.RuntimeDTO;

public final class HttpServiceRuntimeImpl implements HttpServiceRuntime
{
    /**
     * Service property for change count. This constant is defined here to avoid
     * a dependency on R7 of the framework.
     * The value of the property is of type {@code Long}.
     */
    private static final String PROP_CHANGECOUNT = "service.changecount";

    private static final String PROP_CHANGECOUNTDELAY = "org.apache.felix.http.whiteboard.changecount.delay";

    private volatile Hashtable<String, Object> attributes = new Hashtable<>();

    private final HandlerRegistry registry;
    private final WhiteboardManager contextManager;

    private volatile long changeCount;

    private volatile ServiceReference<HttpServiceRuntime> serviceReference;

    private volatile Timer timer;

    private final long updateChangeCountDelay;

    public HttpServiceRuntimeImpl(HandlerRegistry registry,
            WhiteboardManager contextManager,
            BundleContext bundleContext)
    {
        this.registry = registry;
        this.contextManager = contextManager;
        final Object val = bundleContext.getProperty(PROP_CHANGECOUNTDELAY);
        long value = 2000L;
        if ( val != null )
        {
        	try
        	{
        		value = Long.parseLong(val.toString());
        	}
        	catch ( final NumberFormatException nfe)
        	{
        		// ignore
        	}
        	if ( value < 1 )
        	{
        		value = 0L;
        	}
        }
    	updateChangeCountDelay = value;
    }

    @Override
    public RuntimeDTO getRuntimeDTO()
    {
        final RuntimeDTOBuilder runtimeDTOBuilder = new RuntimeDTOBuilder(contextManager.getRuntimeInfo(),
                this.serviceReference);
        return runtimeDTOBuilder.build();
    }

    @Override
    public RequestInfoDTO calculateRequestInfoDTO(final String path)
    {
        return new RequestInfoDTOBuilder(registry, path).build();
    }

    public synchronized void setAttribute(String name, Object value)
    {
        Hashtable<String, Object> newAttributes = new Hashtable<>(attributes);
        newAttributes.put(name, value);
        attributes = newAttributes;
    }

    public synchronized void setAllAttributes(Dictionary<String, Object> newAttributes)
    {
        Hashtable<String, Object> replacement = new Hashtable<>();
        for (String key : list(newAttributes.keys()))
        {
            replacement.put(key, newAttributes.get(key));
        }
        replacement.put(PROP_CHANGECOUNT, this.changeCount);
        attributes = replacement;
    }

    public Dictionary<String, Object> getAttributes()
    {
        return attributes;
    }

    public void setServiceReference(
            final ServiceReference<HttpServiceRuntime> reference)
    {
        this.serviceReference = reference;
    }

    public void updateChangeCount(final ServiceRegistration<HttpServiceRuntime> reg)
    {
        if ( reg != null )
        {
            boolean setProps = false;
            final long count;
            synchronized ( this )
            {
                this.changeCount++;
                count = this.changeCount;
                this.setAttribute(PROP_CHANGECOUNT, this.changeCount);
                if ( this.updateChangeCountDelay > 0 && this.timer == null )
                {
                    this.timer = new Timer();
                }
                if ( this.updateChangeCountDelay == 0L )
                {
                    setProps = true;
                }
                else
                {
                    timer.schedule(new TimerTask()
                    {

                        @Override
                        public void run()
                        {
                            synchronized ( HttpServiceRuntimeImpl.this )
                            {
                                if ( changeCount == count )
                                {
                                    try
                                    {
                                        reg.setProperties(getAttributes());
                                    }
                                    catch ( final IllegalStateException ise)
                                    {
                                        // we ignore this as this might happen on shutdown
                                    }
                                    timer.cancel();
                                    timer = null;
                                }
                            }
                        }
                    }, this.updateChangeCountDelay);
                }
            }
            if ( setProps ) {
                reg.setProperties(getAttributes());
            }
        }
    }
}

@@ -4570,16 +4570,24 @@
 rops
+Directly
  = false
 ;%0A  
@@ -4586,38 +4586,8 @@
 alse
-;%0A            final long count
 ;%0A  
@@ -4684,16 +4684,27 @@
         
+final long 
 count = 
@@ -4845,33 +4845,13 @@
 lay 
-%3E 0 && this.timer == null
+%3C= 0L
  )%0A 
@@ -4891,32 +4891,31 @@
     
-this.timer = new Timer()
+setPropsDirectly = true
 ;%0A  
@@ -4950,48 +4950,12 @@
     
-if ( this.updateChangeCountDelay == 0L )
+else
 %0A   
@@ -4993,24 +4993,33 @@
     
-setProps = true;
+if ( this.timer == null )
 %0A   
@@ -5023,34 +5023,46 @@
                 
-%7D%0A
+    %7B%0A        
                 
@@ -5053,36 +5053,57 @@
                 
-else
+this.timer = new Timer();
 %0A               
@@ -5095,33 +5095,37 @@
                 
-%7B
+    %7D
 %0A               
@@ -6224,64 +6224,308 @@
 rops
- ) %7B%0A                reg.setProperties(getAttributes());
+Directly )%0A            %7B%0A                try%0A                %7B%0A                    reg.setProperties(getAttributes());%0A                %7D%0A                catch ( final IllegalStateException ise)%0A                %7B%0A                    // we ignore this as this might happen on shutdown%0A                %7D
 %0A   

Update change count handling