// This class assumes you are using Dreamlo for leaderboards
//
// If you aren't then you will not get much use out of this class.

using System;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using SimpleJSON;

/*
    public void FillData(string strData)
    {
        if (string.IsNullOrEmpty(strData))
            return;

        //Debug.Log("Calling FIllData on " + strData);

        string[] splitData = null;

        if( strData.Contains("|"))
        {
            splitData = strData.Split('|');
        }
        else
        {
            strData = strData.Replace("\"","");
            splitData = strData.Split(',');
        }

        try
        {
            strName = splitData[0];
            iVal1 = int.Parse(splitData[1]);
            if (!string.IsNullOrEmpty(splitData[2]))
                iVal2 = int.Parse(splitData[2]);
            if (!string.IsNullOrEmpty(splitData[3]))
                strVal3 = splitData[3];
            time = System.DateTime.Parse(splitData[4]);
            if (splitData.Length > 5)
                iRank = int.Parse(splitData[5]);
        }
        catch
        {

        }
    }
}
*/

public class TM_LeaderboardManager_Dreamlo : TM_LeaderboardManager
{
    private string strPlayerID;
    private string tempola;


    //Set these here, in the code.
    private const string kStrDreamloPrivateCode = "G45im4IwQ0uay-Ap3WR5AwjAhNIoYJ_U-SF5N_XthZTQ"; // "Your Dreamlo Private Code Here";
    private const string kStrDreamloPublicCode = "58fa82b54389cf024cf38f70"; //Public code goes here

    public override void ChangePlayerID(System.Object playerID)
    {
        if (playerID == null)
        {
            //Something clearly went wrong here.
            Debug.LogError("TMLeaderboardDreamlo: Null value sent into ChangePlayerID");
            return;
        }
        //In dreamlo's case, we would like a string.
        string strNewID = (string) playerID;
        if (string.IsNullOrEmpty(strNewID))
        {
            //This is harmless but not functional. It's possible that your implementation
            //requires an empty string be sent up during initialization.
            Debug.LogWarning("TMLeaderboadDreamlo: Empty string sent to ChangePlayerID.");
            ClearCachedValues();
            iPlayerRank = -1;
            dataState = ELeaderboardDataState.no_data_ready;
            return;
        }

        if (strNewID != strPlayerID)
        {
            //we've changed the ID, which means we need to refresh our data
            ClearCachedValues();

            strPlayerID = strNewID;

            //now we need to get our position in the current leaderboards.
            StartCoroutine(CheckBoardsForPlayerPosition());

        }
    }

    //Ask for a single entry using pipe-get. Would be nice if JSON worked here
    //but it does not.
    //The return value looks like this:
    // randomrika|1832807|0||6/7/2016 11:38:54 PM|3
    //And that very last value is the position we want.
    IEnumerator CheckBoardsForPlayerPosition()
    {
        //if we're in the middle of something, wait until we're done
        while (dataState == ELeaderboardDataState.waiting_for_data)
        {
            yield return null;
        }


        dataState = ELeaderboardDataState.player_rank_unknown;

        string strPlayerNameFixed = strPlayerID.Replace(" ", "%20");

        StringBuilder urlBuilder = new StringBuilder("http://dreamlo.com/lb/" + kStrDreamloPublicCode + "/pipe-get/" +
                                                     strPlayerNameFixed);

        //Send this up!
        WWW www = new WWW(urlBuilder.ToString());
        yield return www;

        Debug.Log("Hey I heard back from Dreamlo!");

        //if we're on the board, cool
        if (!string.IsNullOrEmpty(www.text))
        {
            string[] strPipeDelimitedResult = www.text.Split('|');
            iPlayerRank = Int32.Parse(strPipeDelimitedResult[strPipeDelimitedResult.Length - 1]);
            //Debug.Log("and " + strMostRecentURLReadyPlayerName + " is ranked " + iMostRecentLeaderboardRank);
        }
        //if not, record that here by making the value -1
        else
        {
            iPlayerRank = -1;
        }

        //we are now ready to scoop up some data. Since we've cleared the cache,
        //we have no data to present.
        dataState = ELeaderboardDataState.no_data_ready;

    }

