package jwt

import (
	"encoding/base64"
	"encoding/json"
	"io"
)

var enc = base64.RawURLEncoding

//Encode writes a JWT encoded value to w with specified Algorithm.
//The same Algorithm MUST be used to validate this class of JWT.
//The algorithm field cannot be trusted and can be used to stage exploits.
func Encode(header, claims interface{}, a Algorithm) (jwt []byte, err error) {
	var bHeader, bClaims []byte
	if bHeader, err = json.Marshal(header); err != nil {
		return
	}

	if bClaims, err = json.Marshal(claims); err != nil {
		return
	}

	var (
		hb64l = enc.EncodedLen(len(bHeader))
		cb64l = enc.EncodedLen(len(bClaims))
		sb64l = enc.EncodedLen(a.Size())
	)

	jwt = make([]byte, hb64l+cb64l+sb64l+2) // base64 plus two dots

	// write header
	enc.Encode(jwt[:hb64l], bHeader)

	//write dot
	jwt[hb64l] = '.'

	//write claims
	enc.Encode(jwt[hb64l+1:cb64l+hb64l+1], bClaims)

	//write dot
	jwt[cb64l+hb64l+1] = '.'

	//no signature
	if sb64l == 0 {
		return
	}

	//write signature to the end
	var bSig []byte
	if bSig, err = a.Sign(jwt[:cb64l+hb64l+1]); err != nil {
		return
	}

	enc.Encode(jwt[hb64l+cb64l+2:hb64l+cb64l+sb64l+2], bSig)

	return
}

//Write writes a JWT encoded value to w with specified Algorithm.
//The same Algorithm MUST be used to validate this class of JWT.
//The algorithm field cannot be trusted and can be used to stage exploits.
func Write(header, claims interface{}, a Algorithm, w io.Writer) (n int, err error) {
	var bt []byte
	if bt, err = Encode(header, claims, a); err != nil {
		return
	}

	return w.Write(bt)
}
