//go:build linux || darwin
// +build linux darwin

package namedpipe

import (
	"bufio"
	"fmt"
	log "github.com/sirupsen/logrus"
	"os"
	"sync/atomic"
	"syscall"
)

const ONEAGENT = "cw-oneagent"
const NETFLOW = "cw-netflow"
const SERVERAGENT = "cw-serveragent"

const MASTER = true
const AGENT = false

type NamedPipe4U struct {
	*namedPipe
	sendPipeFileHandler *os.File
	recvPipeFileHandler *os.File
	sendPipePath        string
	recvPipePath        string
}

/*
ListenNpipe

	@Description: 创建命名管道文件，返回命名管道对象
	@param  agent_type:   from
	@param  to_type:      to
	@param  isMaster:     是否为daemon创建的
	@param  single:       是否为单文件模式
	@param  rootPath:     命名管道文件夹路径
	@return *NamedPipe4U: 命名管道对象
	@return error:        错误信息
*/
func ListenNpipe(agent_type, to_type string, isMaster, single bool, rootPath string) (*NamedPipe4U, error) {
	np, err := newNamePipe(agent_type, to_type, isMaster, single, rootPath)
	if err != nil {
		return nil, err
	}
	np4u := &NamedPipe4U{namedPipe: np}
	if err := np4u.initPipe(); err != nil {
		return nil, err
	}
	return np4u, nil
}

func (np *NamedPipe4U) getPipePath() (daemon2subPipe, sub2DaemonPipe string, err error) {

	basePath := np.rootPath + "/"
	//确认目录存在
	if _, err := os.Stat(basePath); os.IsNotExist(err) {
		if err := os.MkdirAll(basePath, 0755); err != nil {
			panic(fmt.Sprintf("mkdir for named-pipe failed . path:%v ,error:%v", basePath, err.Error()))
		}
	}

	if np.single {
		singlePipe := basePath + np.fromType
		return singlePipe, singlePipe, nil
	}
	daemon2subPipe = basePath + fmt.Sprintf("%s2%s", np.fromType, np.toType)
	sub2DaemonPipe = basePath + fmt.Sprintf("%s2%s", np.toType, np.fromType)
	return daemon2subPipe, sub2DaemonPipe, err
}

func (np *NamedPipe4U) initPipe() error {
	var pipeFiles []string
	daemon2subPipe, sub2DaemonPipe, err := np.getPipePath()
	if err != nil {
		return err
	}

	if np.single {
		pipeFiles = []string{daemon2subPipe}
	} else {
		pipeFiles = []string{daemon2subPipe, sub2DaemonPipe}
	}

	for _, pf := range pipeFiles {
		if _, err := os.Lstat(pf); os.IsNotExist(err) == true {
			err = syscall.Mkfifo(pf, 0666)
			if err != nil {
				return err
			}
		} else if err != nil {
			return err
		}
	}

	//根据角色指定pipe的路径（角色不同路径不同）
	if np.isDaemon {
		//daemon进程Open顺序 和 net进程Open顺序 相反
		np.sendPipePath = daemon2subPipe
		np.recvPipePath = sub2DaemonPipe
		//OpenFile过程可能会阻塞
		if sf, err := os.OpenFile(np.sendPipePath, os.O_WRONLY|os.O_CREATE, os.ModeNamedPipe|0666); err == nil {
			np.sendPipeFileHandler = sf
			np.sendPipe = bufio.NewWriter(sf)
		} else {
			log.WithField("module", "namedPipe_Unix").Errorf("open send pipe(%s) error: %v", np.sendPipePath, err)
			return err
		}

		if rf, err := os.OpenFile(np.recvPipePath, os.O_RDONLY|os.O_CREATE, os.ModeNamedPipe|0666); err == nil {
			np.recvPipeFileHandler = rf
			np.recvPipe = bufio.NewReader(rf)
		} else {
			log.WithField("module", "namedPipe_Unix").Errorf("open recv pipe(%s) error: %v", np.recvPipePath, err)
			sf := np.sendPipeFileHandler
			np.sendPipeFileHandler = nil
			np.sendPipe = nil
			if sf != nil {
				if errClose := sf.Close(); errClose != nil {
					log.WithField("module", "namedPipe_Unix").Errorf("close send pipe(%s) error:%v", np.sendPipePath, errClose)
				}
			}
			return err
		}
	} else {
		//net进程Open顺序和daemon进程Open顺序相反
		np.sendPipePath = daemon2subPipe
		np.recvPipePath = sub2DaemonPipe
		//OpenFile过程可能会阻塞
		if rf, err := os.OpenFile(np.recvPipePath, os.O_RDONLY|os.O_CREATE, os.ModeNamedPipe|0666); err == nil {
			np.recvPipeFileHandler = rf
			np.recvPipe = bufio.NewReader(rf)
		} else {
			log.WithField("module", "namedPipe_Unix").Errorf("open recv pipe(%s) error: %v", np.recvPipePath, err)
			return err
		}

		if sf, err := os.OpenFile(np.sendPipePath, os.O_WRONLY|os.O_CREATE, os.ModeNamedPipe|0666); err == nil {
			np.sendPipeFileHandler = sf
			np.sendPipe = bufio.NewWriter(sf)
		} else {
			log.WithField("module", "namedPipe_Unix").Errorf("open send pipe(%s) error: %v", np.sendPipePath, err)
			rf := np.recvPipeFileHandler
			np.recvPipeFileHandler = nil
			np.recvPipe = nil
			if rf != nil {
				if errClose := rf.Close(); errClose != nil {
					log.WithField("module", "namedPipe_Unix").Errorf("close recv pipe(%s) error:%v", np.recvPipePath, errClose)
				}
			}
			return err
		}
	}
	return nil
}

// FreeUp4EOF only called when io.EOF occurs
func (np *NamedPipe4U) FreeUp4EOF() {
	if !atomic.CompareAndSwapUint32(&np.quitFlag, 0, 1) {
		log.WithField("module", "namedPipe_Unix").Warnf("named pipe(%s)(%s) already free", np.sendPipePath, np.recvPipePath)
		return
	}
	log.WithField("module", "namedPipe_Unix").Infof("named pipe free(%s)(%s) begin", np.sendPipePath, np.recvPipePath)

	sendPipeFileHandler := np.sendPipeFileHandler
	np.sendPipeFileHandler = nil
	if sendPipeFileHandler != nil {
		err := sendPipeFileHandler.Close()
		if err != nil {
			log.WithField("module", "namedPipe_Unix").Errorf("close send pipe(%s) failed. error:%v", np.sendPipePath, err)
		}
	}

	recvPipeFileHandler := np.recvPipeFileHandler
	np.recvPipeFileHandler = nil
	if recvPipeFileHandler != nil {
		err := recvPipeFileHandler.Close()
		if err != nil {
			log.WithField("module", "namedPipe_Unix").Errorf("close recv pipe(%s) failed. error:%v", np.recvPipePath, err)
		}
	}

	np.sendPipe = nil
	np.recvPipe = nil
	log.WithField("module", "namedPipe_Unix").Infof("named pipe(%s)(%s) free end", np.sendPipePath, np.recvPipePath)
}
