package rest

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"time"
)

func NewRequestStream(timeout time.Time, req *Request) *RequestStream {
	return &RequestStream{
		Request: req,
		Timeout: timeout,
	}
}

func ReadRequestStream(r io.Reader) (*RequestStream, IErrorArgs) {
	// type
	req := RequestStream{
		Request: &Request{},
	}
	if err := ReadByte(r, "type", (*byte)(&req.Type)); err != nil {
		return nil, err
	}
	log.Println("TYPE", req.Type)
	if req.Type == RequestTypeIn || req.Type == RequestTypeOut {
		// id
		if err := ReadInt64(r, 2, "id", &req.ID); err != nil {
			return nil, err
		}
		log.Println("ID", req.ID)
		if req.Type == RequestTypeIn {
			// timeout
			var timeout int64
			if err := ReadInt64(r, 8, "timeout", &timeout); err != nil {
				return nil, err
			}
			req.Timeout = time.Unix(timeout, 0)
			log.Println("TIMEOUT", req.Type)
		}
	}
	// command
	if err := ReadString(r, 2, "command", &req.Command); err != nil {
		return nil, err
	}
	// data
	dataBuf, err := ReadBufSize(r, 8, "data")
	if err != nil {
		return nil, err
	}
	if len(dataBuf) > 0 {
		if err := json.Unmarshal(dataBuf, &req.Data); err != nil {
			return nil, ioError("data", err)
		}
	}
	// files count
	var filesCount int64
	if err := ReadInt64(r, 2, "files_count", &filesCount); err != nil {
		return nil, err
	}
	// files
	if filesCount > 0 {
		req.Files = make(RequestFiles)

	}
	for i := 0; i < int(filesCount); i++ {
		filePrefix := fmt.Sprintf("file[%v]", i)
		var fileName string
		if err := ReadString(r, 2, filePrefix+".name", &fileName); err != nil {
			return nil, err
		}
		var fileSize int64
		if err := ReadInt64(r, 8, filePrefix+".size", &fileSize); err != nil {
			return nil, err
		}
		if fileSize < 1024*1024 {
			// RAM
			fileData, err := ReadBuf(r, int(fileSize), filePrefix+".data")
			if err != nil {
				return nil, err
			}
			buf := NewReadCloserLen(
				io.NopCloser(bytes.NewBuffer(fileData)),
				int64(len(fileData)),
			)
			req.Files[fileName] = buf
		} else {
			// временной файл
			tmpF, err := NewTemporaryFile(fileSize, r)
			if err != nil {
				return nil, err
			}
			req.Files[fileName] = tmpF
		}
	}
	return &req, nil
}

////////////////////////////////////////

type RequestStream struct {
	ID      int64
	Timeout time.Time
	*Request
}

func (s *RequestStream) Write(w io.Writer) IErrorArgs {
	// type
	if err := WriteByte(w, byte(s.Type), "type"); err != nil {
		return err
	}
	if s.Type == RequestTypeIn || s.Type == RequestTypeOut {
		// id
		if err := WriteInt64(w, s.ID, 2, "id"); err != nil {
			return err
		}
		if s.Type == RequestTypeIn {
			// timeout
			if err := WriteInt64(w, s.Timeout.Unix(), 8, "timeout"); err != nil {
				return err
			}
		}
	}
	// command
	if err := WriteString(w, s.Command, "command", 2); err != nil {
		return err
	}
	// data
	if err := WriteBufSize(w, s.Data.JSON(), 8, "data"); err != nil {
		return err
	}
	// files count
	if err := WriteInt64(w, int64(len(s.Files)), 2, "files_count"); err != nil {
		return err
	}
	// files
	for name, file := range s.Files {
		prefix := fmt.Sprintf("file[%s]", name)
		// file name
		if err := WriteString(w, name, prefix+".name", 2); err != nil {
			return err
		}
		// file body size
		if err := WriteInt64(w, file.Len(), 8, prefix+".size"); err != nil {
			return err
		}
		// file body
		if _, err := io.Copy(w, file); err != nil {
			return ioError(prefix+".data", err)
		}
	}
	return nil
}