package lib

import (
	"bytes"
	"encoding/json"
)

//topicPointer and textPointer should not be exported
//mb move tweet reconstruction to dbRead from servers
type Tweet struct {
	TopicPointer string
	TextPointer  int
	Topics       []string
	Text         string
	RoundPosted  int
}

var dbR = make(map[string][]Tweet)

var archive = make(map[string][]Tweet)

//has to be dividable by 32
var minimumBlockSize int

const roundsBeforeArchiving = 3

var topicList []string

var archiveTopicList []string

func NewEntries(inputTweets []Tweet, whereTo int) {
	tmpdb := dbR
	if whereTo == 1 {
		tmpdb = archive
	}
	var position int = 0
	for _, tweet := range inputTweets {
		for index := range tweet.Topics {
			//new topic
			if _, ok := tmpdb[tweet.Topics[index]]; !ok {
				if whereTo == 0 {
					topicList = append(topicList, tweet.Topics[index])
				} else {
					archiveTopicList = append(archiveTopicList, tweet.Topics[index])
				}
			}
			//new tweet
			if index == 0 {
				position = len(tmpdb[tweet.Topics[0]])
				tmpdb[tweet.Topics[index]] = append(tmpdb[tweet.Topics[0]], tweet)
			} else {
				//known tweet
				//setting pointer for all other Topics
				topic := tweet.Topics[index]
				tweet.TopicPointer = tweet.Topics[0]
				tweet.TextPointer = position
				tweet.Topics = nil
				tweet.Text = ""
				tmpdb[topic] = append(tmpdb[topic], tweet)
			}
		}
	}
	if whereTo == 0 {
		dbR = tmpdb
	} else {
		archive = tmpdb
	}
}

//todo! add round to pirquery only get tweets that have been posted from that round onward
func GetTweets(pirQuery []byte, dataLength int, whereFrom int) []byte {
	tmpdb := dbR
	if whereFrom == 1 {
		tmpdb = archive
	}
	minimumBlockSize = dataLength * maxTweetAmount(whereFrom)
	var wantedTopics = getNamesForTopics(pirQuery, whereFrom)
	tweetsToReturn := make([][]Tweet, len(wantedTopics))
	for index, wantedTopic := range wantedTopics {
		for _, tweet := range tmpdb[wantedTopic] {
			//new Tweet
			if tweet.Text != "" {
				tweet.RoundPosted = 0
				tweetsToReturn[index] = append(tweetsToReturn[index], tweet)
			} else {
				//"copied" tweet
				//find tweet with pointers
				tweet = tmpdb[tweet.TopicPointer][tweet.TextPointer]
				tweet.RoundPosted = 0
				tweetsToReturn[index] = append(tweetsToReturn[index], tweet)
			}
		}
	}
	return tweetsToByteArray(tweetsToReturn)
}

func maxTweetAmount(whereFrom int) int {
	tmpdb := dbR
	if whereFrom == 1 {
		tmpdb = archive
	}
	var max int = 0
	for i := range tmpdb {
		nrOfTweets := len(dbR[i])
		if nrOfTweets > max {
			max = nrOfTweets
		}
	}
	return max
}

func getNamesForTopics(wantedIndices []byte, whereFrom int) []string {
	var topicNames []string
	tmpTopicList := topicList
	if whereFrom == 1 {
		tmpTopicList = archiveTopicList
	}
	for index, element := range wantedIndices {
		if element == 1 {
			topicNames = append(topicNames, tmpTopicList[index])
		}
	}
	return topicNames
}

//transform struct to byte array for sending
func tweetsToByteArray(tweetsToReturn [][]Tweet) []byte {
	tweetsAsBytes := make([]byte, minimumBlockSize)
	for _, block := range tweetsToReturn {
		var tweetToAppend []byte
		for _, tweet := range block {
			for _, topic := range tweet.Topics {
				tweetToAppend = append(tweetToAppend, []byte(topic)...)
				tweetToAppend = append(tweetToAppend, ","...)
			}
			//replaces last with ";", bc there is text following and not another topic
			tweetToAppend = tweetToAppend[:len(tweetToAppend)-1]
			tweetToAppend = append(tweetToAppend, []byte(";")[0])
			tweetToAppend = append(tweetToAppend, []byte(tweet.Text)...)
			tweetToAppend = append(tweetToAppend, []byte(";")[0])
		}
		//adds padding
		for i := len(tweetToAppend); i < minimumBlockSize; i++ {
			tweetToAppend = append(tweetToAppend, []byte(";")[0])
		}
		Xor(tweetToAppend, tweetsAsBytes)
	}

	return tweetsAsBytes
}

//see func name
func GetTopicList(whereFrom int) ([]byte, int) {
	tmpTopicList := topicList
	if whereFrom == 1 {
		tmpTopicList = archiveTopicList
	}
	if (len(tmpTopicList)) == 0 {
		return nil, 0
	}
	topicByteArray := new(bytes.Buffer)
	json.NewEncoder(topicByteArray).Encode(tmpTopicList)
	return topicByteArray.Bytes(), len(tmpTopicList)
}

//iterates through full dbR and moves old tweets to archive
func CleanUpdbR(round int) {
	var tweetsToArchive []Tweet
	for j := range dbR {
		tweets := dbR[j]
		for i := len(tweets) - 1; i >= 0; i-- {
			if round-roundsBeforeArchiving == tweets[i].RoundPosted {
				//only adds the tweet to the archive when there is text
				if tweets[i].Text != "" {
					tweetsToArchive = append(tweetsToArchive, tweets[i])
				}
				//delets the tweet from the array
				tweets = append(tweets[:i], tweets[i+1:]...)
			}
		}
		dbR[j] = tweets
	}
	NewEntries(tweetsToArchive, 1)
}
