package api

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

type WebConnection struct {
	rw io.ReadWriteCloser
}

func (c *WebConnection) ReadText() (string, error) {
	header := make([]byte, 2)
	_, err := io.ReadFull(c.rw, header)
	if err != nil {
		return "", err
	}

	fin := header[0]&0x80 != 0
	opcode := header[0] & 0x0F
	if opcode == 0x8 { // close frame
		return "", io.EOF
	}
	if !fin || opcode != 1 {
		return "", fmt.Errorf("unsupported WS frame")
	}

	mask := header[1]&0x80 != 0
	payloadLen := int(header[1] & 0x7F)

	if payloadLen == 126 {
		ext := make([]byte, 2)
		io.ReadFull(c.rw, ext)
		payloadLen = int(ext[0])<<8 | int(ext[1])
	}

	maskKey := make([]byte, 4)
	if mask {
		io.ReadFull(c.rw, maskKey)
	}

	payload := make([]byte, payloadLen)
	io.ReadFull(c.rw, payload)

	if mask {
		for i := 0; i < payloadLen; i++ {
			payload[i] ^= maskKey[i%4]
		}
	}

	return string(payload), nil
}

func (c *WebConnection) WriteText(s string) error {
	payload := []byte(s)
	header := []byte{0x81} // FIN + text frame

	if len(payload) < 126 {
		header = append(header, byte(len(payload)))
	} else {
		header = append(header, 126, byte(len(payload)>>8), byte(len(payload)))
	}

	_, err := c.rw.Write(append(header, payload...))
	return err
}

func (c *WebConnection) WriteMsg(fn string, args ...string) error {
	b, err := json.Marshal(Msg{
		Fn:   fn,
		Args: args,
	})
	if err != nil {
		panic(err)
	}
	return c.WriteText(string(b))
}

func (c *WebConnection) ReadMsg() (*Msg, error) {
	msg, err := c.ReadText()
	if err != nil {
		return nil, err
	}
	m := &Msg{}
	err = json.Unmarshal([]byte(msg), m)
	if err != nil {
		return nil, err
	}
	return m, nil
}
