
// peerServer provides functions to start and stop a host's server.

package peerServer

//TODO: Make sure this package works

import "seekia/internal/logger"
//import "seekia/internal/helpers"
import "seekia/internal/mySettings"
import "seekia/internal/network/appNetworkType/getAppNetworkType"
import "seekia/internal/network/peerConnection"
import "seekia/internal/network/craftResponses"

import "sync"
import "net"
//import "time"
//import "errors"

// We lock this mutex whenever we start or stop the server
var startOrStopServerMutex sync.Mutex

var torServerIsRunningBoolMutex sync.RWMutex
var torServerIsRunningBool bool

var clearnetServerIsRunningBoolMutex sync.RWMutex
var clearnetServerIsRunningBool bool

func getTorServerIsRunningBool()bool{

	torServerIsRunningBoolMutex.RLock()
	currentStatus := torServerIsRunningBool
	torServerIsRunningBoolMutex.RUnlock()

	return currentStatus
}

func getClearnetServerIsRunningBool()bool{

	clearnetServerIsRunningBoolMutex.RLock()
	currentStatus := clearnetServerIsRunningBool
	clearnetServerIsRunningBoolMutex.RUnlock()

	return currentStatus
}

func setClearnetServerIsRunningBool(newStatus bool){

	clearnetServerIsRunningBoolMutex.Lock()
	clearnetServerIsRunningBool = newStatus
	clearnetServerIsRunningBoolMutex.Unlock()
}

func setTorServerIsRunningBool(newStatus bool){

	torServerIsRunningBoolMutex.Lock()
	torServerIsRunningBool = newStatus
	torServerIsRunningBoolMutex.Unlock()
}

// If host mode is enabled, this will be called automatically every 3 seconds by backgroundJobs
// If the server is already running, we return nil
func StartPeerServer()error{

	startOrStopServerMutex.Lock()
	defer startOrStopServerMutex.Unlock()

	exists, hostModeStatus, err := mySettings.GetSetting("HostModeOnOffStatus")
	if (err != nil) { return err }
	if (exists == false || hostModeStatus != "On"){
		return nil
	}

	//TODO: Make sure Host identity exists, and connectivity checks (example: check.torproject.org)

	//TODO: Uncomment below once it is ready

/*

	startServerFunction := func(torOrClearnet string)error{

		if (torOrClearnet != "Tor" && torOrClearnet != "Clearnet"){
			return errors.New("startServerFunction called with invalid torOrClearnet: " + torOrClearnet)
		}

		getCurrentServerIsRunningStatus := func()bool{

			if (torOrClearnet == "Tor"){
				currentStatus := getTorServerIsRunningBool()
				return currentStatus			
			}

			currentStatus := getClearnetServerIsRunningBool()
			return currentStatus
		}

		currentRunningStatus := getCurrentServerIsRunningStatus()
		if (currentRunningStatus == true){
			return nil
		}

		getServerListenerObject := func()(net.Listener, error){

			//if (torOrClearnet == "Tor"){
			//	//TODO
			//	return nil, nil
			//}

			getMyClearnetPort := func()(string, error){

				exists, myPort, err := mySettings.GetSetting("HostOverClearnetPort")
				if (err != nil) { return "", err }
				if (exists == false){
					return "10321", nil
				}
				myPortInt, err := helpers.ConvertStringToInt(myPort)
				if (err != nil) {
					return "", errors.New("MySettings corrupt: Contains invalid HostOverClearnetPort: " + myPort)
				}

				if (myPortInt < 1 || myPortInt > 49151){
					return "", errors.New("MySettings corrupt: Contains invalid HostOverClearnetPort: " + myPort)
				}

				return myPort, nil
			}

			myClearnetPort, err := getMyClearnetPort()
			if (err != nil) { return nil, err }

			newListenerObject, err := net.Listen("tcp", "127.0.0.1:" + myClearnetPort)
			if (err != nil) { return nil, err }

			return newListenerObject, nil			
		}

		serverListenerObject, err := getServerListenerObject()
		if (err != nil){
			return errors.New("Server Error: " + err.Error())
		}

		// This loop will wait for new connections, and handle them when they arrive
		runServerLoop := func(){

			for {

				newConnectionObject, err := serverListenerObject.Accept()
				if (err != nil) {
					logger.AddLogEntry("Network", "Server Error:" + err.Error())
					break
				}

				go handleServerConnection(newConnectionObject)	
			}

			serverListenerObject.Close()

			if (torOrClearnet == "Tor"){
				setTorServerIsRunningBool(false)
			} else {
				setClearnetServerIsRunningBool(false)
			}
		}

		// This loop will check if we have shutdown the server
		// If we have, it will shutdown the serverListenerObject
		runServerShutdownLoop := func(){

			for {

				if (torOrClearnet == "Tor"){
					torServerIsRunning := getTorServerIsRunningBool()
					if (torServerIsRunning == false){
						break		
					}
				} else {
					clearnetServerIsRunning := getClearnetServerIsRunningBool()
					if (clearnetServerIsRunning == false){
						break		
					}
				}

				time.Sleep(time.Millisecond * 500)		
			}

			serverListenerObject.Close()
		}

		if (torOrClearnet == "Tor"){
			setTorServerIsRunningBool(true)
		} else {
			setClearnetServerIsRunningBool(true)
		}

		go runServerLoop()
		go runServerShutdownLoop()

		return nil
	}

	settingExists, hostOverClearnetStatus, err := mySettings.GetSetting("HostOverClearnetOnOffStatus")
	if (err != nil) { return err }	
	if (settingExists == true && hostOverClearnetStatus == "On"){
		err := startServerFunction("Clearnet")
		if (err != nil){ return err }
	}

	settingExists, hostOverTorStatus, err := mySettings.GetSetting("HostOverTorOnOffStatus")
	if (err != nil) { return err }
	if (settingExists == false || hostOverTorStatus != "Off"){
		err := startServerFunction("Tor")
		if (err != nil){ return err }
	}
*/

	return nil
}

