//
//  tank
//
//  Import and Playlist Daemon for autoradio project
//
//
//  Copyright (C) 2017 Christian Pointner <equinox@helsinki.at>
//
//  This file is part of tank.
//
//  tank is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  any later version.
//
//  tank is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with tank. If not, see <http://www.gnu.org/licenses/>.
//

package v1

import (
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/gorilla/mux"
	"gitlab.servus.at/autoradio/tank/importer"
	"gitlab.servus.at/autoradio/tank/store"
)

var (
	apiListing = APIListing{make(map[string]*APIEndpoint)}
)

// common
func sendWebResponse(w http.ResponseWriter, status int, respdata interface{}) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	json.NewEncoder(w).Encode(respdata) // TODO: Error Handling?
}

func notFound(w http.ResponseWriter, r *http.Request) {
	sendWebResponse(w, http.StatusNotFound, ErrorResponse{"not found"})
}

func methodNotAllowed(w http.ResponseWriter, r *http.Request) {
	sendWebResponse(w, http.StatusMethodNotAllowed, ErrorResponse{fmt.Sprintf("method %s is not allowed here", r.Method)})
}

func listEndpoints(w http.ResponseWriter, r *http.Request) {
	sendWebResponse(w, http.StatusOK, apiListing)
}

// Groups
func listGroups(st *store.Store, im *importer.SessionStore, w http.ResponseWriter, r *http.Request) {
	sendWebResponse(w, http.StatusNotImplemented, ErrorResponse{"listing groups not yet implemented"})
}

// Imports
func listImportsOfGroup(st *store.Store, im *importer.SessionStore, w http.ResponseWriter, r *http.Request) {
	sendWebResponse(w, http.StatusNotImplemented, ErrorResponse{"listing imports of group not yet implemented"})
}

func createImportForGroup(st *store.Store, im *importer.SessionStore, w http.ResponseWriter, r *http.Request) {
	sendWebResponse(w, http.StatusNotImplemented, ErrorResponse{"creating imports in group not yet implemented"})
}

func readImportOfGroup(st *store.Store, im *importer.SessionStore, w http.ResponseWriter, r *http.Request) {
	sendWebResponse(w, http.StatusNotImplemented, ErrorResponse{"reading imports of group not yet implemented"})
}

func uploadFileToImportOfGroup(st *store.Store, im *importer.SessionStore, w http.ResponseWriter, r *http.Request) {
	sendWebResponse(w, http.StatusNotImplemented, ErrorResponse{"uploading files to imports of group not yet implemented"})
}

func deleteImportOfGroup(st *store.Store, im *importer.SessionStore, w http.ResponseWriter, r *http.Request) {
	sendWebResponse(w, http.StatusNotImplemented, ErrorResponse{"deleting imports from group not yet implemented"})
}

// Files
func listFilesOfGroup(st *store.Store, im *importer.SessionStore, w http.ResponseWriter, r *http.Request) {
	sendWebResponse(w, http.StatusNotImplemented, ErrorResponse{"listing files of group not yet implemented"})
}

func readFileOfGroup(st *store.Store, im *importer.SessionStore, w http.ResponseWriter, r *http.Request) {
	sendWebResponse(w, http.StatusNotImplemented, ErrorResponse{"reading file of group not yet implemented"})
}

func updateFileOfGroup(st *store.Store, im *importer.SessionStore, w http.ResponseWriter, r *http.Request) {
	sendWebResponse(w, http.StatusNotImplemented, ErrorResponse{"updateing file of group not yet implemented"})
}

func deleteFileOfGroup(st *store.Store, im *importer.SessionStore, w http.ResponseWriter, r *http.Request) {
	sendWebResponse(w, http.StatusNotImplemented, ErrorResponse{"deleting file from group not yet implemented"})
}

// Playlists
func listPlaylistsOfGroup(st *store.Store, im *importer.SessionStore, w http.ResponseWriter, r *http.Request) {
	sendWebResponse(w, http.StatusNotImplemented, ErrorResponse{"listing playlists of group not yet implemented"})
}

func createPlaylistForGroup(st *store.Store, im *importer.SessionStore, w http.ResponseWriter, r *http.Request) {
	sendWebResponse(w, http.StatusNotImplemented, ErrorResponse{"creating playlists in group not yet implemented"})
}

func readPlaylistOfGroup(st *store.Store, im *importer.SessionStore, w http.ResponseWriter, r *http.Request) {
	sendWebResponse(w, http.StatusNotImplemented, ErrorResponse{"reading playlist of group not yet implemented"})
}

