package config

import (
	"github.com/BurntSushi/toml"
	"log"
	"os"
	"reflect"
	"strings"
	"time"
)

const (
	EVENT_JOIN_LEAVE      = "join_leave"
	EVENT_FAILURE         = "failure"
	EVENT_REJOIN_CHANNELS = "rejoin_channels"
	EVENT_USER_ACTION     = "user_action"
	EVENT_MSG_DELETE      = "msg_delete"
    EVENT_EDIT = "edit"
    EVENT_PM = "pm"
)

type Message struct {
	Text      string    `json:"text"`
	Channel   string    `json:"channel"`
	Username  string    `json:"username"`
	UserID    string    `json:"userid"` 
	Avatar    string    `json:"avatar"`
	Account   string    `json:"account"`
	Event     string    `json:"event"`
	Protocol  string    `json:"protocol"`
	Gateway   string    `json:"gateway"`
	Timestamp time.Time `json:"timestamp"`
	ID        string    `json:"id"`
	Extra     map[string][]interface{}
}

type FileInfo struct {
	Name string
	Data *[]byte
}

type ChannelInfo struct {
	Name        string
	Account     string
	Direction   string
	ID          string
	SameChannel map[string]bool
	Options     ChannelOptions
}

type Protocol struct {
	AuthCode               string 
	BindAddress            string 
	Buffer                 int    
	Charset                string 
	EditSuffix             string 
	EditDisable            bool   
	IconURL                string 
	IgnoreNicks            string 
	IgnoreMessages         string 
	Jid                    string 
	Login                  string 
	Muc                    string 
	Name                   string 
	Nick                   string 
	NickFormatter          string 
	NickServNick           string 
	NickServUsername       string 
	NickServPassword       string 
	NicksPerRow            int    
	NoHomeServerSuffix     bool   
	NoTLS                  bool   
	Password               string 
	PrefixMessagesWithNick bool   
	Protocol               string 
	MessageQueue           int    
	MessageDelay           int    
	MessageLength          int    
	MessageFormat          string 
	RemoteNickFormat       string 
	Server                 string 
    Sid                    string 
	ShowJoinPart           bool   
	ShowEmbeds             bool   
	SkipTLSVerify          bool   
	StripNick              bool   
	Team                   string 
	Token                  string 
	URL                    string 
	UseAPI                 bool   
	UseSASL                bool   
	UseTLS                 bool   
	UseFirstName           bool   
	UseUserName            bool   
	UseInsecureURL         bool   
	WebhookBindAddress     string 
	WebhookURL             string 
	WebhookUse             string 
}

type ChannelOptions struct {
	Key        string 
	WebhookURL string 
}

type Bridge struct {
	Account     string
	Channel     string
	Options     ChannelOptions
	SameChannel bool
}

type Gateway struct {
    Name   string
    Enable bool
    In     []Bridge
    Out    []Bridge
    InOut  []Bridge
}

type SameChannelGateway struct {
    Name     string
    Enable   bool
    Channels []string
    Accounts []string
}

type Config struct {
	Api                map[string]Protocol
	IRC                map[string]Protocol
	Mattermost         map[string]Protocol
	Matrix             map[string]Protocol
	Slack              map[string]Protocol
	Steam              map[string]Protocol
	Gitter             map[string]Protocol
	Xmpp               map[string]Protocol
	Discord            map[string]Protocol
	Telegram           map[string]Protocol
	Rocketchat         map[string]Protocol
    Unreal             map[string]Protocol
	General            Protocol
	Gateway            []Gateway
	SameChannelGateway []SameChannelGateway
}

func NewConfig(cfgfile string) *Config {
	var cfg Config
	if _, err := toml.DecodeFile(cfgfile, &cfg); err != nil {
		log.Fatal(err)
	}
	fail := false
	for k, v := range cfg.Mattermost {
		res := Deprecated(v, "mattermost."+k)
		if res {
			fail = res
		}
	}
	for k, v := range cfg.Slack {
		res := Deprecated(v, "slack."+k)
		if res {
			fail = res
		}
	}
	for k, v := range cfg.Rocketchat {
		res := Deprecated(v, "rocketchat."+k)
		if res {
			fail = res
		}
	}
	if fail {
		log.Fatalf("Fix your config. Please see changelog for more information")
	}
	return &cfg
}

