package weed_server

import (
	"bufio"
	"fmt"
	"io"
	"net"
	"strings"

	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/storage/needle"
	"github.com/chrislusf/seaweedfs/weed/util"
)

func (vs *VolumeServer) HandleTcpConnection(c net.Conn) {
	defer c.Close()

	glog.V(0).Infof("Serving writes from %s", c.RemoteAddr().String())

	bufReader := bufio.NewReaderSize(c, 1024*1024)
	bufWriter := bufio.NewWriterSize(c, 1024*1024)

	for {
		cmd, err := bufReader.ReadString('\n')
		if err != nil {
			if err != io.EOF {
				glog.Errorf("read command from %s: %v", c.RemoteAddr().String(), err)
			}
			return
		}
		cmd = cmd[:len(cmd)-1]
		switch cmd[0] {
		case '+':
			fileId := cmd[1:]
			err = vs.handleTcpPut(fileId, bufReader)
			if err == nil {
				bufWriter.Write([]byte("+OK\n"))
			} else {
				bufWriter.Write([]byte("-ERR " + string(err.Error()) + "\n"))
			}
		case '-':
			fileId := cmd[1:]
			err = vs.handleTcpDelete(fileId)
			if err == nil {
				bufWriter.Write([]byte("+OK\n"))
			} else {
				bufWriter.Write([]byte("-ERR " + string(err.Error()) + "\n"))
			}
		case '?':
			fileId := cmd[1:]
			err = vs.handleTcpGet(fileId, bufWriter)
		case '!':
			bufWriter.Flush()
		}

	}

}

func (vs *VolumeServer) handleTcpGet(fileId string, writer *bufio.Writer) (err error) {

	volumeId, n, err2 := vs.parseFileId(fileId)
	if err2 != nil {
		return err2
	}

	volume := vs.store.GetVolume(volumeId)
	if volume == nil {
		return fmt.Errorf("volume %d not found", volumeId)
	}

	err = volume.StreamRead(n, writer)
	if err != nil {
		return err
	}

	return nil
}

func (vs *VolumeServer) handleTcpPut(fileId string, bufReader *bufio.Reader) (err error) {

	volumeId, n, err2 := vs.parseFileId(fileId)
	if err2 != nil {
		return err2
	}

	volume := vs.store.GetVolume(volumeId)
	if volume == nil {
		return fmt.Errorf("volume %d not found", volumeId)
	}

	sizeBuf := make([]byte, 4)
	if _, err = bufReader.Read(sizeBuf); err != nil {
		return err
	}
	dataSize := util.BytesToUint32(sizeBuf)

	err = volume.StreamWrite(n, bufReader, dataSize)
	if err != nil {
		return err
	}

	return nil
}

func (vs *VolumeServer) handleTcpDelete(fileId string) (err error) {

	volumeId, n, err2 := vs.parseFileId(fileId)
	if err2 != nil {
		return err2
	}

	_, err = vs.store.DeleteVolumeNeedle(volumeId, n)
	if err != nil {
		return err
	}

	return nil
}

func (vs *VolumeServer) parseFileId(fileId string) (needle.VolumeId, *needle.Needle, error) {

	commaIndex := strings.LastIndex(fileId, ",")
	if commaIndex <= 0 {
		return 0, nil, fmt.Errorf("unknown fileId %s", fileId)
	}

	vid, fid := fileId[0:commaIndex], fileId[commaIndex+1:]

	volumeId, ve := needle.NewVolumeId(vid)
	if ve != nil {
		return 0, nil, fmt.Errorf("unknown volume id in fileId %s", fileId)
	}

	n := new(needle.Needle)
	n.ParsePath(fid)
	return volumeId, n, nil
}