    //The only initialization function for a Dreamlo board is to grab the player ID.
    public override void Initialize(System.Object playerID)
    {
        //if there's no playerID yet, that's ok.
        if (playerID == null)
        {
            //We don't know what rank the player is because there's no player at all.
            iPlayerRank = -1;
            ClearCachedValues();
            dataState = ELeaderboardDataState.no_data_ready;
        }
        else
        {
            ChangePlayerID(playerID);
        }
    }

    //Ask the server for a block of values.
    protected override void RequestLeaderboardEntries_Internal(int minValue, int maxValue)
    {
        //dreamlo starts count at 0, so step one down
        int iAdjustedMin = Math.Max(minValue - 1, 0);
        int iTotalRequested = (maxValue - minValue) + 1;

        StringBuilder urlBuilder = new StringBuilder("http://dreamlo.com/lb/");
        urlBuilder.Append(kStrDreamloPublicCode);
        urlBuilder.Append("/json/" );
        urlBuilder.Append(iAdjustedMin.ToString() );
        urlBuilder.Append("/" );
        urlBuilder.Append(iTotalRequested.ToString());

        //Go fetch these values, and put them in the proper block. Send in the original minValue.
        requestedTopScoresBlock = new List<TM_LeaderboardEntry>();

        StartCoroutine(GetEntriesFromURL(urlBuilder.ToString(), requestedTopScoresBlock, minValue));
    }

    //Ask the server for a block of around a given point,
    //and that given point is the player rank.
    //For example, if you want to display 10 scores with the player's score near the top,
    //you could request 3 above and 6 below.
    protected override void RequestLeaderboardEntriesNearPlayer_Internal(int numAbove, int numBelow)
    {
        if (iPlayerRank == -1)
        {
            iCachedNearPlayerAbove = -1;
            iCachedNearPlayerBelow = -1;
            Debug.LogWarning("TMLeaderboardsDreamlo: Can't request for values near the player when the player rank isn't known yet.");
            dataState = ELeaderboardDataState.no_data_ready;
            return;
        }

        //If the player is near the top, cull the request at 0
        int iMinValue = Mathf.Max(0, iPlayerRank - numAbove);

        //it's ok if the player is near the bottom though, dreamlo will accept requests
        //larger than the leaderboard size.
        int iTotalRequested = numAbove + numBelow + 1;

        StringBuilder urlBuilder = new StringBuilder("http://dreamlo.com/lb/");
        urlBuilder.Append(kStrDreamloPublicCode);
        urlBuilder.Append("/json/" );
        urlBuilder.Append(iMinValue.ToString() );
        urlBuilder.Append("/" );
        urlBuilder.Append(iTotalRequested.ToString());

        requestedNearPlayerBlock = new List<TM_LeaderboardEntry>();

        //Go fetch these values, and put them in the proper block
        StartCoroutine(GetEntriesFromURL(urlBuilder.ToString(), requestedNearPlayerBlock, iMinValue + 1));
    }

    //Dreamlo doesn't support this, so we just return nothing but a warning
    protected override void RequestLeaderboardEntriesFriendsOfPlayer_Internal(int iAmount)
    {
        Debug.LogWarning("TMLeaderbordsDreamlo: Dreamlo doesn't currently support friends-based leaderboarding.");
        dataState = ELeaderboardDataState.no_data_ready;
        return;
    }

    //This code handles both top score blocks and near player blocks
    IEnumerator GetEntriesFromURL(string url, List<TM_LeaderboardEntry> setList, int iFirstRecordRank)
    {
        int iOldRank = iPlayerRank;
        //if we're waiting for some data to come back, be chill
        while (dataState == ELeaderboardDataState.waiting_for_data ||
               dataState == ELeaderboardDataState.player_rank_unknown )
        {
            yield return null;
        }

        //If our rank changed after this function was called, the iFirstRecordRank is off
        //and should be adjusted
        if (iOldRank != iPlayerRank)
        {
            //build a new URL and start over -- a new coroutine with this code will spawn
            switch (recentlyRequestedDataType)
            {
                case ERecentlyRequestedLeaderboardData.score_range:
                    requestedTopScoresBlock = null;
                    RequestLeaderboardEntries(iCachedMinRank, iCachedMaxRank);
                    break;
                case ERecentlyRequestedLeaderboardData.near_player:
                    requestedNearPlayerBlock = null;
                    RequestLeaderboardEntriesNearPlayer(iCachedNearPlayerAbove, iCachedNearPlayerBelow);
                    break;
            }
            yield break;
        }

        dataState = ELeaderboardDataState.waiting_for_data;
        WWW www = new WWW(url);
        yield return www;
        if (!string.IsNullOrEmpty(www.error) ||
            string.IsNullOrEmpty(www.text))
        {
            //perhaps the servers are down?
            dataState = ELeaderboardDataState.request_failed;
            yield break;
        }

        //Here's a big ol' block of JSON, let's parse it up!
        ParseJSONRecords(www.text, setList, iFirstRecordRank);
        dataState = ELeaderboardDataState.ready;
    }