func OverrideCfgFromEnv(cfg *Config, protocol string, account string) {
    var protoCfg Protocol
    val := reflect.ValueOf(cfg).Elem()

    for i := 0; i < val.NumField(); i++ {
        typeField := val.Type().Field(i)

        if strings.ToLower(typeField.Name) == protocol {

            data := val.Field(i).MapIndex(reflect.ValueOf(account))
            protoCfg = data.Interface().(Protocol)
            protoStruct := reflect.ValueOf(&protoCfg).Elem()

            for i := 0; i < protoStruct.NumField(); i++ {
                typeField := protoStruct.Type().Field(i)

                key := "matterbridge_" + protocol + "_" + account + "_" + typeField.Name
                key = strings.ToUpper(key)

                res := os.Getenv(key)

                if res != "" {
                    fieldVal := protoStruct.Field(i)
                    if fieldVal.Kind() == reflect.String {
                        log.Printf("config: overriding %s from env with %s\n", key, res)
                        fieldVal.Set(reflect.ValueOf(res))
                    }
                }
            }

            val.Field(i).SetMapIndex(reflect.ValueOf(account), reflect.ValueOf(protoCfg))
            break
        }
    }
}

func GetIconURL(msg *Message, cfg *Protocol) string {
    iconURL := cfg.IconURL
    info := strings.Split(msg.Account, ".")
    protocol := info[0]
    name := info[1]
    iconURL = strings.Replace(iconURL, "{NICK}", msg.Username, -1)
    iconURL = strings.Replace(iconURL, "{BRIDGE}", name, -1)
    iconURL = strings.Replace(iconURL, "{PROTOCOL}", protocol, -1)
    return iconURL
}

func Deprecated(cfg Protocol, account string) bool {
	if cfg.BindAddress != "" {
		log.Printf("ERROR: %s BindAddress is deprecated, you need to change it to WebhookBindAddress.", account)
	} else if cfg.URL != "" {
		log.Printf("ERROR: %s URL is deprecated, you need to change it to WebhookURL.", account)
	} else if cfg.UseAPI {
		log.Printf("ERROR: %s UseAPI is deprecated, it's enabled by default, please remove it from your config file.", account)
	} else {
		return false
	}
	return true

}
package config

import (
	"github.com/BurntSushi/toml"
	"log"
	"os"
	"reflect"
	"strings"
	"time"
)

const (
	EVENT_JOIN_LEAVE      = "join_leave"
	EVENT_FAILURE         = "failure"
	EVENT_REJOIN_CHANNELS = "rejoin_channels"
	EVENT_USER_ACTION     = "user_action"
	EVENT_MSG_DELETE      = "msg_delete"
    EVENT_EDIT = "edit"
    EVENT_PM = "pm"
)

type Message struct {
	Text      string    `json:"text"`
	Channel   string    `json:"channel"`
	Username  string    `json:"username"`
	UserID    string    `json:"userid"` // userid on the bridge
	Avatar    string    `json:"avatar"`
	Account   string    `json:"account"`
	Event     string    `json:"event"`
	Protocol  string    `json:"protocol"`
	Gateway   string    `json:"gateway"`
	Timestamp time.Time `json:"timestamp"`
	ID        string    `json:"id"`
	Extra     map[string][]interface{}
}

type FileInfo struct {
	Name string
	Data *[]byte
}

type ChannelInfo struct {
	Name        string
	Account     string
	Direction   string
	ID          string
	SameChannel map[string]bool
	Options     ChannelOptions
}

type Protocol struct {
	AuthCode               string // steam
	BindAddress            string // mattermost, slack // DEPRECATED
	Buffer                 int    // api
	Charset                string // irc
	EditSuffix             string // mattermost, slack, discord, telegram, gitter
	EditDisable            bool   // mattermost, slack, discord, telegram, gitter
	IconURL                string // mattermost, slack
	IgnoreNicks            string // all protocols
	IgnoreMessages         string // all protocols
	Jid                    string // xmpp
	Login                  string // mattermost, matrix
	Muc                    string // xmpp
	Name                   string // all protocols
	Nick                   string // all protocols
	NickFormatter          string // mattermost, slack
	NickServNick           string // IRC
	NickServUsername       string // IRC
	NickServPassword       string // IRC
	NicksPerRow            int    // mattermost, slack
	NoHomeServerSuffix     bool   // matrix
	NoTLS                  bool   // mattermost
	Password               string // IRC,mattermost,XMPP,matrix,unreal
	PrefixMessagesWithNick bool   // mattemost, slack
	Protocol               string //all protocols
	MessageQueue           int    // IRC, size of message queue for flood control
	MessageDelay           int    // IRC, time in millisecond to wait between messages
	MessageLength          int    // IRC, max length of a message allowed
	MessageFormat          string // telegram
	RemoteNickFormat       string // all protocols
	Server                 string // IRC,mattermost,XMPP,discord,unreal
    Sid                    string // unreal
	ShowJoinPart           bool   // all protocols
	ShowEmbeds             bool   // discord
	SkipTLSVerify          bool   // IRC, mattermost
	StripNick              bool   // all protocols
	Team                   string // mattermost
	Token                  string // gitter, slack, discord, api
	URL                    string // mattermost, slack // DEPRECATED
	UseAPI                 bool   // mattermost, slack
	UseSASL                bool   // IRC
	UseTLS                 bool   // IRC
	UseFirstName           bool   // telegram
	UseUserName            bool   // discord
	UseInsecureURL         bool   // telegram
	WebhookBindAddress     string // mattermost, slack
	WebhookURL             string // mattermost, slack
	WebhookUse             string // mattermost, slack, discord
}

