package protocol

import "io"
import "fmt"

// Status is an 8-bit code that routers and services can send to each-other to
// inform the other about issues (or lack thereof) with the last recieved
// message in a given sequence. This is not to be confused with an HTTP status
// code.
type Status uint8; const (
	StatusOk Status = iota
	StatusBadMessageType
	StatusMalformedMessage
	StatusBadVersion
	StatusBadCredentials
	StatusBadMount
)

// ReadStatus reads a Status code from a Reader.
func ReadStatus (reader io.Reader) (status Status, err error) {
	untyped, err := ReadU8(reader)
	return Status(untyped), err
}

// Serialize writes a Status code to a Writer.
func (status Status) Serialize (writer io.Writer) error {
	return WriteU8(writer, uint8(status))
}

// String returns a human-readable representation of a status code.
func (status Status) String () string {
	switch status {
	case StatusOk:               return "ok"
	case StatusBadMessageType:   return "invalid message type"
	case StatusMalformedMessage: return "malformed message"
	case StatusBadVersion:       return "unsupported protocol version"
	case StatusBadCredentials:   return "invalid credentials"
	case StatusBadMount:         return "invalid mount"
	}
	return fmt.Sprintf("Status(%d)", status)
}

// Error is equivalent to String. This lets a status be returned as an error.
func (status Status) Error () string {
	return status.String()
}

// Version represents a Hnakra protocol version.
type Version struct {
	Major uint8
	Minor uint8
}

// ReadVersion reads version information from a Reader.
func ReadVersion (reader io.Reader) (version Version, err error) {
	version.Major, err = ReadU8(reader)
	if err != nil { return }
	version.Minor, err = ReadU8(reader)
	return
}

// Serialize writes version information to a Writer.
func (version Version) Serialize (writer io.Writer) error {
	err := WriteU8(writer, uint8(version.Major))
	if err != nil { return err }
	return WriteU8(writer, uint8(version.Minor))
}

// String returns a human-readable representation of a version.
func (version Version) String () string {
	return fmt.Sprintf("%d.%d", version.Major, version.Minor)
}

// MessageLogin is a message sent to a router by a service requesting to log in
// and mount on a path. The service should not send anything before this
// message. The router must reply with a MessageStatus containing StatusOk if
// the login and mount suceeded. or some other code if it did not. While waiting
// for this message to be sent, the service should not send anything.
type MessageLogin struct {
	ID
	Version

	User string
	Key []byte
	Name string
	Description string
	
	Scheme string
	Host   string
	Path   string
}

// ReadMessageLogin reads a login message from a Reader, starting after the type
// code.
func ReadMessageLogin (reader io.Reader) (message MessageLogin, err error) {
	message.ID, err = ReadID(reader)
	if err != nil { return }
	message.Version, err = ReadVersion(reader)
	if err != nil { return }
	message.User, err = ReadString8(reader)
	if err != nil { return }
	message.Key, err = ReadBuf8(reader)
	if err != nil { return }
	message.Name, err = ReadString8(reader)
	if err != nil { return }
	message.Description, err = ReadString8(reader)
	if err != nil { return }
	message.Scheme, err = ReadString8(reader)
	if err != nil { return }
	message.Host, err = ReadString16(reader)
	if err != nil { return }
	message.Path, err = ReadString16(reader)
	return
}

// Serialize writes the login message to a Writer, starting after the type code.
func (message MessageLogin) Serialize (writer io.Writer) (err error) {
	err = message.ID.Serialize(writer)
	if err != nil { return }
	err = message.Version.Serialize(writer)
	if err != nil { return }
	err = WriteString8(writer, message.User)
	if err != nil { return }
	err = WriteBuf8(writer, message.Key)
	if err != nil { return }
	err = WriteString8(writer, message.Name)
	if err != nil { return }
	err = WriteString8(writer, message.Description)
	if err != nil { return }
	err = WriteString8(writer, message.Scheme)
	if err != nil { return }
	err = WriteString16(writer, message.Host)
	if err != nil { return }
	err = WriteString16(writer, message.Path)
	return
}

// Send writes the type code and the message to a Writer.
func (message MessageLogin) Send (writer io.Writer) (err error) {
	err = TypeLogin.Serialize(writer)
	if err != nil { return }
	return message.Serialize(writer)
}

// MessageStatus is a message that carries a Status code. If sent, it must be in
// reply to the last recieved message with an equivalent ID.
type MessageStatus struct {
	ID
	Status
}

// ReadMessageStatus reads a status message from a Reader, starting after the
// type code.
func ReadMessageStatus (reader io.Reader) (message MessageStatus, err error) {
	message.ID, err = ReadID(reader)
	if err != nil { return }
	message.Status, err = ReadStatus(reader)
	return
}

// Serialize writes the status message to a Writer, starting after the type
// code.
func (message MessageStatus) Serialize (writer io.Writer) (err error) {
	err = message.ID.Serialize(writer)
	if err != nil { return }
	return message.Status.Serialize(writer)
}

// Send writes the type code and the message to a Writer.
func (message MessageStatus) Send (writer io.Writer) (err error) {
	err = TypeStatus.Serialize(writer)
	if err != nil { return }
	return message.Serialize(writer)
}
