// Copyright 2019 The Pigweed Authors
//
// 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
//
//	https://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 main

import (
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"

	"google.golang.org/protobuf/encoding/prototext"
	"pigweed/pw_target_runner"

	pb "pigweed/proto/pw_target_runner/exec_server_config_pb"
)

// ServerOptions contains command-line options for the server.
type ServerOptions struct {
	// Path to a server configuration file.
	config string

	// Port on which to run.
	port int
}

// configureServerFromFile sets up the server with workers specified in a
// config file. The file contains a pw.target_runner.ServerConfig protobuf
// message in canonical protobuf text format.
func configureServerFromFile(s *pw_target_runner.Server, filepath string) error {
	content, err := ioutil.ReadFile(filepath)
	if err != nil {
		return err
	}

	var config pb.ServerConfig
	if err := prototext.Unmarshal(content, &config); err != nil {
		return err
	}

	log.Printf("Parsed server configuration from %s\n", filepath)

	runners := config.GetRunner()
	if runners == nil {
		return nil
	}

	// Create an exec worker for each of the runner messages listed in the
	// config and register them with the server.
	for i, runner := range runners {
		// Build the complete command for the worker from its "command"
		// and "args" fields in the proto message. The command is
		// required; arguments are optional.
		cmd := []string{runner.GetCommand()}
		if cmd[0] == "" {
			msg := fmt.Sprintf(
				"ServerConfig.runner[%d] does not specify a command; skipping\n", i)
			return errors.New(msg)
		}

		if args := runner.GetArgs(); args != nil {
			cmd = append(cmd, args...)
		}

		worker := pw_target_runner.NewExecDeviceRunner(i, cmd)
		s.RegisterWorker(worker)

		log.Printf(
			"Registered ExecDeviceRunner %s with args %v\n",
			cmd[0],
			cmd[1:])
	}

	return nil
}

func main() {
	configPtr := flag.String("config", "", "Path to server configuration file")
	portPtr := flag.Int("port", 8080, "Server port")

	flag.Parse()

	server := pw_target_runner.NewServer()

	if *configPtr != "" {
		if err := configureServerFromFile(server, *configPtr); err != nil {
			log.Fatalf("Failed to parse config file %s: %v", *configPtr, err)
		}
	}

	if err := server.Bind(*portPtr); err != nil {
		log.Fatal(err)
	}

	if err := server.Serve(); err != nil {
		log.Fatalf("Failed to start server: %v", err)
	}
}