// If host mode is disabled, this will be called automatically every 3 seconds by backgroundJobs
// If the server is not running, we return nil
// We will also call this when the application is shutting down. Background jobs will stop calling it at that point.
func StopPeerServer()error{

	//TODO: Graceful shutdown of current connections
	// Finish sending data, and then close connection.
	// We just want to avoid the requestor from designating us as a malicious host

	startOrStopServerMutex.Lock()
	defer startOrStopServerMutex.Unlock()

	setClearnetServerIsRunningBool(false)
	setTorServerIsRunningBool(false)

	return nil
}


func handleServerConnection(inputConnection net.Conn){

	handleConnection := func()error{

		// First request should always be an EstablishConnectionKey request

		downloadSuccessful, requestBytes, err := peerConnection.GetRequestThroughConnection(inputConnection)
		if (err != nil){ return err }
		if (downloadSuccessful == false){
			return nil
		}

		appNetworkType, err := getAppNetworkType.GetAppNetworkType()
		if (err != nil) { return err }

		requestIsWellFormed, identityHashMatches, networkTypeMatches, establishKeyResponse, connectionKey, err := craftResponses.GetEstablishConnectionKeyResponse(requestBytes, appNetworkType)
		if (err != nil) { return err }
		if (requestIsWellFormed == false){

			responseToSend := []byte("Invalid request.")

			_, err := peerConnection.SendResponseThroughConnection(inputConnection, responseToSend)
			if (err != nil){ return err }

			return nil
		}
		if (identityHashMatches == false){

			// The requestor believes we have a different identity hash than we currently do.
			// This could be happening because we recently switched our identity
			// It could also be happening because someone else is sharing our IP address on their own host profile

			responseToSend := []byte("Contacted wrong host.")

			_, err := peerConnection.SendResponseThroughConnection(inputConnection, responseToSend)
			if (err != nil){ return err }

			return nil
		}
		if (networkTypeMatches == false){
			// The requestor believes we are on a different network type than we are
			// This may be because we recently switched network types.

			responseToSend := []byte("Invalid network type.")

			_, err := peerConnection.SendResponseThroughConnection(inputConnection, responseToSend)
			if (err != nil){ return err }

			return nil
		}

		responseSentSuccessfully, err := peerConnection.SendResponseThroughConnection(inputConnection, establishKeyResponse)
		if (err != nil){ return err }
		if (responseSentSuccessfully == false){
			return nil
		}

		// Connection is established
		// Now we wait for new data to be sent

		for {

			downloadSuccessful, requestBytes, err := peerConnection.GetRequestThroughConnection(inputConnection)
			if (err != nil){ return err }
			if (downloadSuccessful == false){
				return nil
			}

			responseBytes, err := craftResponses.GetServerResponseForRequest(requestBytes, appNetworkType, connectionKey)
			if (err != nil) { return err }

			responseSuccessful, err := peerConnection.SendResponseThroughConnection(inputConnection, responseBytes)
			if (err != nil) { return err }
			if (responseSuccessful == false){
				return nil
			}

			//TODO: Deal with pruning old connections
			// We should keep a count of all active connections
			// If it exceeds a certain limit, we should stop accepting new connections and reply with a Busy message
			// We should also start dropping connections, starting with the ones that have been idle for the longest
		}
	}

	err := handleConnection()
	if (err != nil){
		logger.AddLogEntry("Network", "Server Error: " + err.Error())
	}

	inputConnection.Close()
}