type ChannelOptions struct {
	Key        string // irc
	WebhookURL string // discord
}

type Bridge struct {
	Account     string
	Channel     string
	Options     ChannelOptions
	SameChannel bool
}

type Gateway struct {
    Name   string
    Enable bool
    In     []Bridge
    Out    []Bridge
    InOut  []Bridge
}

type SameChannelGateway struct {
    Name     string
    Enable   bool
    Channels []string
    Accounts []string
}

type Config struct {
	Api                map[string]Protocol
	IRC                map[string]Protocol
	Mattermost         map[string]Protocol
	Matrix             map[string]Protocol
	Slack              map[string]Protocol
	Steam              map[string]Protocol
	Gitter             map[string]Protocol
	Xmpp               map[string]Protocol
	Discord            map[string]Protocol
	Telegram           map[string]Protocol
	Rocketchat         map[string]Protocol
    Unreal             map[string]Protocol
	General            Protocol
	Gateway            []Gateway
	SameChannelGateway []SameChannelGateway
}

func NewConfig(cfgfile string) *Config {
	var cfg Config
	if _, err := toml.DecodeFile(cfgfile, &cfg); err != nil {
		log.Fatal(err)
	}
	fail := false
	for k, v := range cfg.Mattermost {
		res := Deprecated(v, "mattermost."+k)
		if res {
			fail = res
		}
	}
	for k, v := range cfg.Slack {
		res := Deprecated(v, "slack."+k)
		if res {
			fail = res
		}
	}
	for k, v := range cfg.Rocketchat {
		res := Deprecated(v, "rocketchat."+k)
		if res {
			fail = res
		}
	}
	if fail {
		log.Fatalf("Fix your config. Please see changelog for more information")
	}
	return &cfg
}

func OverrideCfgFromEnv(cfg *Config, protocol string, account string) {
    var protoCfg Protocol
    val := reflect.ValueOf(cfg).Elem()
    // loop over the Config struct
    for i := 0; i < val.NumField(); i++ {
        typeField := val.Type().Field(i)
        // look for the protocol map (both lowercase)
        if strings.ToLower(typeField.Name) == protocol {
            // get the Protocol struct from the map
            data := val.Field(i).MapIndex(reflect.ValueOf(account))
            protoCfg = data.Interface().(Protocol)
            protoStruct := reflect.ValueOf(&protoCfg).Elem()
            // loop over the found protocol struct
            for i := 0; i < protoStruct.NumField(); i++ {
                typeField := protoStruct.Type().Field(i)
                // build our environment key (eg MATTERBRIDGE_MATTERMOST_WORK_LOGIN)
                key := "matterbridge_" + protocol + "_" + account + "_" + typeField.Name
                key = strings.ToUpper(key)
                // search the environment
                res := os.Getenv(key)
                // if it exists and the current field is a string
                // then update the current field
                if res != "" {
                    fieldVal := protoStruct.Field(i)
                    if fieldVal.Kind() == reflect.String {
                        log.Printf("config: overriding %s from env with %s\n", key, res)
                        fieldVal.Set(reflect.ValueOf(res))
                    }
                }
            }
            // update the map with the modified Protocol (cfg.Protocol[account] = Protocol)
            val.Field(i).SetMapIndex(reflect.ValueOf(account), reflect.ValueOf(protoCfg))
            break
        }
    }
}

func GetIconURL(msg *Message, cfg *Protocol) string {
    iconURL := cfg.IconURL
    info := strings.Split(msg.Account, ".")
    protocol := info[0]
    name := info[1]
    iconURL = strings.Replace(iconURL, "{NICK}", msg.Username, -1)
    iconURL = strings.Replace(iconURL, "{BRIDGE}", name, -1)
    iconURL = strings.Replace(iconURL, "{PROTOCOL}", protocol, -1)
    return iconURL
}

func Deprecated(cfg Protocol, account string) bool {
	if cfg.BindAddress != "" {
		log.Printf("ERROR: %s BindAddress is deprecated, you need to change it to WebhookBindAddress.", account)
	} else if cfg.URL != "" {
		log.Printf("ERROR: %s URL is deprecated, you need to change it to WebhookURL.", account)
	} else if cfg.UseAPI {
		log.Printf("ERROR: %s UseAPI is deprecated, it's enabled by default, please remove it from your config file.", account)
	} else {
		return false
	}
	return true
	//log.Fatalf("ERROR: Fix your config: %s", account)
}
