package wire

import (
	"bytes"
	"io"
	"testing"
	"time"
)

// TestNetAddressV2FromBytes tests that NetAddressV2FromBytes works as
// expected.
func TestNetAddressV2FromBytes(t *testing.T) {
	tests := []struct {
		addrBytes       []byte
		expectedString  string
		expectedNetwork string
	}{
		// Ipv4 encoding
		{
			[]byte{0x7f, 0x00, 0x00, 0x01},
			"127.0.0.1",
			string(ipv4),
		},

		// Ipv6 encoding
		{
			[]byte{
				0x20, 0x01, 0x09, 0xe8, 0x26, 0x15, 0x73, 0x00,
				0x09, 0x54, 0x12, 0x63, 0xef, 0xc8, 0x2e, 0x34,
			},
			"2001:9e8:2615:7300:954:1263:efc8:2e34",
			string(ipv6),
		},

		// OnionCat encoding
		{
			[]byte{
				0xfd, 0x87, 0xd8, 0x7e, 0xeb, 0x43, 0xff, 0xfe,
				0xcc, 0x39, 0xa8, 0x73, 0x69, 0x15, 0xff, 0xff,
			},
			"777myonionurl777.onion",
			string(torv2),
		},

		// Torv2 encoding
		{
			[]byte{
				0xff, 0xfe, 0xcc, 0x39, 0xa8, 0x73, 0x69, 0x15,
				0xff, 0xff,
			},
			"777myonionurl777.onion",
			string(torv2),
		},

		// Torv3 encoding
		{
			[]byte{
				0xca, 0xd2, 0xd3, 0xc8, 0xdc, 0x9c, 0xc4, 0xd3,
				0x70, 0x33, 0x30, 0xc5, 0x23, 0xaf, 0x02, 0xed,
				0xc4, 0x9d, 0xf8, 0xc6, 0xb0, 0x4e, 0x74, 0x6d,
				0x3b, 0x51, 0x57, 0xa7, 0x15, 0xfe, 0x98, 0x35,
			},
			"zljnhsg4ttcng4btgdcshlyc5xcj36ggwbhhi3j3kfl2ofp6ta26jlid.onion",
			string(torv3),
		},
	}

	t.Logf("Running %d tests", len(tests))
	for i, test := range tests {
		na := NetAddressV2FromBytes(time.Time{}, 0, test.addrBytes, 0)

		if test.expectedNetwork != string(torv3) {
			if na.ToLegacy() == nil {
				t.Errorf("Test #%d has nil legacy encoding", i)
			}
		} else {
			if !na.IsTorV3() {
				t.Errorf("Test #%d is not torv3 address", i)
			}
		}

		if na.Addr.String() != test.expectedString {
			t.Errorf("Test #%d did not match expected string", i)
		}

		if na.Addr.Network() != test.expectedNetwork {
			t.Errorf("Test #%d did not match expected network", i)
		}

		var b bytes.Buffer
		if err := writeNetAddressV2(&b, 0, na); err != nil {
			t.Errorf("Test #%d failed writing address %v", i, err)
		}

		// Assert that the written netID is equivalent to the above.
		if string(b.Bytes()[5]) != test.expectedNetwork {
			t.Errorf("Test #%d did not match expected network", i)
		}
	}
}

// TestReadNetAddressV2 tests that readNetAddressV2 behaves as expected in
// different scenarios.
func TestReadNetAddressV2(t *testing.T) {
	tests := []struct {
		buf             []byte
		expectedNetwork string
		expectedError   error
	}{
		// Invalid address size for unknown netID.
		{
			[]byte{
				0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfd, 0xff,
				0xff,
			},
			"",
			ErrInvalidAddressSize,
		},

		// Valid address size for unknown netID.
		{
			[]byte{
				0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x20, 0x10,
				0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
				0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
				0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
				0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x22,
				0x22,
			},
			"",
			ErrSkippedNetworkID,
		},

		// Invalid ipv4 size.
		{
			[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x05},
			"",
			ErrInvalidAddressSize,
		},

		// Valid ipv4 encoding.
		{
			[]byte{
				0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x7f,
				0x00, 0x00, 0x01, 0x22, 0x22,
			},
			string(ipv4),
			nil,
		},

		// Invalid ipv6 size.
		{
			[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xfc},
			"",
			ErrInvalidAddressSize,
		},

		// OnionCat encoding is skipped.
		{
			[]byte{
				0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0xfd,
				0x87, 0xd8, 0x7e, 0xeb, 0x43, 0xff, 0xfe, 0xcc,
				0x39, 0xa8, 0x73, 0x69, 0x15, 0xff, 0xff, 0x22,
				0x22,
			},
			"",
			ErrSkippedNetworkID,
		},

		// Valid ipv6 encoding.
		{
			[]byte{
				0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0xff,
				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
				0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x22,
				0x22,
			},
			string(ipv6),
			nil,
		},

		// Invalid torv2 size.
		{
			[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02},
			"",
			ErrInvalidAddressSize,
		},

		// Valid torv2 encoding.
		{
			[]byte{
				0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0a, 0x20,
				0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
				0x20, 0x22, 0x22,
			},
			string(torv2),
			nil,
		},

		// Invalid torv3 size.
		{
			[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x02},
			"",
			ErrInvalidAddressSize,
		},

		// Valid torv3 encoding.
		{
			[]byte{
				0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x20, 0x10,
				0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
				0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
				0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
				0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x22,
				0x22,
			},
			string(torv3),
			nil,
		},

		// Invalid i2p size.
		{
			[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x02},
			"",
			ErrInvalidAddressSize,
		},

		// Valid i2p encoding.
		{
			[]byte{
				0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x10,
				0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
				0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
				0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
				0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x22,
				0x22,
			},
			string(i2p),
			ErrSkippedNetworkID,
		},

		// Invalid cjdns size.
		{
			[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x02},
			"",
			ErrInvalidAddressSize,
		},

		// Valid cjdns encoding.
		{
			[]byte{
				0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x10, 0x20,
				0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
				0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22,
				0x22,
			},
			string(cjdns),
			ErrSkippedNetworkID,
		},
	}

	t.Logf("Running %d tests", len(tests))
	for i, test := range tests {
		r := bytes.NewReader(test.buf)
		na := &NetAddressV2{}

		err := readNetAddressV2(r, 0, na)
		if err != test.expectedError {
			t.Errorf("Test #%d had unexpected error %v", i, err)
		} else if err != nil {
			continue
		}

		// Trying to read more should give EOF.
		var b [1]byte
		if _, err := r.Read(b[:]); err != io.EOF {
			t.Errorf("Test #%d did not cleanly finish reading", i)
		}

		if na.Addr.Network() != test.expectedNetwork {
			t.Errorf("Test #%d had unexpected network %v", i,
				na.Addr.Network())
		}
	}
}
