/*
 *  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.polygene.library.rest.client.api;

import java.util.ArrayList;
import java.util.List;
import org.apache.polygene.api.common.Optional;
import org.apache.polygene.api.injection.scope.Structure;
import org.apache.polygene.api.injection.scope.Uses;
import org.apache.polygene.api.structure.Module;
import org.apache.polygene.library.rest.client.ClientCache;
import org.apache.polygene.library.rest.client.RequestWriterDelegator;
import org.apache.polygene.library.rest.client.ResponseReaderDelegator;
import org.apache.polygene.library.rest.client.spi.RequestWriter;
import org.apache.polygene.library.rest.client.spi.ResponseHandler;
import org.apache.polygene.library.rest.client.spi.ResponseReader;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.Uniform;
import org.restlet.data.ClientInfo;
import org.restlet.data.Form;
import org.restlet.data.Language;
import org.restlet.data.MediaType;
import org.restlet.data.Preference;
import org.restlet.data.Reference;

/**
 * Builder for UsecaseClient
 */
public class ContextResourceClientFactory
{
    @Structure
    private Module module;

    @Uses
    @Optional
    private ClientCache cache;

    @Uses
    private ResponseReaderDelegator readerDelegator;

    @Uses
    private RequestWriterDelegator requestWriterDelegator;

    @Uses
    private Uniform client;

    private ClientInfo info = new ClientInfo();
    private Form requestHeaders = new Form();

    private ResponseHandler errorHandler;

    public ContextResourceClient newClient( Reference reference )
    {
        ContextResourceClient contextResourceClient = module.newObject( ContextResourceClient.class, this, reference );
        contextResourceClient.onError( errorHandler );
        return contextResourceClient;
    }

    public void setAgent(String agent)
    {
        info.setAgent( agent );
    }

    public Form getCustomRequestHeaders()
    {
        return requestHeaders;
    }

    public void setAcceptedLanguages(Language... acceptedLanguages)
    {
        List<Preference<Language>> languages = new ArrayList<>();
        for( Language acceptedLanguage : acceptedLanguages )
        {
            languages.add( new Preference<>());
        }

        info.setAcceptedLanguages( languages );

    }

    public void setAcceptedMediaTypes(MediaType... acceptedMediaTypes)
    {
        List<Preference<MediaType>> mediatypes = new ArrayList<>();
        for( MediaType mediaType : acceptedMediaTypes )
        {
            mediatypes.add( new Preference<>( mediaType ) );
        }

        info.setAcceptedMediaTypes( mediatypes );
    }

    public ClientInfo getInfo()
    {
        return info;
    }

    public void setErrorHandler( ResponseHandler errorHandler )
    {
        this.errorHandler = errorHandler;
    }

    public ResponseHandler getErrorHandler()
    {
        return errorHandler;
    }

    // Internal
    void updateCommandRequest( Request request )
    {
        request.setClientInfo( info );

        // Update cache information
        if( cache != null )
        {
            cache.updateCommandConditions( request );
        }

        // Add all custom headers
        if (!requestHeaders.isEmpty())
            request.getAttributes().put( "org.restlet.http.headers", requestHeaders );
    }

    void updateQueryRequest( Request request )
    {
        request.setClientInfo( info );

        // Update cache information
        if( cache != null )
        {
            cache.updateQueryConditions( request );
        }

    }

    void updateCache( Response response )
    {
        if( cache != null )
        {
            cache.updateCache( response );
        }
    }

    Uniform getClient()
    {
        return client;
    }

    public <T> T readResponse( Response response, Class<T> queryResult )
    {
        return (T) readerDelegator.readResponse( response, queryResult );
    }

    public void writeRequest( Request request, Object queryRequest )
    {
        if( !requestWriterDelegator.writeRequest( queryRequest, request ) )
        {
            throw new IllegalArgumentException( "Illegal query request type:" + queryRequest.getClass().getName() );
        }
    }

    public void registerResponseReader(ResponseReader reader)
    {
        readerDelegator.registerResponseReader( reader );
    }

    public void registerRequestWriter(RequestWriter writer)
    {
        requestWriterDelegator.registerRequestWriter( writer );
    }
}
