//
//  rhimportd
//
//  The Radio Helsinki Rivendell Import Daemon
//
//
//  Copyright (C) 2015-2016 Christian Pointner <equinox@helsinki.at>
//
//  This file is part of rhimportd.
//
//  rhimportd 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.
//
//  rhimportd 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 rhimportd. If not, see <http://www.gnu.org/licenses/>.
//

package main

import (
	"code.helsinki.at/rhrd-go/rddb"
	"code.helsinki.at/rhrd-go/rhimport"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"sync"
	"time"
)

var (
	rhl  = log.New(os.Stderr, "[rhimportd]\t", log.LstdFlags)
	rhdl = log.New(ioutil.Discard, "[rhimportd-dbg]\t", log.LstdFlags)
)

func init() {
	if _, exists := os.LookupEnv("RHIMPORTD_DEBUG"); exists {
		rhdl.SetOutput(os.Stderr)
	}
}

type envStringValue string

func newEnvStringValue(key, dflt string) *envStringValue {
	if envval, exists := os.LookupEnv(key); exists {
		return (*envStringValue)(&envval)
	} else {
		return (*envStringValue)(&dflt)
	}
}

func (s *envStringValue) Set(val string) error {
	*s = envStringValue(val)
	return nil
}

func (s *envStringValue) Get() interface{} { return string(*s) }

func (s *envStringValue) String() string { return fmt.Sprintf("%s", *s) }

type envDurationValue time.Duration

func newEnvDurationValue(key string, dflt time.Duration) (*envDurationValue, error) {
	if envval, exists := os.LookupEnv(key); exists {
		var val envDurationValue
		err := (&val).Set(envval)
		return &val, err
	} else {
		return (*envDurationValue)(&dflt), nil
	}
}

func (d *envDurationValue) Set(val string) error {
	dval, err := time.ParseDuration(val)
	*d = envDurationValue(dval)
	return err
}

func (d *envDurationValue) Get() interface{} { return time.Duration(*d) }

func (d *envDurationValue) String() string { return fmt.Sprintf("%v", *d) }

func main() {
	webAddr := newEnvStringValue("RHIMPORTD_WEB_ADDR", "localhost:4080")
	flag.Var(webAddr, "web-addr", "addr:port to listen on (environment: RHIMPORTD_WEB_ADDR)")
	webStaticDir := newEnvStringValue("RHIMPORTD_WEB_STATIC_DIR", "/usr/share/rhimportd/web-static/")
	flag.Var(webStaticDir, "web-static-dir", "path to static html files (environment: RHIMPORTD_WEB_STATIC_DIR)")
	telnetAddr := newEnvStringValue("RHIMPORTD_TELNET_ADDR", "localhost:4023")
	flag.Var(telnetAddr, "telnet-addr", "addr:port to listen on (environment: RHIMPORTD_TELNET_ADDR)")
	watchDir := newEnvStringValue("RHIMPORTD_WATCH_DIR", "")
	flag.Var(watchDir, "watch-dir", "directory to look for file based requests (environment: RHIMPORTD_WATCH_DIR)")
	rdconf := newEnvStringValue("RHIMPORTD_RD_CONF", "/etc/rd.conf")
	flag.Var(rdconf, "rdconf", "path to the Rivendell config file (environment: RHIMPORTD_RD_CONF)")
	rdxportUrl := newEnvStringValue("RHIMPORTD_RDXPORT_URL", "http://localhost/rd-bin/rdxport.cgi")
	flag.Var(rdxportUrl, "rdxport-url", "the url to the Rivendell web-api (environment: RHIMPORTD_RDXPORT_URL)")
	tempDir := newEnvStringValue("RHIMPORTD_TEMP_DIR", os.TempDir())
	flag.Var(tempDir, "tmp-dir", "path to temporary files (environment: RHIMPORTD_TEMP_DIR)")
	localFetchDir := newEnvStringValue("RHIMPORTD_LOCAL_FETCH_DIR", os.TempDir())
	flag.Var(localFetchDir, "local-fetch-dir", "base path for local:// urls (environment: RHIMPORTD_LOCAL_FETCH_DIR)")

	uploadMaxAge, err := newEnvDurationValue("RHIMPORTD_UPLOAD_MAX_AGE", 30*time.Minute)
	if err != nil {
		rhl.Println("Error parsing RHIMPORTD_UPLOAD_MAX_AGE from environment:", err)
		return
	}
	flag.Var(uploadMaxAge, "upload-max-age", "maximum age of uploaded files before the get deleted (environment: RHIMPORTD_UPLOAD_MAX_AGE)")

	help := flag.Bool("help", false, "show usage")

	flag.Parse()
	if *help {
		flag.Usage()
		return
	}

	conf := rhimport.NewConfig(rdxportUrl.Get().(string), tempDir.Get().(string), localFetchDir.Get().(string))

	db, err := rddb.NewDB(rdconf.Get().(string))
	if err != nil {
		rhl.Println("Error initializing Rivdenll DB:", err)
		return
	}
	defer db.Cleanup()

	sessions, err := rhimport.NewSessionStore(conf, db.GetInterface())
	if err != nil {
		rhl.Println("Error initializing Session Store:", err)
		return
	}
	defer sessions.Cleanup()

	var wg sync.WaitGroup

	if webAddr.Get().(string) != "" {
		wg.Add(1)
		go func() {
			defer wg.Done()
			rhl.Println("starting web-ctrl")
			StartControlWeb(webAddr.Get().(string), webStaticDir.Get().(string), uploadMaxAge.Get().(time.Duration), conf, db.GetInterface(), sessions.GetInterface())
			rhl.Println("web-ctrl finished")
		}()
	}

	if telnetAddr.Get().(string) != "" {
		wg.Add(1)
		go func() {
			defer wg.Done()
			rhl.Println("starting telnet-ctrl")
			StartControlTelnet(telnetAddr.Get().(string), conf, db.GetInterface(), sessions.GetInterface())
			rhl.Println("telnet-ctrl finished")
		}()
	}

	if watchDir.Get().(string) != "" {
		wg.Add(1)
		go func() {
			defer wg.Done()
			rhl.Println("starting watch-dir-ctrl")
			StartWatchDir(watchDir.Get().(string), conf, db.GetInterface())
			rhl.Println("watch-dir-ctrl finished")
		}()
	}

	alldone := make(chan bool)
	go func() {
		defer func() { alldone <- true }()
		wg.Wait()
	}()

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)

	select {
	case <-c:
		rhl.Println("received interrupt, shutdown")
		return
	case <-alldone:
		return
	}
}