// Copyright 2015 CoreOS, Inc.
//
// 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 capnslog

import (
	
	
	
)

// LogLevel is the set of all log levels.
type LogLevel int8

const (
	// CRITICAL is the lowest log level; only errors which will end the program will be propagated.
	CRITICAL LogLevel = iota - 1
	// ERROR is for errors that are not fatal but lead to troubling behavior.
	ERROR
	// WARNING is for errors which are not fatal and not errors, but are unusual. Often sourced from misconfigurations.
	WARNING
	// NOTICE is for normal but significant conditions.
	NOTICE
	// INFO is a log level for common, everyday log updates.
	INFO
	// DEBUG is the default hidden level for more verbose updates about internal processes.
	DEBUG
	// TRACE is for (potentially) call by call tracing of programs.
	TRACE
)

// Char returns a single-character representation of the log level.
func ( LogLevel) () string {
	switch  {
	case CRITICAL:
		return "C"
	case ERROR:
		return "E"
	case WARNING:
		return "W"
	case NOTICE:
		return "N"
	case INFO:
		return "I"
	case DEBUG:
		return "D"
	case TRACE:
		return "T"
	default:
		panic("Unhandled loglevel")
	}
}

// String returns a multi-character representation of the log level.
func ( LogLevel) () string {
	switch  {
	case CRITICAL:
		return "CRITICAL"
	case ERROR:
		return "ERROR"
	case WARNING:
		return "WARNING"
	case NOTICE:
		return "NOTICE"
	case INFO:
		return "INFO"
	case DEBUG:
		return "DEBUG"
	case TRACE:
		return "TRACE"
	default:
		panic("Unhandled loglevel")
	}
}

// Update using the given string value. Fulfills the flag.Value interface.
func ( *LogLevel) ( string) error {
	,  := ParseLevel()
	if  != nil {
		return 
	}

	* = 
	return nil
}

// Returns an empty string, only here to fulfill the pflag.Value interface.
func ( *LogLevel) () string {
	return ""
}

// ParseLevel translates some potential loglevel strings into their corresponding levels.
func ( string) (LogLevel, error) {
	switch  {
	case "CRITICAL", "C":
		return CRITICAL, nil
	case "ERROR", "0", "E":
		return ERROR, nil
	case "WARNING", "1", "W":
		return WARNING, nil
	case "NOTICE", "2", "N":
		return NOTICE, nil
	case "INFO", "3", "I":
		return INFO, nil
	case "DEBUG", "4", "D":
		return DEBUG, nil
	case "TRACE", "5", "T":
		return TRACE, nil
	}
	return CRITICAL, errors.New("couldn't parse log level " + )
}

type RepoLogger map[string]*PackageLogger

type loggerStruct struct {
	sync.Mutex
	repoMap   map[string]RepoLogger
	formatter Formatter
}

// logger is the global logger
var logger = new(loggerStruct)

// SetGlobalLogLevel sets the log level for all packages in all repositories
// registered with capnslog.
func ( LogLevel) {
	logger.Lock()
	defer logger.Unlock()
	for ,  := range logger.repoMap {
		.setRepoLogLevelInternal()
	}
}

// GetRepoLogger may return the handle to the repository's set of packages' loggers.
func ( string) (RepoLogger, error) {
	logger.Lock()
	defer logger.Unlock()
	,  := logger.repoMap[]
	if ! {
		return nil, errors.New("no packages registered for repo " + )
	}
	return , nil
}

// MustRepoLogger returns the handle to the repository's packages' loggers.
func ( string) RepoLogger {
	,  := GetRepoLogger()
	if  != nil {
		panic()
	}
	return 
}

// SetRepoLogLevel sets the log level for all packages in the repository.
func ( RepoLogger) ( LogLevel) {
	logger.Lock()
	defer logger.Unlock()
	.setRepoLogLevelInternal()
}

func ( RepoLogger) ( LogLevel) {
	for ,  := range  {
		.level = 
	}
}

// ParseLogLevelConfig parses a comma-separated string of "package=loglevel", in
// order, and returns a map of the results, for use in SetLogLevel.
func ( RepoLogger) ( string) (map[string]LogLevel, error) {
	 := strings.Split(, ",")
	 := make(map[string]LogLevel)
	for ,  := range  {
		 := strings.Split(, "=")
		if len() != 2 {
			return nil, errors.New("oddly structured `pkg=level` option: " + )
		}
		,  := ParseLevel([1])
		if  != nil {
			return nil, 
		}
		[[0]] = 
	}
	return , nil
}

// SetLogLevel takes a map of package names within a repository to their desired
// loglevel, and sets the levels appropriately. Unknown packages are ignored.
// "*" is a special package name that corresponds to all packages, and will be
// processed first.
func ( RepoLogger) ( map[string]LogLevel) {
	logger.Lock()
	defer logger.Unlock()
	if ,  := ["*"];  {
		.setRepoLogLevelInternal()
	}
	for ,  := range  {
		,  := []
		if ! {
			continue
		}
		.level = 
	}
}

// SetFormatter sets the formatting function for all logs.
func ( Formatter) {
	logger.Lock()
	defer logger.Unlock()
	logger.formatter = 
}

// NewPackageLogger creates a package logger object.
// This should be defined as a global var in your package, referencing your repo.
func ( string,  string) ( *PackageLogger) {
	logger.Lock()
	defer logger.Unlock()
	if logger.repoMap == nil {
		logger.repoMap = make(map[string]RepoLogger)
	}
	,  := logger.repoMap[]
	if ! {
		logger.repoMap[] = make(RepoLogger)
		 = logger.repoMap[]
	}
	,  := []
	if ! {
		[] = &PackageLogger{
			pkg:   ,
			level: INFO,
		}
		 = []
	}
	return
}