package config_manager_go

import (
	"encoding/json"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"loafle.com/overflow/agent_api/config_manager"
	"loafle.com/overflow/agent_api/observer"
	"loafle.com/overflow/agent_api/observer/messages"
	"log"
	"os"
	"strings"
	"sync"
)

type configManager struct {
	config_manager.ConfigManager // interface implements

	addCh    chan interface{}
	removeCh chan interface{}

	configs      map[string] *config_manager.Config
	globalConfig config_manager.GlobalConfig
}

var _configManager *configManager

func init() {
	var once sync.Once
	once.Do(func() {
		_configManager = NewConfigManager()
	})

	// regist handlers
	agentStartHandler()
	addSensorHandler()
	removeSensorHandler()
	agentEndHandler()
}


func NewConfigManager() *configManager {
	c := &configManager{
		configs:  make(map[string]*config_manager.Config, 0),
		addCh:    make(chan interface{}, 0),
		removeCh: make(chan interface{}, 0),
	}
	return c
}

func (c *configManager) getConfigPath() string {
	return c.globalConfig.Paths.RootFolder + c.globalConfig.Paths.ConfigFolder
}

func (c *configManager) getContainerPath() string {
	return c.getConfigPath() + "container/"
}

func (c *configManager) getConfigFilePath(config *config_manager.Config) string {
	return c.getContainerPath() + appendSeperator(config.Crawler.Container) + appendSeperator(config.Crawler.Name) + config.Id
}



func appendSeperator(str string) string {
	if strings.LastIndex(str, "/") != len(str)-1 {
		return str + "/"
	}
	return str
}

func (c *configManager) loadGlobalConfig(path string) {
	data, err := ioutil.ReadFile(path)
	if err != nil {
	}
	err = yaml.Unmarshal([]byte(data), &c.globalConfig)
	if err != nil {
	}
}

func (c *configManager) loadCrawlerConfigAll() error {

	root := c.getConfigPath()
	files, err := ioutil.ReadDir(root)
	if err != nil {
		log.Panic(err)
	}
	for _, file := range files {
		if file.IsDir() == true {
			c.loadCrawlerConfig(root, file.Name())
		}
	}
	return nil
}

func (c *configManager) loadCrawlerConfig(root string, dir string) {

	separator := ""
	if strings.LastIndex(root, "/") != len(root)-1 {
		separator = "/"
	}
	currentDir := root + separator + dir
	files, err := ioutil.ReadDir(currentDir)
	if err != nil {
		log.Panic(err)
	}

	for _, file := range files {
		
		// 디렉터리라면 재귀 / 파일이라면 설정 로드
		if file.IsDir() == true {
			c.loadCrawlerConfig(currentDir, file.Name())
		} else {
			b, err := ioutil.ReadFile(currentDir + separator + file.Name())
			if err != nil {
				log.Panic(err)
			}
			var m = config_manager.Config{}
			json.Unmarshal(b, &m)
			c.configs[file.Name()] = &m
		}
	}

}

func (c *configManager) addConfig() {
	for data := range c.addCh {

		path := data.(string)
		b, err := ioutil.ReadFile(path)
		if err != nil {
			// error process
			continue
		}

		// 임시파일을 로드 ,  Config로 변환
		var m = config_manager.Config{}
		err = json.Unmarshal(b, &m)
		if err != nil {
			// error process
			continue
		}

		// agent 폴더 / config / crawler / .. / .. / .. / 에 해당하는 파일이 있는지 확인, 있다면 삭제
		// Config 파일로 저장
		savePath := c.getConfigFilePath(&m)
		ioutil.WriteFile(savePath, b, 0644)

		// tempfile remove
		err = os.Remove(path)
		if err != nil {
			// error process
			continue
		}

		// Config 맵에 저장
		c.configs[m.Id] = &m

		// notify id
		err = observer.Notify(messages.SCF_SENSOR_ADD_DONE, m.Id)
		if err != nil {
			continue
		}
	}
}

func (c *configManager) removeConfig() {
	for data := range c.removeCh {
		removeid := data.(string)

		// check exists
		config, ok := c.configs[removeid]
		if !ok {
			continue
		}

		// 해당 파일 삭제
		path := c.getConfigFilePath(config)
		err := os.Remove(path)
		if err != nil {
			// error check
			continue
		}

		// 해당 id 삭제
		delete(c.configs, removeid)

		// notify id
		err = observer.Notify("TASK_DONE", removeid)
		if err != nil {
			continue
		}
	}
}



// implements methods
func (c *configManager) GetGlobalConfig() *config_manager.GlobalConfig {
	return &c.globalConfig
}
func (c *configManager) GetSensorById(id string) *config_manager.Config {
	return c.configs[id]
}
func (c *configManager) GetSensors() map[string] *config_manager.Config {
	return c.configs
}
