package bridge

import (
	//"fmt"
	"github.com/42wim/matterbridge/bridge/config"
	"github.com/42wim/matterbridge/bridge/gitter"
	"github.com/42wim/matterbridge/bridge/irc"
	"github.com/42wim/matterbridge/bridge/mattermost"
	"github.com/42wim/matterbridge/bridge/slack"
	"github.com/42wim/matterbridge/bridge/xmpp"
	log "github.com/Sirupsen/logrus"
	"strings"
)

type Bridge struct {
	*config.Config
	Source      string
	Bridges     []Bridger
	Channels    []map[string]string
	ignoreNicks map[string][]string
}

type Bridger interface {
	Send(msg config.Message) error
	Name() string
	Connect() error
	//Command(cmd string) string
}

func NewBridge(cfg *config.Config) error {
	c := make(chan config.Message)
	b := &Bridge{}
	b.Config = cfg
	if cfg.IRC.Enable {
		b.Bridges = append(b.Bridges, birc.New(cfg, c))
	}
	if cfg.Mattermost.Enable {
		b.Bridges = append(b.Bridges, bmattermost.New(cfg, c))
	}
	if cfg.Xmpp.Enable {
		b.Bridges = append(b.Bridges, bxmpp.New(cfg, c))
	}
	if cfg.Gitter.Enable {
		b.Bridges = append(b.Bridges, bgitter.New(cfg, c))
	}
	if cfg.Slack.Enable {
		b.Bridges = append(b.Bridges, bslack.New(cfg, c))
	}
	if len(b.Bridges) < 2 {
		log.Fatalf("only %d sections enabled. Need at least 2 sections enabled (eg [IRC] and [mattermost]", len(b.Bridges))
	}
	for _, br := range b.Bridges {
		br.Connect()
	}
	b.mapChannels()
	b.mapIgnores()
	b.handleReceive(c)
	return nil
}

func (b *Bridge) handleReceive(c chan config.Message) {
	for {
		select {
		case msg := <-c:
			for _, br := range b.Bridges {
				b.handleMessage(msg, br)
			}
		}
	}
}

func (b *Bridge) mapChannels() error {
	for _, val := range b.Config.Channel {
		m := make(map[string]string)
		m["irc"] = val.IRC
		m["mattermost"] = val.Mattermost
		m["xmpp"] = val.Xmpp
		m["gitter"] = val.Gitter
		m["slack"] = val.Slack
		b.Channels = append(b.Channels, m)
	}
	return nil
}

func (b *Bridge) mapIgnores() {
	m := make(map[string][]string)
	m["irc"] = strings.Fields(b.Config.IRC.IgnoreNicks)
	m["mattermost"] = strings.Fields(b.Config.Mattermost.IgnoreNicks)
	m["xmpp"] = strings.Fields(b.Config.Xmpp.IgnoreNicks)
	m["gitter"] = strings.Fields(b.Config.Gitter.IgnoreNicks)
	m["slack"] = strings.Fields(b.Config.Slack.IgnoreNicks)
	b.ignoreNicks = m
}

func (b *Bridge) getDestChannel(msg *config.Message, dest string) string {
	for _, v := range b.Channels {
		if v[msg.Origin] == msg.Channel {
			return v[dest]
		}
	}
	return ""
}

func (b *Bridge) handleMessage(msg config.Message, dest Bridger) {
	if b.ignoreMessage(&msg) {
		return
	}
	if dest.Name() != msg.Origin {
		msg.Channel = b.getDestChannel(&msg, dest.Name())
		if msg.Channel == "" {
			return
		}
		b.modifyMessage(&msg, dest.Name())
		log.Debugf("sending %#v from %s to %s", msg, msg.Origin, dest.Name())
		dest.Send(msg)
	}
}

func (b *Bridge) ignoreMessage(msg *config.Message) bool {
	// should we discard messages ?
	for _, entry := range b.ignoreNicks[msg.Origin] {
		if msg.Username == entry {
			return true
		}
	}
	return false
}

func setNickFormat(msg *config.Message, format string) {
	if format == "" {
		msg.Username = msg.Origin + "-" + msg.Username + ": "
		return
	}
	msg.Username = strings.Replace(format, "{NICK}", msg.Username, -1)
	msg.Username = strings.Replace(msg.Username, "{BRIDGE}", msg.Origin, -1)
}

func (b *Bridge) modifyMessage(msg *config.Message, dest string) {
	switch dest {
	case "irc":
		setNickFormat(msg, b.Config.IRC.RemoteNickFormat)
	case "gitter":
		setNickFormat(msg, b.Config.Gitter.RemoteNickFormat)
	case "xmpp":
		setNickFormat(msg, b.Config.Xmpp.RemoteNickFormat)
	case "mattermost":
		setNickFormat(msg, b.Config.Mattermost.RemoteNickFormat)
	case "slack":
		setNickFormat(msg, b.Config.Slack.RemoteNickFormat)
	}
}