(function() {

    function fetchSeed(id) {
        var HttpRequest = Packages.com.gmt2001.HttpRequest,
        HashMap = Packages.java.util.HashMap,
        JSONObject = Packages.org.json.JSONObject,
        header = new HashMap(0),
        json = new JSONObject('{}');
        var request = HttpRequest.getData(HttpRequest.RequestType.GET,'https://www.geoguessr.com/api/v3/games/'+id,json.toString(),header);
        if (request.success) {
        var data = new JSONObject(request.content)
        return JSON.parse(data);
        }
	};
	function getCountryBDC(location) {
        var HttpRequest = Packages.com.gmt2001.HttpRequest,
        HashMap = Packages.java.util.HashMap,
        JSONObject = Packages.org.json.JSONObject,
        header = new HashMap(0),
        json = new JSONObject('{}');
        const BDC_KEY = "067c718d2bbd4a2ba8d0645e1633cac2";
        var request = HttpRequest.getData(HttpRequest.RequestType.GET,'https://api.bigdatacloud.net/data/reverse-geocode?latitude='+location.lat+'&longitude='+location.lng+'&key='+BDC_KEY,json.toString(),header);
        if (request.success) {
        var data = new JSONObject(request.content)
        return data.countryCode;
        }
	};
    function moveFrom(coords, angle, distance) {
        var radianAngle = (angle * Math.PI) / 180;
        var x = 0 + distance * Math.cos(radianAngle);
        var y = 0 + distance * Math.sin(radianAngle);
        var newLat = coords.lat + y * M;
        var newLng = coords.lng + (x * M) / Math.cos(coords.lat * (Math.PI / 180));
        return { lat: newLat, lng: newLng };
    }

    function toPos(coords) {
        return { lat: parseFloat(coords.split(",")[0]), lng: parseFloat(coords.split(",")[1]) };
    }
	function getSurroundings (location) {
		const meters = 100;
		const R_EARTH = 6378.137;
		const M = 1 / (((2 * Math.PI) / 360) * R_EARTH) / 1000;
		var coordinates = [location];
		for (var angle = 0; angle < 360; angle += 45) {
			coordinates.push(moveFrom({ lat: location.lat, lng: location.lng }, angle, meters));
		}
		return coordinates;
	};

	function isCoordinates (coordinates) {
		const regex = /^[-+]?([1-8]?\d(\.\d+)?|90(\.0+)?),\s*[-+]?(180(\.0+)?|((1[0-7]\d)|([1-9]?\d))(\.\d+)?)$/g;
		return regex.test(coordinates);
	};

	function calculateScale(bounds){
        return haversineDistance({ lat: bounds.min.lat, lng: bounds.min.lng }, { lat: bounds.max.lat, lng: bounds.max.lng }) / 7.458421;
    }

	function haversineDistance(mk1, mk2){
		const R = 6371.071;
		const rlat1 = mk1.lat * (Math.PI / 180);
		const rlat2 = mk2.lat * (Math.PI / 180);
		const difflat = rlat2 - rlat1;
		const difflon = (mk2.lng - mk1.lng) * (Math.PI / 180);
		const km = 2 * R *
			Math.asin(Math.sqrt(Math.sin(difflat / 2) * Math.sin(difflat / 2) + Math.cos(rlat1) * Math.cos(rlat2) * Math.sin(difflon / 2) * Math.sin(difflon / 2)));
		return km;
	};

	function calculateScore(distance, scale){
        return Math.round(5000 * Math.pow(0.99866017, (distance * 1000) / scale));
    }

	function normalize(val){
        return val.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
    }

	function isMatch(input, key){
        return input.length >= 3 && key.includes(input) && input.length <= key.length;
    }

	function findCountry(input){
		const normalized = GameHelper.normalize(input);
		return countryCodesNames.find((country) => country.code === normalized || GameHelper.isMatch(normalized, country.names.toLowerCase()));
	};

    $.getSetIniDbBoolean('chatguessr', 'isOpen', false);
    var seed = {},
        scale,
        guesses = {};

    function addGuess(user,position) {
        if(seed.round == undefined) return;
        var current_round = ""+(seed.round-1);
        if(guesses[current_round]==undefined) guesses[current_round] = []
        if(guesses[current_round].some((v)=>v.user==user)) return;
        if(!isCoordinates(position)) return;
        var pos = toPos(position)
        var distance = haversineDistance(pos, {lat: seed.rounds[current_round].lat, lng: seed.rounds[current_round].lng})
        
        var score = calculateScore(distance, scale)
        guesses[current_round].push({
            user:user,
            location:position,
            distance:distance,
            score:score,
            streak: 0,
        });
        guesses.round = seed.round - 1;
        sendGuessData();
    }

    function sendGuessData() {
        guesses.rounds = seed.rounds;
        $.panelsocketserver.sendJSONToAll(JSON.stringify({
            'eventFamily': 'chatguessr',
            'eventType': 'guesses',
            'data': JSON.stringify(guesses)
        }));
    }

    $.bind('command', function(event) {
        const sender = event.getSender().toLowerCase(),
            command = event.getCommand(),
            args = event.getArgs(),
            action = args[0];

        if (command.equalsIgnoreCase('g')) {
            if($.getIniDbBoolean('chatguessr', 'isOpen')) addGuess(sender,args);
        }else if (command.equalsIgnoreCase('cg')) {
            //send url 
            //$.say($.whisperPrefix(sender) + $.lang.get('chatguessr.help', ' Use "!cg [open | close | ...]" to open/close the feature.'));
        }else if(command.equalsIgnoreCase('cga')){
            if(action.equalsIgnoreCase('open')){
                $.setIniDbBoolean('cgstatus', 'isOpen', true);
            }else if(action.equalsIgnoreCase('close')){
                $.setIniDbBoolean('cgstatus', 'isOpen', false)
            }else if(action.equalsIgnoreCase('start')){
                seed = fetchSeed(args[1]);
                scale = calculateScale(seed.bounds);
                guesses.round = seed.round - 1;
                sendGuessData();
            }else if(action.equalsIgnoreCase('end')){
                $.setIniDbBoolean('cgstatus', 'isOpen', false)
                guesses = {};
                seed = {};
                sendGuessData();
            }else if(action.equalsIgnoreCase('refresh')){
                if(seed.token == undefined) return;
                var newseed = fetchSeed(seed.token);
                if(newseed.round > seed.round){
                    $.setIniDbBoolean('chatguessr', 'isOpen',false) //NEW ROUND /Score
                    var gp = newseed.player.guesses[seed.round - 1].lat+","+newseed.player.guesses[seed.round - 1].lng
                    addGuess($.channelName,gp);
                }else if(newseed.state=="finished" && newseed.player.guesses.length > seed.player.guesses.length){
                    $.setIniDbBoolean('chatguessr', 'isOpen',false)
                    var gp = newseed.player.guesses[newseed.round - 1].lat+","+newseed.player.guesses[newseed.round - 1].lng
                    addGuess($.channelName,gp);
                }else if(newseed.state=="finished"){
                    $.setIniDbBoolean('chatguessr', 'isOpen',false) //FINISHED
                    //Compute total scores +  give reward
                    seed = newseed;
                    sendGuessData();
                }else{
                    $.setIniDbBoolean('chatguessr', 'isOpen',true) //NEW ROUD /Guess
                    guesses.round = seed.round - 1;
                    sendGuessData();
                }
                seed = newseed;
            }else if(action.equalsIgnoreCase('gg')){
                sendGuessData();
            }else if(action.equalsIgnoreCase('fg')){
                addGuess("Alpha","0,0");
                addGuess("Beta","40,0");
                addGuess("Charlie","0,60");
                addGuess("Delta","80,80");
            }
        }
    });

    $.bind('initReady', function() {
        $.registerChatCommand('./custom/custom/chatguessr.js', 'cg',7);
        $.registerChatCommand('./custom/custom/chatguessr.js', 'g',7);
        $.registerChatCommand('./custom/custom/chatguessr.js', 'cga',7);
        $.registerChatSubcommand('cga', 'open', 2);
        $.registerChatSubcommand('cga', 'close', 2);
        $.registerChatSubcommand('cga', 'start', 2);
        $.registerChatSubcommand('cga', 'end', 2);
        $.registerChatSubcommand('cga', 'refresh', 2);
        $.registerChatSubcommand('cga', 'gg', 2);
        $.registerChatSubcommand('cga', 'fg', 2);
    });

})();
