package main

import (
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"encoding/binary"
	"fmt"
	"github.com/imroc/req"
	"net"
	"time"
)

type serverLog struct {
	ID uint16			`json:"id"`
	Stamp time.Time		`json:"stamp"`
	Log string			`json:"log"`
}

type serverMsg struct {
	id uint16
	acc uint32
	light bool
}

var serverLogs = []serverLog{{0, time.Now(), "Server started..."}}

func sendData(msg *serverMsg) {
	defer logPanic()
	c := config.Influx
	if c == nil {
		return
	}
	var light int
	if light = 0; msg.light { light = 1 }
	now := time.Now().UnixNano()
	data := fmt.Sprintf("accelerometer,board=%d value=%d %d\n", msg.id, msg.acc, now)
	data += fmt.Sprintf("light,board=%d value=%d %d", msg.id, light, now)
	auth := base64.StdEncoding.EncodeToString([]byte(c.Username+":"+c.Password))
	head := req.Header{
		"Authorization": "Basic " + auth,
	}
	r, err := req.Post(fmt.Sprintf("http://%s/write?db=%s", c.Domain, c.Database), head, data)
	if err != nil {
		errCheckPanic(err, "Failed send to influx server")
	}
	if r.Response().StatusCode > 300 {
		fmt.Printf("Influx server returned code %d\n", r.Response().StatusCode)
	}
}

func serveConnection(conn net.Conn) {
	defer logPanic()
	defer fmt.Printf("Connection closed %s\n", conn.RemoteAddr().String())
	defer conn.Close()
	fmt.Printf("Connection from %s\n", conn.RemoteAddr().String())
	buf := make([]byte, 4)
	for {
		_, err := conn.Read(buf)
		errCheckPanic(err, "Connection failed with %s", conn.RemoteAddr().String())
		id := binary.LittleEndian.Uint16(buf[:2])

		board, ok := config.Boards[id]
		if !ok {
			fmt.Printf("Connection with ID %d is not registerd\n", id)
			break
		}
		board.LastSeen = time.Now()

		msglen := binary.LittleEndian.Uint16(buf[2:])
		fmt.Printf("Connection from ID %d message length %d\n %x\n", id, msglen, buf)
		payload := make([]byte, msglen)
		_, err = conn.Read(payload)
		errCheckPanic(err, "Connection failed %s device #%d", conn.RemoteAddr().String(), id)
		fmt.Printf("Payload: %x\n", payload)
		message := make([]byte, msglen-16)

		block, err := aes.NewCipher(board.KEY)
		errCheckPanic(err, "Decryption failed %s device #%d", conn.RemoteAddr().String(), id)
		mode := cipher.NewCBCDecrypter(block, payload[:16])
		mode.CryptBlocks(message, payload[16:])
		fmt.Printf("Message: %x\n", message)
		msg := serverMsg{
			id,
			binary.LittleEndian.Uint32(message[:4]),
			message[4] != 0x00,
		}
		sendData(&msg)
		smessage := fmt.Sprintf("ACC: %d, LIGHT: %t", msg.acc, msg.light)
		serverLogs = append([]serverLog{{id, board.LastSeen, smessage}}, serverLogs...)
		if len(serverLogs) > 150 {
			serverLogs = serverLogs[:len(serverLogs)-1]
		}
		fmt.Printf("Message: %x\n%s\n", message, smessage)
	}

}

func serve(address string) {
	ln, err := net.Listen("tcp", address)
	errCheckExit(err,"Failed accept connection")

	defer ln.Close()
	fmt.Printf("Service socket ready at %s\n", address)

	for {
		conn, err := ln.Accept()
		errCheckExit(err,"Failed accept connection %s", conn.RemoteAddr().String())
		go serveConnection(conn)
	}

}
