/*
** Zabbix
** Copyright (C) 2001-2021 Zabbix SIA
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
**/

package file

import (
	"errors"
	"fmt"
	"os"
	"strings"
)

// File Types
const (
	zbxFtFile = 1 << iota
	zbxFtDir
	zbxFtSym
	zbxFtSock
	zbxFtBdev
	zbxFtCdev
	zbxFtFifo
	zbxFtAll = (zbxFtFile | zbxFtDir | zbxFtSym | zbxFtSock | zbxFtBdev | zbxFtCdev | zbxFtFifo)
	zbxFtDev = (zbxFtBdev | zbxFtCdev)
)

type fileType uint16

func (f fileType) hasType(t fileType) bool { return f&t != 0 }
func (f *fileType) addType(t fileType)     { *f |= t }

func typesToMask(param string) (fileType, error) {
	var mask fileType = 0
	template := map[string]fileType{
		"file": zbxFtFile,
		"dir":  zbxFtDir,
		"sym":  zbxFtSym,
		"sock": zbxFtSock,
		"bdev": zbxFtBdev,
		"cdev": zbxFtCdev,
		"fifo": zbxFtFifo,
		"all":  zbxFtAll,
		"dev":  zbxFtDev,
	}

	if strings.TrimSpace(param) == "" {
		return 0, nil
	}

	types := strings.Split(param, ",")

	for _, name := range types {
		name = strings.TrimSpace(name)
		if t, ok := template[name]; ok {
			mask.addType(t)
		} else {
			return 0, fmt.Errorf(`Invalid type "%s".`, name)
		}
	}

	return mask, nil
}

// Export -
func (p *Plugin) exportExists(params []string) (result interface{}, err error) {
	var typesIncl fileType
	var typesExcl fileType
	var types fileType
	var f os.FileInfo

	if len(params) > 3 {
		return nil, errors.New("Too many parameters.")
	}
	if len(params) == 0 || params[0] == "" {
		return nil, errors.New("Invalid first parameter.")
	}

	if len(params) > 1 && params[1] != "" {
		if typesIncl, err = typesToMask(params[1]); err != nil {
			return nil, err
		}
	}

	if len(params) > 2 && params[2] != "" {
		if typesExcl, err = typesToMask(params[2]); err != nil {
			return nil, err
		}
	}

	if typesIncl == 0 {
		if typesExcl == 0 {
			typesIncl.addType(zbxFtFile)
		} else {
			typesIncl.addType(zbxFtAll)
		}
	}

	if typesIncl.hasType(zbxFtSym) || typesExcl.hasType(zbxFtSym) {
		if f, err = os.Lstat(params[0]); err == nil {
			if f.Mode()&os.ModeSymlink != 0 {
				types.addType(zbxFtSym)
			}
		} else if !os.IsNotExist(err) {
			return 0, fmt.Errorf("Cannot obtain file information: %s", err)
		}
	}

	if f, err = os.Stat(params[0]); err == nil {
		if f.Mode().IsRegular() {
			types.addType(zbxFtFile)
		} else if f.Mode().IsDir() {
			types.addType(zbxFtDir)
		} else if f.Mode()&os.ModeSocket != 0 {
			types.addType(zbxFtSock)
		} else if f.Mode()&os.ModeCharDevice != 0 {
			types.addType(zbxFtCdev)
		} else if f.Mode()&os.ModeDevice != 0 {
			types.addType(zbxFtBdev)
		} else if f.Mode()&os.ModeNamedPipe != 0 {
			types.addType(zbxFtFifo)
		}
	} else if !os.IsNotExist(err) {
		return 0, fmt.Errorf("Cannot obtain file information: %s", err)
	}

	if !typesExcl.hasType(types) && typesIncl.hasType(types) {
		return 1, nil
	}
	return 0, nil
}
