/*
 * Copyright (C) 2018 "IoT.bzh"
 * Author Sebastien Douheret <sebastien@iot.bzh>
 *
 * Licensed 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 xdsserver

import (
	"net/http"

	common "gerrit.automotivelinux.org/gerrit/src/xds/xds-common.git/golib"
	"gerrit.automotivelinux.org/gerrit/src/xds/xds-server/lib/xsapiv1"
	"github.com/gin-gonic/gin"
)

/***
 * Targets
 ***/

// getTargets returns all targets configuration
func (s *APIService) getTargets(c *gin.Context) {
	c.JSON(http.StatusOK, s.targets.GetConfigArr())
}

// getTarget returns a specific target configuration
func (s *APIService) getTarget(c *gin.Context) {
	id, err := s.targets.ResolveID(c.Param("id"))
	if err != nil {
		common.APIError(c, err.Error())
		return
	}
	f := s.targets.Get(id)
	if f == nil {
		common.APIError(c, "Invalid id")
		return
	}

	c.JSON(http.StatusOK, (*f).GetConfig())
}

// addTarget adds a new target to server config
func (s *APIService) addTarget(c *gin.Context) {
	var cfgArg xsapiv1.TargetConfig
	if c.BindJSON(&cfgArg) != nil {
		common.APIError(c, "Invalid arguments")
		return
	}

	// Retrieve session info
	sess := s.sessions.Get(c)
	if sess == nil {
		common.APIError(c, "Unknown sessions")
		return
	}

	s.Log.Debugln("Add target config: ", cfgArg)

	newTgt, err := s.targets.Add(cfgArg, sess)
	if err != nil {
		common.APIError(c, err.Error())
		return
	}

	c.JSON(http.StatusOK, newTgt)
}

// delTarget deletes target from server config
func (s *APIService) delTarget(c *gin.Context) {
	id, err := s.targets.ResolveID(c.Param("id"))
	if err != nil {
		common.APIError(c, err.Error())
		return
	}

	// Retrieve session info
	sess := s.sessions.Get(c)
	if sess == nil {
		common.APIError(c, "Unknown sessions")
		return
	}

	s.Log.Debugln("Delete target id ", id)

	delEntry, err := s.targets.Delete(id, sess)
	if err != nil {
		common.APIError(c, err.Error())
		return
	}
	c.JSON(http.StatusOK, delEntry)
}

/***
 * Terminals
 ***/
// getTgtTerms Get list of all terminals
func (s *APIService) getTgtTerms(c *gin.Context) {
	id, err := s.targets.ResolveID(c.Param("id"))
	if err != nil {
		common.APIError(c, err.Error())
		return
	}

	res, err := s.targets.GetTerminalsArr(id)
	if err != nil {
		common.APIError(c, err.Error())
		return
	}

	c.JSON(http.StatusOK, res)
}

// getTgtTerm Get info a terminal
func (s *APIService) getTgtTerm(c *gin.Context) {
	id, tid, err := s._decodeTermArgs(c)
	if err != nil {
		common.APIError(c, err.Error())
		return
	}

	iTerm, err := s.targets.GetTerminal(id, tid)
	if err != nil {
		common.APIError(c, err.Error())
		return
	}

	c.JSON(http.StatusOK, (*iTerm).GetConfig())
}

// createTgtTerm Create a new terminal
func (s *APIService) createTgtTerm(c *gin.Context) {
	s.updateTgtTerm(c)
}

// updateTgtTerm Update terminal config
func (s *APIService) updateTgtTerm(c *gin.Context) {
	var cfgArg xsapiv1.TerminalConfig

	tgtID, termID, err := s._decodeTermArgs(c)
	if tgtID == "" && err != nil {
		common.APIError(c, err.Error())
		return
	}
	if err := c.BindJSON(&cfgArg); err != nil {
		common.APIError(c, "Invalid arguments")
		return
	}
	if cfgArg.ID == "" {
		cfgArg.ID = termID
	}
	if termID != "" && cfgArg.ID != termID {
		common.APIError(c, "Invalid arguments, inconsistent terminal id ")
		return
	}
	s.Log.Debugln("Add or Update terminal config: ", cfgArg)
	term, err := s.targets.CreateUpdateTerminal(tgtID, cfgArg, false)
	if err != nil {
		common.APIError(c, err.Error())
		return
	}
	c.JSON(http.StatusOK, term)
}

