package smb

import (
	"errors"
	"fmt"
	"strings"

	"imuslab.com/smb/driver/mod/smb/encoder"
)

func init() {
	commandRequestMap[CommandTreeConnect] = func() DataI {
		return &TreeConnectRequest{}
	}
}

// TreeConnect
type TreeConnectRequest struct {
	Header
	StructureSize uint16
	TreeFlags     uint16 //Flags/Reserved
	PathOffset    uint16 `smb:"offset:Path"`
	PathLength    uint16 `smb:"len:Path"`
	Path          []byte
}
type request_TreeConnect_Extension struct {
	TreeConnectContextOffset  uint32 `smb:"offset:TreeConnectContext"`
	TreeConnectContextCount   uint16 `smb:"len:TreeConnectContext"`
	Reserved                  []byte `smb:"fixed:10"`
	PathNameOffset            uint16 `smb:"offset:PathName"`
	PathNameLength            uint16 `smb:"len:PathName"`
	TreeConnectContextsOffset uint16 `smb:"offset:TreeConnectContexts"`
	TreeConnectContextsLength uint16 `smb:"len:TreeConnectContexts"`
	TreeConnectContext        []byte
	PathName                  []byte
	TreeConnectContexts       *requset_TREE_CONNECT_CONTEXT
}
type requset_TREE_CONNECT_CONTEXT struct {
	ContextType uint16
	DataLength  uint16
	Reserved    uint32
	Data        []byte
}

type AccessMask uint32

const (
	//2.2.13.1.1
	FILE_READ_DATA   AccessMask = 0x00000001
	FILE_WRITE_DATA  AccessMask = 0x00000002
	FILE_APPEND_DATA AccessMask = 0x00000004
	FILE_READ_EA     AccessMask = 0x00000008

	FILE_WRITE_EA        AccessMask = 0x00000010
	FILE_DELETE_CHILD    AccessMask = 0x00000040
	FILE_EXECUTE         AccessMask = 0x00000020
	FILE_READ_ATTRIBUTES AccessMask = 0x00000080

	FILE_WRITE_ATTRIBUTES AccessMask = 0x00000100

	DELETE       AccessMask = 0x00010000
	READ_CONTROL AccessMask = 0x00020000
	WRITE_DAC    AccessMask = 0x00040000
	WRITE_OWNER  AccessMask = 0x00080000

	SYNCHRONIZE AccessMask = 0x00100000

	ACCESS_SYSTEM_SECURITY AccessMask = 0x01000000
	MAXIMUM_ALLOWED        AccessMask = 0x02000000

	GENERIC_ALL     AccessMask = 0x10000000
	GENERIC_EXECUTE AccessMask = 0x20000000
	GENERIC_WRITE   AccessMask = 0x40000000
	GENERIC_READ    AccessMask = 0x80000000
)

var AllAccessMask = (FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_READ_EA |
	FILE_WRITE_EA | FILE_DELETE_CHILD | FILE_EXECUTE | FILE_READ_ATTRIBUTES |
	FILE_WRITE_ATTRIBUTES |
	DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER |
	SYNCHRONIZE)

var _ encoder.BinaryMarshallable = (AccessMask)(0)

func (c AccessMask) MarshalBinary(meta *encoder.Metadata) ([]byte, error) {
	return MarshalBinary(c, meta, c)
}
func (c AccessMask) UnmarshalBinary(data []byte, meta *encoder.Metadata) (interface{}, error) {
	return UnmarshalBinary(c, data, meta, c)
}

type TreeConnectResponse struct {
	Header
	StructureSize uint16
	ShareType     uint8
	Reserved      byte
	ShareFlags    uint32 //cache policy
	Capabilities  uint32
	Access_Mask   AccessMask //MaximalAccess
}

const (
	SMB2_TREE_CONNECT_FLAG_CLUSTER_RECONNECT uint16 = 1 << iota
	SMB2_TREE_CONNECT_FLAG_REDIRECT_TO_OWNER
	SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT
)

// var treeId uint32 = 0
var NamedPipeShareName = "IPC$"

