// Copyright (C) 2016 The Protocol Authors.

package protocol

import (
	"bytes"
	"encoding/binary"
	"encoding/hex"
	"io"
	"testing"
)

func TestVersion14Hello(t *testing.T) {
	// Tests that we can send and receive a version 0.14 hello message.

	expected := Hello{
		DeviceName:    "test device",
		ClientName:    "syncthing",
		ClientVersion: "v0.14.5",
	}
	msgBuf, err := expected.Marshal()
	if err != nil {
		t.Fatal(err)
	}

	hdrBuf := make([]byte, 6)
	binary.BigEndian.PutUint32(hdrBuf, HelloMessageMagic)
	binary.BigEndian.PutUint16(hdrBuf[4:], uint16(len(msgBuf)))

	outBuf := new(bytes.Buffer)
	outBuf.Write(hdrBuf)
	outBuf.Write(msgBuf)

	inBuf := new(bytes.Buffer)

	conn := &readWriter{outBuf, inBuf}

	send := &Hello{
		DeviceName:    "this device",
		ClientName:    "other client",
		ClientVersion: "v0.14.6",
	}

	res, err := ExchangeHello(conn, send)
	if err != nil {
		t.Fatal(err)
	}

	if res.ClientName != expected.ClientName {
		t.Errorf("incorrect ClientName %q != expected %q", res.ClientName, expected.ClientName)
	}
	if res.ClientVersion != expected.ClientVersion {
		t.Errorf("incorrect ClientVersion %q != expected %q", res.ClientVersion, expected.ClientVersion)
	}
	if res.DeviceName != expected.DeviceName {
		t.Errorf("incorrect DeviceName %q != expected %q", res.DeviceName, expected.DeviceName)
	}
}

func TestOldHelloMsgs(t *testing.T) {
	// Tests that we can correctly identify old/missing/unknown hello
	// messages.

	cases := []struct {
		msg string
		err error
	}{
		{"00010001", ErrTooOldVersion}, // v12
		{"9F79BC40", ErrTooOldVersion}, // v13
		{"12345678", ErrUnknownMagic},
	}

	for _, tc := range cases {
		msg, _ := hex.DecodeString(tc.msg)

		outBuf := new(bytes.Buffer)
		outBuf.Write(msg)

		inBuf := new(bytes.Buffer)

		conn := &readWriter{outBuf, inBuf}

		send := &Hello{
			DeviceName:    "this device",
			ClientName:    "other client",
			ClientVersion: "v1.0.0",
		}

		_, err := ExchangeHello(conn, send)
		if err != tc.err {
			t.Errorf("unexpected error %v != %v", err, tc.err)
		}
	}
}

type readWriter struct {
	r io.Reader
	w io.Writer
}

func (rw *readWriter) Write(data []byte) (int, error) {
	return rw.w.Write(data)
}

func (rw *readWriter) Read(data []byte) (int, error) {
	return rw.r.Read(data)
}
