package proto

import (
	
	
	
)

// Message identifiers are coupled to message creation time.
//
// https://core.telegram.org/mtproto/description#message-identifier-msg-id

const (
	yieldClient         = 0
	yieldServerResponse = 1
	yieldFromServer     = 3

	messageIDModulo = 4
)

func ( int64,  int) int64 {
	const  = 1e9
	// Must approximately equal unixtime*2^32.

	// Important: to counter replay-attacks the lower 32 bits of msg_id
	// passed by the client must not be empty and must present a
	// fractional part of the time point when the message was created.
	 :=  / 
	 :=  % 

	// Ensure that fracPart % 4 == 0.
	 &= -messageIDModulo
	// Adding modulo 4 yield to ensure message type.
	 += int64()

	return ( << 32) | 
}

// MessageID represents 64-bit message id.
type MessageID int64

func ( MessageID) () string {
	return fmt.Sprintf("%x (%s, %s)",
		int64(), .Type(), .Time().Format(time.RFC3339),
	)
}

// MessageType is type of message determined by message id.
//
// A message is rejected over 300 seconds after it is created or
// 30 seconds before it is created (this is needed to protect from replay attacks).
//
// The identifier of a message container must be strictly greater than those of
// its nested messages.
type MessageType byte

const (
	// MessageUnknown reports that message id has unknown time and probably
	// should be ignored.
	MessageUnknown MessageType = iota
	// MessageFromClient is client message identifiers.
	MessageFromClient
	// MessageServerResponse is a response to a client message.
	MessageServerResponse
	// MessageFromServer is a message from the server.
	MessageFromServer
)

func ( MessageType) () string {
	switch  {
	case MessageFromClient:
		return "FromClient"
	case MessageServerResponse:
		return "ServerResponse"
	case MessageFromServer:
		return "FromServer"
	default:
		return "Unknown"
	}
}

// Time returns approximate time when MessageID were generated.
func ( MessageID) () time.Time {
	 := int64() >> 32
	 := int64(int32())
	return time.Unix(, ).UTC()
}

// Type returns message type.
func ( MessageID) () MessageType {
	switch  % messageIDModulo {
	case yieldClient:
		return MessageFromClient
	case yieldServerResponse:
		return MessageServerResponse
	case yieldFromServer:
		return MessageFromServer
	default:
		return MessageUnknown
	}
}

// NewMessageID returns new message id for provided time and type.
func ( time.Time,  MessageType) MessageID {
	return NewMessageIDNano(.UnixNano(), )
}

// NewMessageIDNano returns new message id for provided current unix
// nanoseconds and type.
func ( int64,  MessageType) MessageID {
	var  int
	switch  {
	case MessageFromClient:
		 = yieldClient
	case MessageFromServer:
		 = yieldFromServer
	case MessageServerResponse:
		 = yieldServerResponse
	default:
		 = yieldClient
	}
	return MessageID(newMessageID(, ))
}

// MessageIDGen is message id generator that provides collision prevention.
//
// The main reason of such structure is that now() can return same time during
// multiple calls and that leads to duplicate message id.
type MessageIDGen struct {
	mux  sync.Mutex
	nano int64
	now  func() time.Time
}

// New generates new message id for provided type, protecting from collisions
// that are caused by low system time resolution.
func ( *MessageIDGen) ( MessageType) int64 {
	.mux.Lock()
	defer .mux.Unlock()

	// Minimum resolution is required because id is only approximately
	// equal to unix nano time, some part is replaced by message type.
	const  = 10

	 := .now().UnixNano()
	if  > .nano {
		.nano = 
	} else {
		.nano += 
	}

	return int64(NewMessageIDNano(.nano, ))
}

// NewMessageIDGen creates new message id generator.
//
// Current time will be provided by now() function.
//
// This generator compensates time resolution problem removing
// probability of id collision.
//
// Such problem can be observed for relatively high RPS, sequential calls to
// time.Now() will return same time which leads to equal ids.
func ( func() time.Time) *MessageIDGen {
	return &MessageIDGen{
		now: ,
	}
}

// MessageIDBuf stores last N message ids and is used in replay attack mitigation.
type MessageIDBuf struct {
	mux sync.Mutex
	buf []int64
}

// NewMessageIDBuf initializes new message id buffer for last N stored values.
func ( int) *MessageIDBuf {
	return &MessageIDBuf{
		buf: make([]int64, ),
	}
}

// Consume returns false if message should be discarded.
func ( *MessageIDBuf) ( int64) bool {
	// In addition, the identifiers (msg_id) of the last N messages received
	// from the other side must be stored, and if a message comes in with an
	// msg_id lower than all or equal to any of the stored values, that message
	// is to be ignored. Otherwise, the new message msg_id is added to the set,
	// and, if the number of stored msg_id values is greater than N, the oldest
	// (i. e. the lowest) is discarded.
	//
	// https://core.telegram.org/mtproto/security_guidelines#checking-msg-id

	.mux.Lock()
	defer .mux.Unlock()

	var (
		 int
		  int64
	)
	for ,  := range .buf {
		if  ==  {
			// Equal to stored value.
			return false
		}
		// Searching for minimum value.
		if  <  {
			 = 
			 = 
		}
	}
	if  <  {
		// Lower than all stored values.
		return false
	}

	// Message is accepted. Replacing lowest message id with new id.
	.buf[] = 
	return true
}