package api


import (
	"flag"
	"fmt"
	"os"
	"log"
	"time"

	"gopkg.in/yaml.v2"
)



type SoftwareConfiguration struct {
	configFile string
	// Execution environment
	// If not psecified or recognised then assume product
	// for safey.  Vales are:
	//  - production
	//  - staging
	//  - testing
	//  - development
	Environment string `yaml:"environment"`
    Server struct {
        // IP Address to bind the HTTP Server to
        Host string `yaml:"host"`
        // TCP Port to bind the HTTP Server to
        Port    int `yaml:"port"`
        // Service base path
        BasePath    string `yaml:"base_path"`
		// Timeout vaues
		Timeout struct {
            // Server is the general server timeout to use
            // for graceful shutdowns
            Server time.Duration `yaml:"server"`

            // Write is the amount of time to wait until an HTTP server
            // write opperation is cancelled
            Write time.Duration `yaml:"write"`

            // Read is the amount of time to wait until an HTTP server
            // read operation is cancelled
            Read time.Duration `yaml:"read"`

            // Read is the amount of time to wait
            // until an IDLE HTTP session is closed
            Idle time.Duration `yaml:"idle"`
        } `yaml:"timeout"`
    } `yaml:"server"`
	Folders struct {
		// Data folder
		Data string `yaml:"data"`
		// Static HTLM, CSS folder
		Static string `yaml:"static"`
		// Configuration folder
		Config string `yaml:"config"`
	} `yaml:"folders"`
	DataSource struct {
		// Load products on start up
		Load bool `yaml:"load"`
		// Overwrte existing product data
		Overwrite bool `yaml:"overwrite"`
	} `yaml:"dataSource"`
	Api struct {
		// Deafult page size for lists
		PageSize int `yaml:"page_size"`
	} `yaml:"api"`
	Hosts struct {
		// Product protocol, host name and port, e.g.http://localhost:8075
		Product string `yaml:"product"`
		// Portfolio, account protocol, host name and port, e.g.http://localhost:8076
		Protfolio string `yaml:"portfolio"`
		// Quote protocol, host name and port, e.g.http://localhost:8077
		Quote string `yaml:"quote"`
		// Order managemet protocol, host name and port, e.g.http://localhost:8077
		Order string `yaml:"order"`
		// Search protocol, host name and port, e.g.http://localhost:8075
		Search string `yaml:"search"`
	} `yaml:"hosts"`
	Support struct {
		// Message verbose level, 1=minimum message, 5=All messages
		Level int `yaml:"level"`
	} `yaml:"support"`
}

func NewSoftwareConfiguration() *SoftwareConfiguration {

	var sc = SoftwareConfiguration{}
	
	sc.Environment = "production"
	sc.Server.BasePath = "/"

	sc.DataSource.Load = true
	sc.DataSource.Overwrite = false

	sc.Folders.Static = "./static"
	sc.Folders.Data = "./data"
	sc.Folders.Config = "./config"

	sc.Support.Level= 1

	sc.Api.PageSize = 20
	
	return &sc
}

func (self SoftwareConfiguration) GetConfigurationFile() string {
	return self.configFile
}

func LoadConfig(configPath string) (*SoftwareConfiguration, error) {
	
    config := &SoftwareConfiguration{}

    file, err := os.Open(configPath)
    if err != nil {
        return nil, err
    }
    defer file.Close()

    d := yaml.NewDecoder(file)

    if err := d.Decode(&config); err != nil {
        return nil, err
    }

	config.configFile = configPath

	if (config.Environment == "") {
		config.Environment = "production"
	}

	// Perform some validation
	if (config.Environment != "production" &&
		config.Environment != "staging" &&
		config.Environment != "testing" &&
		config.Environment != "development") {
			log.Printf("Environment value '%s' not valid.  Reverting to defauult", config.Environment)			
			config.Environment = "production"
	}

	if (config.Api.PageSize < 1) {
		config.Api.PageSize = 20
	}

    return config, nil
} 

func ParseFlags(configPath string) (*SoftwareConfiguration, error) {
    
    flag.StringVar(&configPath, "config", configPath, "path to config file")
	var port = flag.Int("port", 0, "Port for HTTP server micro service")
	var dataFolder = flag.String("data", "", "Data folder")
	var staticFolder = flag.String("static", "", "Static folder")

    flag.Parse()

	fileStat, err := os.Stat(configPath)
    if err != nil {
        return nil, err
    }
    if fileStat.IsDir() {
        return nil, fmt.Errorf("'%s' is a directory, not a normal file", configPath)
    }
	
	config, err :=  LoadConfig(configPath)
    if err != nil {
        return nil, err
    }

	if (*port > 0) {
		config.Server.Port = *port
	}
	if (*dataFolder != "") {
		config.Folders.Data = *dataFolder
	}
	if (*staticFolder != "") {
		config.Folders.Static = *staticFolder
	}

    return config, nil
}


