package serialize

import (
	"fmt"
	"io"

	"code.justin.tv/rhys/nursery/serialize/protobuf"
	"github.com/golang/protobuf/proto"
)

func toProtobuf(out *protobuf.Msg, in *talGolden) error {
	out.Reset()
	kind := protobuf.Msg_Kind(in.Kind)
	out.Kind = &kind
	out.Time = proto.Uint64(in.Time)
	out.Service = proto.String(in.Service)
	out.Machine = proto.String(in.Machine)
	out.Pid = proto.Int32(int32(in.PID))
	out.Traceid = proto.Uint64(in.TraceID)
	out.Subtraceid = proto.String(in.SubTraceID)
	for k, v := range in.Desc {
		desc := &protobuf.Msg_Desc{
			Key: proto.String(k),
			Val: proto.String(v),
		}
		out.Desc = append(out.Desc, desc)
	}
	return nil
}

func fromProtobuf(out *talGolden, in *protobuf.Msg) error {
	*out = talGolden{
		Kind:       uint(in.GetKind()),
		Time:       in.GetTime(),
		Service:    in.GetService(),
		Machine:    in.GetMachine(),
		PID:        int(in.GetPid()),
		TraceID:    in.GetTraceid(),
		SubTraceID: in.GetSubtraceid(),
	}
	out.Desc = make(map[string]string, len(in.Desc))
	for _, desc := range in.Desc {
		out.Desc[desc.GetKey()] = desc.GetVal()
	}
	return nil
}

func getProtobufTraceid(data []byte) (uint64, error) {
	const (
		varint  = 0
		fixed64 = 1
		length  = 2
		fixed32 = 5
	)

	const (
		traceidField = 6
	)

	var traceid uint64
	buf := proto.NewBuffer(data)
	for {
		x, err := buf.DecodeVarint()
		if err == io.EOF || err == io.ErrUnexpectedEOF {
			return traceid, nil
		} else if err != nil {
			return traceid, err
		}
		switch field, wire := x>>3, x&0x07; wire {
		case varint:
			_, err := buf.DecodeVarint()
			if err != nil {
				return traceid, err
			}
		case fixed64:
			val, err := buf.DecodeFixed64()
			if err != nil {
				return traceid, err
			}
			if field == traceidField {
				traceid = val
				// We can't return yet, protobuf spec says we honor the last
				// matching value.
			}
		case length:
			_, err := buf.DecodeRawBytes(false)
			if err != nil {
				return traceid, err
			}
		case fixed32:
			_, err := buf.DecodeFixed32()
			if err != nil {
				return traceid, err
			}
		default:
			return traceid, fmt.Errorf("unknown wire type %d", wire)
		}
	}
}
