// Package protocol defines what is sent accross the TCP connection between the
// router and service. It provides primitives for serializing various integer
// types and data structures, as well as entire messages for the protocols it
// supports.
package protocol

import "io"
import "fmt"
import "sync"

// Message represents a message that can be sent along an io.Writer, and read
// back when it comes out the other end. All messages must prefix themselves
// with a Type code.
type Message interface {
	// Send serializes the message to a writer, prefixed with its Type code.
	Send (io.Writer) error
}

// Serializer represents anything who's *content* can be serialized to a byte
// stream. This does not include type information (such as a message Type code
// prefix).
type Serializer interface {
	// Serialize serializes the object's content to a writer.
	Serialize (io.Writer) error
}

// ID is a number that identifies an ongoing sequence. It is similar to a cookie
// in the X11 protocol. All messages that are a part of the same sequence have
// the same ID. ID zero is the initial ID which is generated by the service upon
// sending a MessageLogin to the router. The corresponding sequence must always
// be treated as active as long as the connection is open, and must never be
// reused for something else.
//
// If the leftmost bit of an ID is set, that means it was generated by
// the router. If the leftmost bit of an ID is unset, that means it was
// generated by the service. When generating IDs, the service will start at 1
// and progressively increment the ID number. If the next ID would have its
// leftmost bit set, it must loop back around to 1. Similarly, the router must
// start at the maximum unsigned 32 bit integer (0xFFFFFFFF) and decrement until
// the leftmost bit of the ID would be unset, and then loop back around to the
// initial value.
//
// If either router or service generate an ID that is currently
// in use by a sequence, that ID must be skipped over. For this reason, both
// router and service must maintain a list of ongoing sequences along with their
// active IDs. These lists (and corresponding generators) must be
// connection-specific.
//
// Sequences are implicitly started by a message of the first type in any
// particular protocol sector (see the documentation for Type in this package),
// and must be sent with a newly generated ID as described above. The sequence
// must consist only of messages that are part of the same protocol sector as
// the message that started it (i.e. you cannot send a MessageGeminiResponse in
// a sequence that was started by a MessageHTTPRequest). The only exception to
// this is MessageStatus, which can be sent in any context. If this rule is
// violated in any other capacity, the router or service should send a
// MessageStatus with StatusBadMessageType and close the connection.
//
// The termination of any given sequence is determined by the rules of the
// particular protocol sector it is a part of.
type ID uint32

const RouterIDStart  ID = 0xFFFFFFFF
const ServiceIDStart ID = 0

// ReadID reads an ID from a Reader.
func ReadID (reader io.Reader) (id ID, err error) {
	untyped, err := ReadU32(reader)
	return ID(untyped), err
}

// Serialize writes an ID to a Writer.
func (id ID) Serialize (writer io.Writer) error {
	return WriteU32(writer, uint32(id))
}

// IsRouter returns true if this ID was generated by a router.
func (id ID) IsRouter () bool {
	return id >> 31 == 1
}

// IsService returns true if this ID was generated by a service.
func (id ID) IsService () bool {
	return !id.IsRouter()
}

// IDFactory generates IDs and keeps track of which ones are active. Its methods
// can be called concurrently.
type IDFactory struct {
	lock sync.Mutex

	next   ID
	router bool
	active map[ID] struct { }
}

// NewRouterIDFactory returns a new IDFactory that generates router IDs.
func NewRouterIDFactory () (factory *IDFactory) {
	factory = new(IDFactory)
	factory.active = make(map[ID] struct { })
	factory.router = true
	factory.next   = RouterIDStart
	return
}

// NewServiceIDFactory returns a new IDFactory that generates service IDs.
func NewServiceIDFactory () (factory *IDFactory) {
	factory = new(IDFactory)
	factory.active = make(map[ID] struct { })
	factory.next   = ServiceIDStart
	return
}