func (data *TreeConnectRequest) ServerAction(ctx *DataCtx) (interface{}, error) {
	data.Header.Flags = SMB2_FLAGS_RESPONSE
	if data.TreeFlags&SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT == SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT {
		fmt.Printf("SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT")
	}
	path, err := encoder.FromUnicode(data.Path)
	if err != nil {
		return nil, err
	}

	if true {
		if path[0] != '\\' || path[1] != '\\' {
			panic(fmt.Errorf("invalid sdsdfsf '%s'", path))
		}
		path = path[2:]
		inx := strings.IndexByte(path, '\\')
		if inx < 0 {
			panic(fmt.Errorf("invalid sdsdfsf43"))
		}
		path = path[inx+1:]
		fmt.Printf("path: %v", path)
	}

	path = strings.ToUpper(path)
	fmt.Printf("path: %v", path)
	// tid := atomic.AddUint32(&ctx.s.treeId, 1)
	// tree := CreateTree(tid, path, "./")
	// ctx.s.addTrees([]*Tree{tree})

	SMB2_SHARE_TYPE_DISK := uint8(1)
	SMB2_SHARE_TYPE_PIPE := uint8(2)

	SMB2_SHAREFLAG_NO_CACHING := uint32(0x00000030)
	SMB2_SHAREFLAG_ACCESS_BASED_DIRECTORY_ENUM := uint32(0x00000800)

	Access_Mask := (FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_READ_EA |
		FILE_WRITE_EA | FILE_DELETE_CHILD | FILE_EXECUTE | FILE_READ_ATTRIBUTES |
		FILE_WRITE_ATTRIBUTES |
		DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER |
		SYNCHRONIZE | ACCESS_SYSTEM_SECURITY)

	ShareType := SMB2_SHARE_TYPE_DISK
	ShareFlags := SMB2_SHAREFLAG_NO_CACHING | SMB2_SHAREFLAG_ACCESS_BASED_DIRECTORY_ENUM

	if path == NamedPipeShareName {
		ShareFlags = SMB2_SHAREFLAG_NO_CACHING
		ShareType = SMB2_SHARE_TYPE_PIPE
	}

	anchor := ctx.session.GetAnchor(path)
	if anchor == nil {
		return ERR(data.Header, STATUS_NETWORK_NAME_DELETED)
	}
	data.Header.TreeID = anchor.tid
	data.Header.Status = StatusOk

	ctx.session.SetActiveAnchorKey(path)

	resp := TreeConnectResponse{
		Header:        data.Header,
		StructureSize: 0x0010,
		ShareType:     ShareType,
		ShareFlags:    ShareFlags,
		Access_Mask:   Access_Mask,
	}
	resp.Header.Status = StatusOk
	resp.Header.Signature = make([]byte, 16)
	resp.Header.Credits = 33
	return &resp, nil
}

func (requestSetUp2 *TreeConnectRequest) ClientAction(s *SessionC, negRes *TreeConnectResponse) error {
	return nil
}

func NewTreeConnectReq(s *SessionC, name string) (TreeConnectRequest, error) {
	header := s.newHeader(CommandTreeConnect)

	path := fmt.Sprintf("\\\\%s\\%s", s.options.Host, name)
	return TreeConnectRequest{
		Header:        header,
		StructureSize: 9,
		TreeFlags:     0,
		PathOffset:    0,
		PathLength:    0,
		Path:          encoder.ToUnicode(path),
	}, nil
}

func NewTreeConnectRes() (TreeConnectResponse, error) {
	return TreeConnectResponse{}, nil
}

func (s *SessionC) TreeConnect(name string) error {
	s.Debug("Sending TreeConnect request ["+name+"]", nil)

	req, err := NewTreeConnectReq(s, name)
	var res TreeConnectResponse
	if err = s.RPC(req, &res); err != nil {
		return err
	}

	if res.Header.Status != StatusOk {
		return errors.New("Failed to connect to tree: " + StatusMap[res.Header.Status])
	}
	s.trees[name] = res.Header.TreeID

	s.Debug("Completed TreeConnect ["+name+"]", nil)
	return nil
}
