package org.inb.biomoby.service.resolver;

import org.inb.biomoby.service.*;
import com.sun.istack.NotNull;
import com.sun.xml.ws.api.message.Header;
import com.sun.xml.ws.api.message.HeaderList;
import com.sun.xml.ws.api.message.Packet;
import com.sun.xml.ws.api.server.WSEndpoint;
import com.sun.xml.ws.api.server.WSWebServiceContext;
import com.sun.xml.ws.server.AbstractMultiInstanceResolver;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.xml.namespace.QName;
import org.inb.biomoby.shared.wsrf.MobyEndpointReference;

/**
 * @author Dmitry Rechevsky
 */

public class WSRFResourceInstanceResolver<T> extends AbstractMultiInstanceResolver<T>
{
    private static long count;

    private WSWebServiceContext wsContext;
    private Map<String, T> endpoints;

    public WSRFResourceInstanceResolver(@NotNull Class<T> clazz)
    {
        super(clazz);
        
        endpoints = new ConcurrentHashMap<String, T>();
    }

    public @NotNull T resolve(Packet request)
    {
        System.out.println("[WSRFResourceInstanceResolver].resolve()");
        
        T o;

        WSRFOperation operation = WSRFResourceInstanceResolver.getOperation(request);

        QName serviceInvocationId = new QName(IAsyncMobyService.MOBY_NAMESPACE, MobyEndpointReference.REF_PARAM_NAME);
        HeaderList headers = request.getMessage().getHeaders();

        Header header = headers.get(serviceInvocationId, true);

        String invocationId;
        if (header != null)
        {
            invocationId = header.getStringContent();
            o = endpoints.get(invocationId);

            if (o == null)
            {
                o = create();
            }
        }
        else
        {
            o = create();

            if (o instanceof AbstractAsyncMobyService)
            {
               AbstractAsyncMobyService endpoint = (AbstractAsyncMobyService)o;

               invocationId = endpoint.getServiceInvocationId();

               if (invocationId == null)
               {
                   invocationId = String.valueOf(++count);
               }
               
               // inject invocationId
               endpoint.setInvocationId(invocationId);
            }
            else
            {
                invocationId = String.valueOf(++count);
            }
            
            if (operation == WSRFOperation.Submit)
            {
                endpoints.put(invocationId, o);
            }
            else if (operation != WSRFOperation.Call)
            { // we return a new endpoint instance, but do not make it stateful
                System.out.println("WARNING: intention to create a new resource without previouse submit!!!");
            }
        }
        
        return o;
    }

    @Override
    public void start(@NotNull WSWebServiceContext wsContext, @NotNull WSEndpoint endpoint)
    {
        super.start(wsContext, endpoint);
        this.wsContext = wsContext;
    }
    
    private static WSRFOperation getOperation(Packet request)
    {
        if (request.soapAction == null)
        {
            return null;
        }
        
        String soapAction = request.soapAction.substring(1, request.soapAction.length() - 1);
        
        StringBuilder operation = new StringBuilder();
        operation.append(request.endpoint.getServiceName().getNamespaceURI());
        operation.append('#');
        operation.append(request.endpoint.getServiceName().getLocalPart());
        
        if (operation.toString().equals(soapAction))
        {
            return WSRFOperation.Call;
        }
        
        operation.append("_submit");

        if (operation.toString().equals(soapAction))
        {
            return WSRFOperation.Submit;
        }
        
        return null;
    }
    
    private static boolean isSubmit(Packet request)
    {
        // the soapAction could be not set (null) or can be less than minimum length, so no sense to compare...
        // note: "magic number" '2' is because the soapAction string is quoted.
        if (request.soapAction == null || 
            request.soapAction.length() <= IAsyncMobyService.MOBY_NAMESPACE.length() + "_submit".length() + 2)
        {
            return false;
        }
        
        return request.soapAction.startsWith(IAsyncMobyService.MOBY_NAMESPACE, 1) && request.soapAction.endsWith("_submit\"");
    }
}