package main

import (
	"bytes"
	"encoding/base64"
	"encoding/binary"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
	"time"
)

const (
	outputfile = "/home/appuser/wo-bisch-lorahandler/wo-bisch-lorahandler.log"
)

type MetadataTTN struct {
	Time string `json:"time"`
}

type MessageTTN struct {
	Hardware_serial string      `json:"hardware_serial"`
	Metadata        MetadataTTN `json:"metadata"`
	Payload_raw     string      `json:"payload_raw"`
}

type MessageProperties struct {
	Time        string `json:"Time"`
	Payload_hex string `json:"payload_hex"`
	DevEUI      string `json:"DevEUI"`
	DevAddr     string `json:"DevAddr"`
}

type Message struct {
	Prop MessageProperties `json:"DevEUI_uplink"`
}

// Global variables
var file *os.File

func WriteStringToFile(s string, deveui string) {
	n, err := file.WriteString(s)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	if n != len(s) {
		fmt.Println("failed to write data")
		os.Exit(1)
	}

	// Also write to individual files
	datestr := time.Now().Format("2006-01")
	individual_file := "/home/appuser/wo-bisch-lorahandler/logs/" + deveui + "-" + datestr + ".log"

	fi, err := os.OpenFile(individual_file, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	defer fi.Close()
	fi.WriteString(s)
}

func Convert2Hex(payload_raw string) (string, error) {
	res := "error"
	p, err := base64.StdEncoding.DecodeString(payload_raw)
	if err == nil {
		res = hex.EncodeToString(p)
	}
	return res, err
}

func DecodePayload(s string, deveui string) {
	type payload struct {
		Latitude  uint32
		Longitude uint32
		AlarmBat  uint16
		Flag      uint8
	}

	var ba []byte
	var pl payload

	if len(s) >= 22 {
		ba, _ = hex.DecodeString(s[0:22])
		br := bytes.NewReader(ba)
		pl = payload{}
		err := binary.Read(br, binary.BigEndian, &pl)
		if err != nil {
			fmt.Println(err)
		}

		log.Printf("Latitude:  %.5f", float32(pl.Latitude)/1000000.0)
		log.Printf("Longitude: %.5f", float32(pl.Longitude)/1000000.0)
		log.Printf("AlarmBat:  %d", pl.AlarmBat)
		log.Printf("Flag:      %d", pl.Flag)

		vbat := (pl.AlarmBat & 0x3fff) // Battery Voltage in mV
		fw := 160 + (pl.Flag & 0x1f)   // Firmware version; 5 bits

		mystring := fmt.Sprintf("measurement,deveui=%s lat=%.5f,lon=%.5f,vbat=%d,fw=%d %d\n", deveui, float32(pl.Latitude)/1000000.0, float32(pl.Longitude)/1000000.0, vbat, fw, (time.Now().Unix() * 1000 * 1000 * 1000))

		WriteStringToFile(mystring, deveui)

	} else {
		log.Printf("Payload is not >= 11 bytes (22 chars): %s", s)
	}
}

func main() {
	// Open Output File
	f, err := os.OpenFile(outputfile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
	file = f
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	defer file.Close()

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		var message_swisscom Message
		var message_ttn MessageTTN
		if r.Body == nil {
			http.Error(w, "Please send a request body", 400)
			return
		}

		buf, bodyErr := ioutil.ReadAll(r.Body)
		if bodyErr != nil {
			log.Print("bodyErr ", bodyErr.Error())
			http.Error(w, bodyErr.Error(), http.StatusInternalServerError)
			return
		}

		rdr1 := ioutil.NopCloser(bytes.NewBuffer(buf))
		rdr2 := ioutil.NopCloser(bytes.NewBuffer(buf))
		log.Printf("BODY: %s", rdr1)
		r.Body = rdr2

		// We look for the text string "mini-beieli" in the buffer, then it is a TTN Packet,
		// otherwise it should be a Swisscom Packet...

		fmt.Printf("Length of JSON Body: %d\n", len(buf))

		if strings.Contains(string(buf), "mini-beieli") {
			fmt.Printf("Seems to be a TTN Packet...\n")
			// it is probably a TTN  package...
			err := json.NewDecoder(r.Body).Decode(&message_ttn)
			if err != nil {
				http.Error(w, err.Error(), 400)
				return
			}

			fmt.Println("Time:        " + message_ttn.Metadata.Time)
			fmt.Println("Payload Raw: " + message_ttn.Payload_raw)
			fmt.Println("DevEUI:     " + message_ttn.Hardware_serial)
			payload_hex, err := Convert2Hex(message_ttn.Payload_raw)
			if err == nil {
				DecodePayload(payload_hex, message_ttn.Hardware_serial)
			} else {
				fmt.Println("Error: could not convert raw_package to hex: %s" + message_ttn.Payload_raw)
			}
		} else {
			fmt.Printf("Seems to be a Swisscom Packet...\n")
			// it is probably a Swisscom package...
			err := json.NewDecoder(r.Body).Decode(&message_swisscom)
			if err != nil {
				http.Error(w, err.Error(), 400)
				return
			}
			fmt.Println("Time:        " + message_swisscom.Prop.Time)
			fmt.Println("Payload Hex: " + message_swisscom.Prop.Payload_hex)
			fmt.Println("DevEUI:     " + message_swisscom.Prop.DevEUI)
			fmt.Println("DevAddr:     " + message_swisscom.Prop.DevAddr)
			DecodePayload(message_swisscom.Prop.Payload_hex, message_swisscom.Prop.DevEUI)
		}
	})
	log.Fatal(http.ListenAndServe(":8080", nil))
}
