package main

import (
	"net/http"

	"github.com/emicklei/go-restful"
	"github.com/emicklei/go-restful-swagger12"
	"google.golang.org/appengine"
	"google.golang.org/appengine/memcache"
)

// This example is functionally the same as ../restful-user-service.go
// but it`s supposed to run on Goole App Engine (GAE)
//
// contributed by ivanhawkes

type User struct {
	Id, Name string
}

type UserService struct {
	// normally one would use DAO (data access object)
	// but in this example we simple use memcache.
}

func (u UserService) Register() {
	ws := new(restful.WebService)

	ws.
		Path("/users").
		Consumes(restful.MIME_XML, restful.MIME_JSON).
		Produces(restful.MIME_JSON, restful.MIME_XML) // you can specify this per route as well

	ws.Route(ws.GET("/{user-id}").To(u.findUser).
		// docs
		Doc("get a user").
		Param(ws.PathParameter("user-id", "identifier of the user").DataType("string")).
		Writes(User{})) // on the response

	ws.Route(ws.PATCH("").To(u.updateUser).
		// docs
		Doc("update a user").
		Reads(User{})) // from the request

	ws.Route(ws.PUT("/{user-id}").To(u.createUser).
		// docs
		Doc("create a user").
		Param(ws.PathParameter("user-id", "identifier of the user").DataType("string")).
		Reads(User{})) // from the request

	ws.Route(ws.DELETE("/{user-id}").To(u.removeUser).
		// docs
		Doc("delete a user").
		Param(ws.PathParameter("user-id", "identifier of the user").DataType("string")))

	restful.Add(ws)
}

// GET http://localhost:8080/users/1
//
func (u UserService) findUser(request *restful.Request, response *restful.Response) {
	c := appengine.NewContext(request.Request)
	id := request.PathParameter("user-id")
	usr := new(User)
	_, err := memcache.Gob.Get(c, id, &usr)
	if err != nil || len(usr.Id) == 0 {
		response.WriteErrorString(http.StatusNotFound, "User could not be found.")
	} else {
		response.WriteEntity(usr)
	}
}

// PATCH http://localhost:8080/users
// <User><Id>1</Id><Name>Melissa Raspberry</Name></User>
//
func (u *UserService) updateUser(request *restful.Request, response *restful.Response) {
	c := appengine.NewContext(request.Request)
	usr := new(User)
	err := request.ReadEntity(&usr)
	if err == nil {
		item := &memcache.Item{
			Key:    usr.Id,
			Object: &usr,
		}
		err = memcache.Gob.Set(c, item)
		if err != nil {
			response.WriteError(http.StatusInternalServerError, err)
			return
		}
		response.WriteEntity(usr)
	} else {
		response.WriteError(http.StatusInternalServerError, err)
	}
}

// PUT http://localhost:8080/users/1
// <User><Id>1</Id><Name>Melissa</Name></User>
//
func (u *UserService) createUser(request *restful.Request, response *restful.Response) {
	c := appengine.NewContext(request.Request)
	usr := User{Id: request.PathParameter("user-id")}
	err := request.ReadEntity(&usr)
	if err == nil {
		item := &memcache.Item{
			Key:    usr.Id,
			Object: &usr,
		}
		err = memcache.Gob.Add(c, item)
		if err != nil {
			response.WriteError(http.StatusInternalServerError, err)
			return
		}
		response.WriteHeader(http.StatusCreated)
		response.WriteEntity(usr)
	} else {
		response.WriteError(http.StatusInternalServerError, err)
	}
}

// DELETE http://localhost:8080/users/1
//
func (u *UserService) removeUser(request *restful.Request, response *restful.Response) {
	c := appengine.NewContext(request.Request)
	id := request.PathParameter("user-id")
	err := memcache.Delete(c, id)
	if err != nil {
		response.WriteError(http.StatusInternalServerError, err)
	}
}

func getGaeURL() string {
	if appengine.IsDevAppServer() {
		return "http://localhost:8080"
	} else {
		/**
		 * Include your URL on App Engine here.
		 * I found no way to get AppID without appengine.Context and this always
		 * based on a http.Request.
		 */
		return "http://<your_app_id>.appspot.com"
	}
}

func init() {
	u := UserService{}
	u.Register()

	// Optionally, you can install the Swagger Service which provides a nice Web UI on your REST API
	// You need to download the Swagger HTML5 assets and change the FilePath location in the config below.
	// Open <your_app_id>.appspot.com/apidocs and enter http://<your_app_id>.appspot.com/apidocs.json in the api input field.
	config := swagger.Config{
		WebServices:    restful.RegisteredWebServices(), // you control what services are visible
		WebServicesUrl: getGaeURL(),
		ApiPath:        "/apidocs.json",

		// Optionally, specify where the UI is located
		SwaggerPath: "/apidocs/",
		// GAE support static content which is configured in your app.yaml.
		// This example expect the swagger-ui in static/swagger so you should place it there :)
		SwaggerFilePath: "static/swagger"}
	swagger.InstallSwaggerService(config)
}