func updatePlaylistOfGroup(st *store.Store, im *importer.SessionStore, w http.ResponseWriter, r *http.Request) {
	sendWebResponse(w, http.StatusNotImplemented, ErrorResponse{"updateing playlist of group not yet implemented"})
}

func deletePlaylistOfGroup(st *store.Store, im *importer.SessionStore, w http.ResponseWriter, r *http.Request) {
	sendWebResponse(w, http.StatusNotImplemented, ErrorResponse{"deleting playlist from group not yet implemented"})
}

type apiHandlerFunc func(*store.Store, *importer.SessionStore, http.ResponseWriter, *http.Request)

type apiHandler struct {
	store    *store.Store
	importer *importer.SessionStore
	H        apiHandlerFunc
}

func (self apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	self.H(self.store, self.importer, w, r)
}

func InstallHandler(r *mux.Router, st *store.Store, im *importer.SessionStore) {
	r.NotFoundHandler = http.HandlerFunc(notFound)
	r.MethodNotAllowedHandler = http.HandlerFunc(methodNotAllowed)

	// Groups
	r.Handle("/groups", apiHandler{st, im, listGroups}).Methods("GET")
	r.HandleFunc("/groups", methodNotAllowed)

	// Imports
	r.Handle("/groups/{group-id}/imports", apiHandler{st, im, listImportsOfGroup}).Methods("GET")
	r.Handle("/groups/{group-id}/imports", apiHandler{st, im, createImportForGroup}).Methods("POST")
	r.HandleFunc("/groups/{group-id}/imports", methodNotAllowed)

	r.Handle("/groups/{group-id}/imports/{import-id}", apiHandler{st, im, readImportOfGroup}).Methods("GET")
	r.Handle("/groups/{group-id}/imports/{import-id}", apiHandler{st, im, uploadFileToImportOfGroup}).Methods("PUT")
	r.Handle("/groups/{group-id}/imports/{import-id}", apiHandler{st, im, deleteImportOfGroup}).Methods("DELETE")
	r.HandleFunc("/groups/{group-id}/imports/{import-id}", methodNotAllowed)

	// Files
	r.Handle("/groups/{group-id}/files", apiHandler{st, im, listFilesOfGroup}).Methods("GET")
	r.HandleFunc("/groups/{group-id}/files", methodNotAllowed)

	r.Handle("/groups/{group-id}/files/{file-id}", apiHandler{st, im, readFileOfGroup}).Methods("GET")
	r.Handle("/groups/{group-id}/files/{file-id}", apiHandler{st, im, updateFileOfGroup}).Methods("PUT")
	r.Handle("/groups/{group-id}/files/{file-id}", apiHandler{st, im, deleteFileOfGroup}).Methods("DELETE")
	r.HandleFunc("/groups/{group-id}/files/{file-id}", methodNotAllowed)

	// Playlists
	r.Handle("/groups/{group-id}/playlists", apiHandler{st, im, listPlaylistsOfGroup}).Methods("GET")
	r.Handle("/groups/{group-id}/playlists", apiHandler{st, im, createPlaylistForGroup}).Methods("POST")
	r.HandleFunc("/groups/{group-id}/playlists", methodNotAllowed)

	r.Handle("/groups/{group-id}/playlists/{playlist-id}", apiHandler{st, im, readPlaylistOfGroup}).Methods("GET")
	r.Handle("/groups/{group-id}/playlists/{playlist-id}", apiHandler{st, im, updatePlaylistOfGroup}).Methods("PUT")
	r.Handle("/groups/{group-id}/playlists/{playlist-id}", apiHandler{st, im, deletePlaylistOfGroup}).Methods("DELETE")
	r.HandleFunc("/groups/{group-id}/playlists/{playlist-id}", methodNotAllowed)

	// Index
	r.HandleFunc("/", listEndpoints).Methods("GET")
	r.HandleFunc("/", methodNotAllowed)

	r.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
		p, err := route.GetPathTemplate()
		if err != nil {
			return nil
		}

		m, err := route.GetMethods()
		if err != nil || len(m) == 0 {
			return err
		}

		if e, exists := apiListing.Endpoints[p]; exists {
			e.Methods = append(e.Methods, m...)
		} else {
			apiListing.Endpoints[p] = &APIEndpoint{m}
		}
		return nil
	})
}