// Next generates a new ID and returns it. The ID will be marked as active, and
// a call to Free() is required to mark it as inactive when its corresponding
// sequence has completed.
func (factory *IDFactory) Next () ID {
	// FIXME: this is not the best way of doing it. it is unlikely that this
	// will ever cause problems, but the possibility is there.

	factory.lock.Lock()
	defer factory.lock.Unlock()
	
	id := factory.next
	if factory.router {
		next := id - 1
		for factory.isActive(id) {
			next --
			if next.IsService() {
				next = RouterIDStart
			}
		}
		factory.next = next 
	} else {
		next := id + 1
		for factory.isActive(id) {
			next ++
			if next.IsRouter() {
				next = ServiceIDStart
			}
		}
		factory.next = next
	}
	return id
}

// Free marks an ID as inactive. Note that calling Free(0) is in violation of
// the protocol.
func (factory *IDFactory) Free (id ID) {
	factory.lock.Lock()
	defer factory.lock.Unlock()
	
	delete(factory.active, id)
}

func (factory *IDFactory) isActive (id ID) bool {
	_, active := factory.active[id]
	return active
}

// Type is an 8-bit code that comes before each message, and determines which
// kind of message it is. The 256 type code values are divided into eight
// 32-code sectors, where each sector corresponds to a different protocol.
type Type uint8; const (
	// Initial sector
	TypeNone          Type = iota + 0x00
	TypeLogin
	TypeStatus

	// HTTP/HTTPS protocol sector
	TypeHTTPRequest   Type = iota + 0x20
	TypeHTTPResponse
	TypeHTTPBodyRequest
	TypeHTTPBodySegment
	TypeHTTPBodyEnd

	// Gemini protocol sector TODO implement
	TypeGeminiRequest Type = iota + 0x40
	TypeGeminiResponse
	TypeGeminiBodySegment
	TypeGeminiBodyEnd

	// Database access sector TODO implement
	TypeDatabaseGet   Type = iota + 0x40
	TypeDatabaseSet
	TypeDatabaseValue
)

// ReadType reads a Type code from a Reader.
func ReadType (reader io.Reader) (ty Type, err error) {
	untyped, err := ReadU8(reader)
	return Type(untyped), err
}

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

// String returns the name of the type as a string.
func (ty Type) String () string {
	switch ty {
	case TypeLogin:             return "TypeLogin"
	case TypeStatus:            return "TypeStatus"
	case TypeHTTPRequest:       return "TypeHTTPRequest"
	case TypeHTTPResponse:      return "TypeHTTPResponse"
	case TypeHTTPBodyRequest:   return "TypeHTTPBodyRequest"
	case TypeHTTPBodySegment:   return "TypeHTTPBodySegment"
	case TypeHTTPBodyEnd:       return "TypeHTTPBodyEnd"
	case TypeGeminiRequest:     return "TypeGeminiRequest"
	case TypeGeminiResponse:    return "TypeGeminiResponse"
	case TypeGeminiBodySegment: return "TypeGeminiBodySegment"
	case TypeGeminiBodyEnd:     return "TypeGeminiBodyEnd"
	}
	return fmt.Sprintf("Type(%d)", ty)
}

// ReadMessage reads a Type code and its corresponding message from a Reader.
// The type of message can be determined using a type switch.
func ReadMessage (reader io.Reader) (Message, error) {
	ty, err := ReadType(reader)
	if err != nil { return nil, err }
	switch ty {
	case TypeLogin:             return ReadMessageLogin(reader)
	case TypeStatus:            return ReadMessageStatus(reader)
	case TypeHTTPRequest:       return ReadMessageHTTPRequest(reader)
	case TypeHTTPResponse:      return ReadMessageHTTPResponse(reader)
	case TypeHTTPBodyRequest:   return ReadMessageHTTPBodyRequest(reader)
	case TypeHTTPBodySegment:   return ReadMessageHTTPBodySegment(reader)
	case TypeHTTPBodyEnd:       return ReadMessageHTTPBodyEnd(reader)
	case TypeGeminiRequest:     return ReadMessageGeminiRequest(reader)
	case TypeGeminiResponse:    return ReadMessageGeminiResponse(reader)
	case TypeGeminiBodySegment: return ReadMessageGeminiBodySegment(reader)
	case TypeGeminiBodyEnd:     return ReadMessageGeminiBodyEnd(reader)
	}
	return nil, StatusBadMessageType
}