    //JSON records from Dreamlo start like this:
    // {"dreamlo":{"leaderboard":{"entry":[{"name":"EVILGAMER","score":"5943937","seconds":"0","text":"","date":"4/8/2016 7:01:57 PM"},
    //
    // We'll dig in past dreamlo, past leaderboard, and to the entry array
    bool ParseJSONRecords(string strBaseJSON, List<TM_LeaderboardEntry> setList, int iFirstRecordRank)
    {
        JSONNode listOfEntries;
        try
        {
            JSONNode master = JSONNode.Parse(strBaseJSON);
            JSONNode dreamlo = master["dreamlo"];
            JSONNode leaderboard = dreamlo["leaderboard"];
            listOfEntries = leaderboard["entry"];
        }
        catch
        {
            Debug.LogError("TMLeaderboardDreamlo: Couldn't parse downloaded JSON string.");
            return false;
        }
        int iEntryIdx = 0;
        if (listOfEntries == null)
        {
            //empty?
            return false;
        }
        while (listOfEntries[iEntryIdx] != null)
        {
            JSONNode jnode = listOfEntries[iEntryIdx];
            TM_LeaderboardEntry newEntry = new TM_LeaderboardEntry
            {
                iRank = iFirstRecordRank + iEntryIdx,
                strName = jnode["name"],
                iScore = jnode["score"]
            };

            //If you would like to grab other values here, put them in the
            //string and int lists provided in TM_LeaderboardEntry.

            setList.Add(newEntry);
            iEntryIdx++;
        }

        //That'll do!
        return true;

    }

    //Sends a new entry up to the Leaderboard. Since you assigned the ID during initialization,
    //you don't need to send it here. This also clears the cache.
    public override void SendNewEntry(int iScore, List<int> listIntValues = null, List<string> listStringValues = null)
    {
        base.SendNewEntry(iScore, listIntValues, listStringValues);

        string strPlayerNameFixed = strPlayerID.Replace(" ", "%20");

        StringBuilder urlBuilder = new StringBuilder("http://dreamlo.com/lb/");
        urlBuilder.Append(kStrDreamloPrivateCode);
        urlBuilder.Append("/add/" );
        urlBuilder.Append(strPlayerNameFixed);
        urlBuilder.Append("/" );
        urlBuilder.Append(iScore.ToString());

        //here you can also append additional int and string values if your game requires them

        StartCoroutine(UploadNewEntry(urlBuilder.ToString()));
    }

    IEnumerator UploadNewEntry(string strUrl)
    {
        //if we are already sending up data, wait for that to finish
        while (dataState == ELeaderboardDataState.waiting_for_data)
        {
            yield return null;
        }

        dataState = ELeaderboardDataState.waiting_for_data;
        WWW www = new WWW(strUrl);
        yield return www;
        if (!string.IsNullOrEmpty(www.error) ||
            string.IsNullOrEmpty(www.text))
        {
            //perhaps the servers are down?
            dataState = ELeaderboardDataState.request_failed;
            yield break;
        }

        //We need to identify our new position
        dataState = ELeaderboardDataState.player_rank_unknown;
        StartCoroutine(CheckBoardsForPlayerPosition());

    }

    //Allows us to check if the player has a rank on the leaderboards -- 
    //perhaps we are viewing the boards before submitting a score
    public bool HasPlayerRank()
    {
        return iPlayerRank > -1;
    }
}