package registry

import (
	"net/http"

	"github.com/docker/docker-registry/digest"
	"github.com/docker/docker-registry/storage"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
)

// layerDispatcher uses the request context to build a layerHandler.
func layerDispatcher(ctx *Context, r *http.Request) http.Handler {
	dgst, err := digest.ParseDigest(ctx.vars["digest"])

	if err != nil {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			ctx.Errors.Push(ErrorCodeInvalidDigest, err)
		})
	}

	layerHandler := &layerHandler{
		Context: ctx,
		Digest:  dgst,
	}

	layerHandler.log = layerHandler.log.WithField("digest", dgst)

	return handlers.MethodHandler{
		"GET":  http.HandlerFunc(layerHandler.GetLayer),
		"HEAD": http.HandlerFunc(layerHandler.GetLayer),
	}
}

// layerHandler serves http layer requests.
type layerHandler struct {
	*Context

	Digest digest.Digest
}

// GetLayer fetches the binary data from backend storage returns it in the
// response.
func (lh *layerHandler) GetLayer(w http.ResponseWriter, r *http.Request) {
	layers := lh.services.Layers()

	layer, err := layers.Fetch(lh.Name, lh.Digest)

	if err != nil {
		switch err {
		case storage.ErrLayerUnknown:
			w.WriteHeader(http.StatusNotFound)
			lh.Errors.Push(ErrorCodeUnknownLayer,
				map[string]interface{}{
					"unknown": storage.FSLayer{BlobSum: lh.Digest},
				})
			return
		default:
			lh.Errors.Push(ErrorCodeUnknown, err)
			return
		}
	}
	defer layer.Close()

	http.ServeContent(w, r, layer.Digest().String(), layer.CreatedAt(), layer)
}

func buildLayerURL(router *mux.Router, r *http.Request, layer storage.Layer) (string, error) {
	route := clonedRoute(router, routeNameBlob)

	layerURL, err := route.Schemes(r.URL.Scheme).Host(r.Host).
		URL("name", layer.Name(),
		"digest", layer.Digest().String())
	if err != nil {
		return "", err
	}

	return layerURL.String(), nil
}