package funkin.api.newgrounds;

#if newgrounds
import flixel.util.FlxSignal;
import flixel.util.FlxTimer;
import io.newgrounds.NG;
import io.newgrounds.NGLite;
import io.newgrounds.components.ScoreBoardComponent.Period;
import io.newgrounds.objects.Error;
import io.newgrounds.objects.Medal;
import io.newgrounds.objects.Score;
import io.newgrounds.objects.ScoreBoard;
import io.newgrounds.objects.events.Response;
import io.newgrounds.objects.events.Result.GetCurrentVersionResult;
import io.newgrounds.objects.events.Result.GetVersionResult;
import lime.app.Application;
import openfl.display.Stage;
#end

/**
 * MADE BY GEOKURELI THE LEGENED GOD HERO MVP
 */
class NGio
{
  #if newgrounds
  /**
   * True, if the saved sessionId was used in the initial login, and failed to connect.
   * Used in MainMenuState to show a popup to establish a new connection
   */
  public static var savedSessionFailed(default, null):Bool = false;

  public static var scoreboardsLoaded:Bool = false;
  public static var isLoggedIn(get, never):Bool;

  inline static function get_isLoggedIn()
  {
    return NG.core != null && NG.core.loggedIn;
  }

  public static var scoreboardArray:Array<Score> = [];

  public static var ngDataLoaded(default, null):FlxSignal = new FlxSignal();
  public static var ngScoresLoaded(default, null):FlxSignal = new FlxSignal();

  public static var GAME_VER:String = "";

  static public function checkVersion(callback:String->Void)
  {
    trace('checking NG.io version');
    GAME_VER = "v" + Application.current.meta.get('version');

    NG.core.calls.app.getCurrentVersion(GAME_VER).addDataHandler(function(response) {
      GAME_VER = response.result.data.currentVersion;
      trace('CURRENT NG VERSION: ' + GAME_VER);
      callback(GAME_VER);
    }).send();
  }