// delTgtTerm Delete a terminal
func (s *APIService) delTgtTerm(c *gin.Context) {

	tgtID, termID, err := s._decodeTermArgs(c)
	if err != nil {
		common.APIError(c, err.Error())
		return
	}
	term, err := s.targets.DeleteTerminal(tgtID, termID)
	if err != nil {
		common.APIError(c, err.Error())
		return
	}

	c.JSON(http.StatusOK, term)
}

// openTgtTerm Open a target terminal/console
func (s *APIService) openTgtTerm(c *gin.Context) {

	id, tid, err := s._decodeTermArgs(c)
	if err != nil {
		common.APIError(c, err.Error())
		return
	}

	// Retrieve session info
	sess := s.sessions.Get(c)
	if sess == nil {
		common.APIError(c, "Unknown sessions")
		return
	}

	term, err := s.targets.OpenTerminal(id, tid, sess)
	if err != nil {
		common.APIError(c, err.Error())
		return
	}
	c.JSON(http.StatusOK, term)
}

// closeTgtTerm Close a terminal
func (s *APIService) closeTgtTerm(c *gin.Context) {
	id, tid, err := s._decodeTermArgs(c)
	if err != nil {
		common.APIError(c, err.Error())
		return
	}

	// Retrieve session info
	sess := s.sessions.Get(c)
	if sess == nil {
		common.APIError(c, "Unknown sessions")
		return
	}

	term, err := s.targets.CloseTerminal(id, tid, sess)
	if err != nil {
		common.APIError(c, err.Error())
		return
	}
	c.JSON(http.StatusOK, term)
}

// resizeTgtTerm Resize a terminal
func (s *APIService) resizeTgtTerm(c *gin.Context) {
	var sizeArg xsapiv1.TerminalResizeArgs

	id, tid, err := s._decodeTermArgs(c)
	if err != nil {
		common.APIError(c, err.Error())
		return
	}
	if err := c.BindJSON(&sizeArg); err != nil {
		common.APIError(c, "Invalid arguments")
		return
	}

	// Retrieve session info
	sess := s.sessions.Get(c)
	if sess == nil {
		common.APIError(c, "Unknown sessions")
		return
	}

	term, err := s.targets.ResizeTerminal(id, tid, sizeArg.Cols, sizeArg.Rows, sess)
	if err != nil {
		common.APIError(c, err.Error())
		return
	}
	c.JSON(http.StatusOK, term)
}

// signalTgtTerm Send a signal to a terminal
func (s *APIService) signalTgtTerm(c *gin.Context) {
	var sigArg xsapiv1.TerminalSignalArgs

	id, tid, err := s._decodeTermArgs(c)
	if err != nil {
		common.APIError(c, err.Error())
		return
	}

	sigName := c.Param("sig")
	if sigName == "" {
		if err := c.BindJSON(&sigArg); err != nil {
			common.APIError(c, "Invalid arguments")
			return
		}
		sigName = sigArg.Signal
	}
	if sigName == "" {
		common.APIError(c, "Invalid arguments")
		return
	}

	if err := s.targets.SignalTerminal(id, tid, sigName); err != nil {
		common.APIError(c, err.Error())
		return
	}

	c.JSON(http.StatusOK, "")
}

// _decodeTermArgs Helper to decode arguments of Terminal routes
func (s *APIService) _decodeTermArgs(c *gin.Context) (string, string, error) {
	id, err := s.targets.ResolveID(c.Param("id"))
	if err != nil {
		return "", "", err
	}

	termID, err := s.targets.ResolveTerminalID(c.Param("tid"))
	if err != nil {
		return id, "", err
	}

	return id, termID, nil
}