/*
 * Copyright 2010 Ning, Inc. 
 * 
 * Ning 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 com.ning.metrics.goodwill.endpoint; 
 
import com.google.inject.Inject; 
import com.ning.metrics.goodwill.access.GoodwillSchema; 
import com.ning.metrics.goodwill.binder.config.GoodwillConfig; 
import com.ning.metrics.goodwill.modules.ThriftRegistrar; 
import com.ning.metrics.goodwill.sink.GoodwillSink; 
import com.ning.metrics.goodwill.store.GoodwillStore; 
import com.sun.jersey.api.view.Viewable; 
import org.apache.log4j.Logger; 
 
import javax.annotation.Nullable; 
import javax.ws.rs.Consumes; 
import javax.ws.rs.DELETE; 
import javax.ws.rs.GET; 
import javax.ws.rs.POST; 
import javax.ws.rs.PUT; 
import javax.ws.rs.Path; 
import javax.ws.rs.PathParam; 
import javax.ws.rs.Produces; 
import javax.ws.rs.core.MediaType; 
import javax.ws.rs.core.Response; 
import java.io.IOException; 
 
@Path("registrar"
public class Registrar 
    private Logger log = Logger.getLogger(Registrar.class); 
 
    private GoodwillStore store; 
    private final GoodwillSink sink; 
    private final GoodwillConfig config; 
 
    @Inject 
    public Registrar
        GoodwillConfig config, 
        GoodwillStore store, 
        @Nullable GoodwillSink sink 
    ) 
    { 
        this.config = config; 
        this.store = store; 
        this.sink = sink; 
 
        if (sink != null) { 
            this.store.setSink(sink); 
        } 
    } 
 
    /*
     * UI 
     */
 
 
    @GET 
    @Produces(MediaType.TEXT_HTML) 
    public Viewable getAll() throws IOException 
    { 
        ThriftRegistrar registrar = new ThriftRegistrar(store.toJSON()); 
        registrar.setActionCoreURL(config.getActionCoreURL()); 
 
        return new Viewable("/registrar/type.jsp", registrar); 
    } 
 
    /*
     * REST API 
     */
 
 
    @GET 
    @Produces("application/json"
    public Response getAllJson() throws IOException 
    { 
        return Response.ok(store.toJSON().toString()).type(MediaType.APPLICATION_JSON_TYPE).build(); 
    } 
 
    @GET 
    @Produces("application/json"
    @Path("/{type}/"
    public Response getTypeJson(@PathParam("type") String typeName) throws IOException 
    { 
        if (typeName == null) { 
            return Response.ok(store.toJSON().toString()).type(MediaType.APPLICATION_JSON_TYPE).build(); 
        } 
        else { 
            GoodwillSchema typeFound = store.findByName(typeName); 
            if (typeFound != null) { 
                return Response.ok(typeFound.toJSON().toString()).type(MediaType.APPLICATION_JSON_TYPE).build(); 
            } 
        } 
 
        return Response.status(Response.Status.NOT_FOUND).build(); 
    } 
 
    @DELETE 
    @Path("/{type}/"
    public Response deleteType(@PathParam("type") String typeName) throws IOException 
    { 
        if (!config.allowDeleteEvent()) { 
            return Response.status(Response.Status.FORBIDDEN).build(); 
        } 
 
        if (typeName == null) { 
            return Response.status(Response.Status.BAD_REQUEST).build(); 
        } 
        else { 
            GoodwillSchema typeFound = store.findByName(typeName); 
            if (typeFound != null) { 
                if (store.deleteType(typeFound)) { 
                    return Response.noContent().build(); 
                } 
                else { 
                    return Response.status(Response.Status.SERVICE_UNAVAILABLE).build(); 
                } 
            } 
            else { 
                // Don't! The condition is not necessarily permanent! 
                //return Response.status(Response.Status.GONE) 
                return Response.status(Response.Status.NOT_FOUND).build(); 
            } 
        } 
    } 
 
    @POST 
    @Consumes("application/json"
    public Response post(String jsonThriftTypeString) 
    { 
        try { 
            GoodwillSchema schema = GoodwillSchema.decode(jsonThriftTypeString); 
            // Use PUT to update a Schema 
            if (store.findByName(schema.getName()) != null) { 
                return Response.status(Response.Status.CONFLICT).build(); 
            } 
 
            store.addType(schema); 
            log.info(String.format("Created new ThriftType <%s> from JSON <%s>", schema.toString(), jsonThriftTypeString)); 
        } 
        catch (IOException e) { 
            log.warn(String.format("Malformatted JSON: %s (%s)", jsonThriftTypeString, e)); 
            return Response.status(Response.Status.BAD_REQUEST).build(); 
        } 
 
        return Response.status(Response.Status.CREATED).build(); 
    } 
 
    @PUT 
    @Consumes("application/json"
    public Response put(String jsonThriftTypeString) 
    { 
        try { 
            GoodwillSchema thriftType = GoodwillSchema.decode(jsonThriftTypeString); 
            store.updateType(thriftType); 
            log.info(String.format("Updated ThriftType <%s> from JSON <%s>", thriftType.toString(), jsonThriftTypeString)); 
        } 
        catch (IOException e) { 
            log.warn(String.format("Malformatted JSON: %s", jsonThriftTypeString)); 
            return Response.status(Response.Status.BAD_REQUEST).build(); 
        } 
 
        return Response.status(Response.Status.ACCEPTED).build(); 
    } 
}