  static public function init()
  {
    var api = APIStuff.API;
    if (api == null || api.length == 0)
    {
      trace("Missing Newgrounds API key, aborting connection");
      return;
    }
    trace("connecting to newgrounds");

    #if NG_FORCE_EXPIRED_SESSION
    var sessionId:String = "fake_session_id";
    function onSessionFail(error:Error)
    {
      trace("Forcing an expired saved session. " + "To disable, comment out NG_FORCE_EXPIRED_SESSION in Project.xml");
      savedSessionFailed = true;
    }
    #else
    var sessionId:String = NGLite.getSessionId();
    if (sessionId != null) trace("found web session id");

    #if (debug)
    if (sessionId == null && APIStuff.SESSION != null)
    {
      trace("using debug session id");
      sessionId = APIStuff.SESSION;
    }
    #end

    var onSessionFail:Error->Void = null;
    if (sessionId == null && Save.instance.ngSessionId != null)
    {
      trace("using stored session id");
      sessionId = Save.instance.ngSessionId;
      onSessionFail = function(error) savedSessionFailed = true;
    }
    #end

    NG.create(api, sessionId, #if NG_DEBUG true #else false #end, onSessionFail);

    #if NG_VERBOSE
    NG.core.verbose = true;
    #end
    // Set the encryption cipher/format to RC4/Base64. AES128 and Hex are not implemented yet
    NG.core.initEncryption(APIStuff.EncKey); // Found in you NG project view

    if (NG.core.attemptingLogin)
    {
      /* a session_id was found in the loadervars, this means the user is playing on newgrounds.com
       * and we should login shortly. lets wait for that to happen
       */
      trace("attempting login");
      NG.core.onLogin.add(onNGLogin);
    }
    // GK: taking out auto login, adding a login button to the main menu
    // else
    // {
    // 	/* They are NOT playing on newgrounds.com, no session id was found. We must start one manually, if we want to.
    // 	 * Note: This will cause a new browser window to pop up where they can log in to newgrounds
    // 	 */
    // 	NG.core.requestLogin(onNGLogin);
    // }
  }

  /**
   * Attempts to log in to newgrounds by requesting a new session ID, only call if no session ID was found automatically
   * @param popupLauncher The function to call to open the login url, must be inside
   * a user input event or the popup blocker will block it.
   * @param onComplete A callback with the result of the connection.
   */
  static public function login(?popupLauncher:(Void->Void)->Void, onComplete:ConnectionResult->Void)
  {
    trace("Logging in manually");
    var onPending:Void->Void = null;
    if (popupLauncher != null)
    {
      onPending = function() popupLauncher(NG.core.openPassportUrl);
    }

    var onSuccess:Void->Void = onNGLogin;
    var onFail:Error->Void = null;
    var onCancel:Void->Void = null;
    if (onComplete != null)
    {
      onSuccess = function() {
        onNGLogin();
        onComplete(Success);
      }
      onFail = function(e) onComplete(Fail(e.message));
      onCancel = function() onComplete(Cancelled);
    }

    NG.core.requestLogin(onSuccess, onPending, onFail, onCancel);
  }

  inline static public function cancelLogin():Void
  {
    NG.core.cancelLoginRequest();
  }

  static function onNGLogin():Void
  {
    trace('logged in! user:${NG.core.user.name}');
    Save.instance.ngSessionId = NG.core.sessionId;
    Save.instance.flush();
    // Load medals then call onNGMedalFetch()
    NG.core.requestMedals(onNGMedalFetch);

    // Load Scoreboards hten call onNGBoardsFetch()
    NG.core.requestScoreBoards(onNGBoardsFetch);

    ngDataLoaded.dispatch();
  }

  static public function logout()
  {
    NG.core.logOut();

    Save.instance.ngSessionId = null;
    Save.instance.flush();
  }

  // --- MEDALS
  static function onNGMedalFetch():Void
  {
    /*
      // Reading medal info
      for (id in NG.core.medals.keys())
      {
        var medal = NG.core.medals.get(id);
        trace('loaded medal id:$id, name:${medal.name}, description:${medal.description}');
      }

      // Unlocking medals
      var unlockingMedal = NG.core.medals.get(54352);// medal ids are listed in your NG project viewer
      if (!unlockingMedal.unlocked)
        unlockingMedal.sendUnlock();
     */
  }

  // --- SCOREBOARDS
  static function onNGBoardsFetch():Void
  {
    /*
      // Reading medal info
      for (id in NG.core.scoreBoards.keys())
      {
        var board = NG.core.scoreBoards.get(id);
        trace('loaded scoreboard id:$id, name:${board.name}');
      }
     */
    // var board = NG.core.scoreBoards.get(8004);// ID found in NG project view

    // Posting a score thats OVER 9000!
    // board.postScore(FlxG.random.int(0, 1000));

    // --- To view the scores you first need to select the range of scores you want to see ---

    // add an update listener so we know when we get the new scores
    // board.onUpdate.add(onNGScoresFetch);
    trace("shoulda got score by NOW!");
    // board.requestScores(20);// get the best 10 scores ever logged
    // more info on scores --- http://www.newgrounds.io/help/components/#scoreboard-getscores
  }

  static function onNGScoresFetch():Void
  {
    scoreboardsLoaded = true;

    ngScoresLoaded.dispatch();
    /*
      for (score in NG.core.scoreBoards.get(8737).scores)
      {
        trace('score loaded user:${score.user.name}, score:${score.formatted_value}');

      }
     */

    // var board = NG.core.scoreBoards.get(8004);// ID found in NG project view
    // board.postScore(HighScore.score);

    // NGio.scoreboardArray = NG.core.scoreBoards.get(8004).scores;
  }
  #end

  static public function logEvent(event:String)
  {
    #if newgrounds
    NG.core.calls.event.logEvent(event).send();
    trace('should have logged: ' + event);
    #else
    #if debug
    trace('event:$event - not logged, missing NG.io lib');
    #end
    #end
  }

  static public function unlockMedal(id:Int)
  {
    #if newgrounds
    if (isLoggedIn)
    {
      var medal = NG.core.medals.get(id);
      if (!medal.unlocked) medal.sendUnlock();
    }
    #else
    #if debug
    trace('medal:$id - not unlocked, missing NG.io lib');
    #end
    #end
  }

  static public function postScore(score:Int = 0, song:String)
  {
    #if newgrounds
    if (isLoggedIn)
    {
      for (id in NG.core.scoreBoards.keys())
      {
        var board = NG.core.scoreBoards.get(id);

        if (song == board.name)
        {
          board.postScore(score, "Uhh meow?");
        }

        // trace('loaded scoreboard id:$id, name:${board.name}');
      }
    }
    #else
    #if debug
    trace('Song:$song, Score:$score - not posted, missing NG.io lib');
    #end
    #end
  }
}

enum ConnectionResult
{
  /** Log in successful */
  Success;

  /** Could not login */
  Fail(msg:String);

  /** User cancelled the login */
  Cancelled;
}