using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ApplicationManager : MonoBehaviour {

    // create a state enum
    public enum ApplicationState
    {
        initApplication,
        checkForContentUpdate,
        waitForServerResonse,
        downloadContentUpdate,
        installContentUpdate,
        running,
        changeScene,
        initScene,
        exitScene,
        savingConfigurations,
        loadingConfigurations,
        enterPauseApplication,
        pauseApplication,
        resumeApplication,
        exitApplication
    }

    #region Public Attributes
    // global instance of the application manager
    public static ApplicationManager instance = null;

    // debug
    public ApplicationState CurrentState { get { return _currentState; } }

    #endregion

    #region Private Attributes

    // private attribute for the update check URL
    private string updateURL = "http://www.evolut.studio/vrapp/index.php";
    private string updateUsername = "tester@vrapp";
    private string updatePassword = "EvolutStudioVR";

    // private attribute for tracking the current application state
    private ApplicationState _currentState = ApplicationState.initApplication;

    // private attribute for the current SceneManager
    private SceneManager currentSceneManager;

    // private attribute for the current SceneController
    private SceneController currentSceneController;

    // private attribute for new scene name to load next
    private string requestedSceneNameToLoad;

    // private attribute for the update notification
    GameObject updateNotification = null;

    #endregion

    #region Public Functions

    // set the given scene manager as the current instance of SceneManager
    public void registerSceneManagerAndController(SceneManager cSceneManager, SceneController cSceneController)
    {
        currentSceneManager = cSceneManager;
        currentSceneController = cSceneController;
    }

    // global function for changing scene
    public void registerSceneChange(string requestedSceneName)
    {
        // register new scene name to load next
        requestedSceneNameToLoad = requestedSceneName;

        // change current state to exitScene
        _currentState = ApplicationState.exitScene;
    }

    #endregion

    #region Private Functions
    
    // init application
    void InitApplication()
    {
        //TODO: other things for init

        // check if the application runs for the first time
        if(!PlayerPrefs.HasKey("Settings"))
        {
            //TODO: load default_settings.json into the new created key "Settings"
        }

        //TODO: check for firsttime running and load default configuration

        // change the current state to running
        _currentState = ApplicationState.checkForContentUpdate;
    }

    void CheckForContentUpdate()
    {
        // start coroutine until response
        StartCoroutine(startServerRequest());

        // instantiate the update notification prefab
        //updateNotification = Instantiate(Resources.Load("UpdateNotification", typeof(GameObject))) as GameObject;

        // change the current state to wait for server response
        _currentState = ApplicationState.waitForServerResonse;
    }

    IEnumerator startServerRequest()
    {
        // Create a Web Form
        WWWForm form = new WWWForm();
        form.AddField("username", updateUsername);
        form.AddField("password", updatePassword);

        // start a request to update server
        WWW w = new WWW(updateURL, form);

        // wait for request
        yield return w;

        // check response from server
        if (!string.IsNullOrEmpty(w.error))
        {
            Debug.Log(w.error);
        }
        else
        {
            // create a new update model from server response
            UpdateModel update = UpdateModel.CreateModelFromJSON(w.text);

            // TODO: Antwort auswerten
            //zwei fälle entweder update=true --> downloadContentUpdate
            // oder update=false --> running
            //currentState = ApplicationState.running;
            Debug.Log(w.text);
            Debug.Log(update.description);
            Debug.Log(update.version);
        }
    }

    void InitScene()
    {
        // Call initScene from current SceneManager
        currentSceneManager.initScene();

        // change current State to running
        _currentState = ApplicationState.running;
    }

    void ExitScene()
    {
        // save scene data
        //TODO

        // change current state to changeScene
        _currentState = ApplicationState.changeScene;
    }

    void ChangeScene()
    {
        // load scene from given name
        UnityEngine.SceneManagement.SceneManager.LoadScene(requestedSceneNameToLoad);

        // change current state to initScene
        _currentState = ApplicationState.initScene;
    }

    void PauseApplication()
    {

    }

    void ResumeApplication()
    {

    }

    // set the current state depending on the given status value
    private void CheckForApplicationPause(bool statusValue)
    {
        if (statusValue)
        {
            _currentState = ApplicationState.pauseApplication;
        }
        else
        {
            _currentState = ApplicationState.resumeApplication;
        }
    }

    #endregion

    #region Unity Events
    void Awake()
    {

        //Check if instance already exists
        if (instance == null)
        {
            //if not, set instance to this
            instance = this;
        }
        else if (instance != this)
        {
            //Then destroy this. This enforces our singleton pattern, meaning there can only ever be one instance of a GameManager.
            Destroy(gameObject);
        }

        //Sets this to not be destroyed when reloading scene
        DontDestroyOnLoad(gameObject);

        // change current state to init application
        _currentState = ApplicationState.initApplication;
    }

    /*void OnApplicationFocus(bool hasFocus)
    {
        CheckForApplicationPause(!hasFocus);
    }
    
    void OnApplicationPause(bool pauseStatus)
    {
        CheckForApplicationPause(pauseStatus);
    }*/
    
    void Update () {
        // Logic depenging on current state
        switch (_currentState)
        {
            case ApplicationState.initApplication:
                Debug.Log("STATE: INIT APPLICATION");
                // call private init application function
                InitApplication();
                break;
            case ApplicationState.checkForContentUpdate:
                Debug.Log("STATE: CHECK FOR CONTENT UPDATE");
                // call private check for content updates function
                CheckForContentUpdate();
                break;
            
            case ApplicationState.running:
                //Debug.Log("STATE: RUNNING");
                //TODO
                break;

            case ApplicationState.pauseApplication:
                Debug.Log("STATE: PAUSE APPLICATION");
                //TODO
                break;

            case ApplicationState.resumeApplication:
                Debug.Log("STATE: RESUME APPLICATION");
                //TODO
                break;

            case ApplicationState.initScene:
                Debug.Log("STATE: INIT SCENE");
                // call private init scene function
                InitScene();
                break;
            case ApplicationState.changeScene:
                Debug.Log("STATE: CHANGE SCENE");
                // call private change scene function
                ChangeScene();
                break;
            case ApplicationState.exitScene:
                Debug.Log("STATE: EXIT SCENE");
                // call privat exit scene function
                ExitScene();
                break;
        }
	}

    #endregion
}
