/*
 *
 * Note.java
 *
 * Class defines what will be in a configuration file for a user note. All metadata is in this class when read from file
*/

package jesse.keeblarcraft.JsonClassObjects;

import java.util.HashMap;
import java.util.Map.Entry;

import jesse.keeblarcraft.ConfigMgr.ConfigManager;
import jesse.keeblarcraft.Keeblarcraft;
import jesse.keeblarcraft.ChatStuff.ChatMsg;

public class PlayerNote {
    /// Class variables
    NoteFile thisNote = new NoteFile();
    ConfigManager config = new ConfigManager();

    /////////////////////////////////////////////////////////////////////////////
    /// @class      NoteMetadata
    ///
    /// @brief      The metadata that is attached to each entry in the notebook.
    ///             This includes not just the note itself, but other important
    ///             factors that can be characteristic to each note and useful
    ///
    /// @note       You can picture this as each page in the notebook if it's
    ///             an easier mental image
    /////////////////////////////////////////////////////////////////////////////
    private class NoteMetadata {
        public NoteMetadata(String note, long id, /*long mcday,*/ long sysTime, long chapter, long part) {
            this.note = note;
            this.noteId = id;
            // this.minecraftDay = mcday;
            this.systemTime = sysTime;
            this.storyChapter = chapter;
            this.storyPart = part;
        }

        String note;        // The note itself

        long noteId;        // Copied in from the file name, noteId
        // long minecraftDay;  // The minecraft day the note was taken on
        long systemTime;    // The current system time of the server
        long storyChapter;  // The chapter of the story the player is in
        long storyPart;     // Every event in a story is one part
    }

    /////////////////////////////////////////////////////////////////////////////
    /// @class      NoteFile
    ///
    /// @brief      This is the notebook
    /////////////////////////////////////////////////////////////////////////////
    public class NoteFile {
        // the uuid is the name of the file written to disk
        String uuid;
        public HashMap<Integer, NoteMetadata> noteMap = new HashMap<Integer, NoteMetadata>();
    }

    /////////////////////////////////////////////////////////////////////////////
    /// @fn         PlayerNote
    ///
    /// @brief      This class's non-trivial constructor. Grabs a handle on the
    ///             server-side file that is the players notebook at object
    ///             creation. If one does not exist, one is created
    ///
    /// @arg[in]    uuid is the players uuid value
    /////////////////////////////////////////////////////////////////////////////
    public PlayerNote(String uuid) {

        // DEVELOPER NOTE:
        //  If you are testing this part of the code, please be reminded that anonymous testing starts a new
        //  player instance everytime you launch. This means the UUID CAN CHANGE when you launch the
        //  game! This is not an issue with proper registered accounts in production
        Boolean existingFile = false;
        try {
            thisNote = config.GetJsonObjectFromFile("notes/" + uuid + ".json", NoteFile.class);
            existingFile = true;
        } catch (Exception e) {
            // Do nothing. This means the file does not exist
        }

        // In the event the above code failed out, this means a new file has to be created for the player's uuid
        if (!existingFile)
        {
            try {
                thisNote.uuid = uuid;
                FlashConfig();
            } catch (Exception e) {
                Keeblarcraft.LOGGER.warn("Could not write to the player notes file");
            }
        }

        // It's possible the above code will return a blank class if a file doesn't exist. This will make
        // a new file with this players uuid
        if ("".equals(thisNote.uuid)) {
            System.out.println("Assigning new config file for this uuid. No previous existing");
            thisNote.uuid = uuid;
        }
    }

    /////////////////////////////////////////////////////////////////////////////
    /// @fn         AddNote
    ///
    /// @brief      Adds a new note to the notebook
    ///
    /// @arg[in]    newNote is the new string value of the note
    ///
    /// @arg[in]    minecraftDay is currently unsupported but will be the day of
    ///             the minecraft world when it is implemented
    ///
    /// @arg[in]    systemTime is the epoch time in milliseconds
    ///
    /// @arg[in]    storyChapter is the chapter in the story this note was taken
    ///
    /// @arg[in]    storyPart is the part in the chapter the note was taken
    /////////////////////////////////////////////////////////////////////////////
    public void AddNote(String newNote, long minecraftDay, long systemTime, long storyChapter, long storyPart) {
        Integer noteKey = thisNote.noteMap.size() + 1;
        thisNote.noteMap.put(noteKey, new NoteMetadata(newNote, noteKey, /*minecraftDay,*/ systemTime, storyChapter, storyPart));

        FlashConfig(); ///TODO: This might be really unnecessary and may only be required on clean up as opposed to everytime the command is run
    }

    /////////////////////////////////////////////////////////////////////////////
    /// @fn         DeleteNote
    ///
    /// @brief      Deletes a note in the notebook
    ///
    /// @arg[in]    noteId is the id to delete
    /////////////////////////////////////////////////////////////////////////////
    public void DeleteNote(Integer noteId) {
        thisNote.noteMap.remove(noteId);
        FlashConfig();
    }

    /////////////////////////////////////////////////////////////////////////////
    /// @fn         ModifyNote
    ///
    /// @brief      Modifies a note at noteId entry OR creates new note at that
    ///             entry if the id didn't exist previously
    ///
    /// @arg[in]    noteId is the id we wish to modify the note of
    ///
    /// @arg[in]    newNote is the new string value of the note
    ///
    /// @arg[in]    minecraftDay is currently unsupported but will be the day of
    ///             the minecraft world when it is implemented
    ///
    /// @arg[in]    systemTime is the epoch time in milliseconds
    ///
    /// @arg[in]    storyChapter is the chapter in the story this note was taken
    ///
    /// @arg[in]    storyPart is the part in the chapter the note was taken
    ///
    /// @return     0 if success, -1 if not
    /////////////////////////////////////////////////////////////////////////////
    public void ModifyNote(Integer noteId, String newNote, /*long minecraftDay,*/ long systemTime, long storyChapter, long storyPart) {
        thisNote.noteMap.put(noteId, new NoteMetadata(newNote, noteId, /*minecraftDay,*/ systemTime, storyChapter, storyPart));
        FlashConfig();
    }

    /////////////////////////////////////////////////////////////////////////////
    /// @fn         PurgeAllNotes
    ///
    /// @brief      Wipes the players notebook clean
    /////////////////////////////////////////////////////////////////////////////
    public void PurgeAllNotes() {
        thisNote.noteMap.clear();
        FlashConfig();
    }

    /////////////////////////////////////////////////////////////////////////////
    /// @fn         GetNoteString
    ///
    /// @brief      Gets the note string in the map by identifier. NOT metadata
    ///
    /// @arg[in]    key is the map key we wish to return
    ///
    /// @return     Empty string if that ID contains no note, or string with note
    /////////////////////////////////////////////////////////////////////////////
    public String GetNoteString(Integer key) {
        if (thisNote.noteMap.containsKey(key)) {
            return thisNote.noteMap.get(key).note;
        } else {
            return "";
        }
    }

    /////////////////////////////////////////////////////////////////////////////
    /// @fn         GetNotebookSize
    ///
    /// @brief      Returns the size of the notebook object
    ///
    /// @return     Size of notebook
    /////////////////////////////////////////////////////////////////////////////
    public Integer GetNotebookSize() {
        return thisNote.noteMap.size();
    }

    /////////////////////////////////////////////////////////////////////////////
    /// @fn         GetNoteKey
    ///
    /// @brief      Returns the key identifier of a note by the value in the map
    ///
    /// @arg[in]    currentNote is the string note that we wish to see if exists
    ///
    /// @return     0 or positive number if key found, negative number if not
    /////////////////////////////////////////////////////////////////////////////
    public Integer GetNoteKey(String currentNote) {
        Integer ret = -1;
        for (Entry<Integer, NoteMetadata> entry : thisNote.noteMap.entrySet()) {
            if (entry.getValue().note.equals(currentNote)) {
                ret = entry.getKey();
            }
        }
        FlashConfig();
        return ret;
    }

    /////////////////////////////////////////////////////////////////////////////
    /// @fn         FlashConfig
    ///
    /// @brief      Writes to the configuration file
    /////////////////////////////////////////////////////////////////////////////
    public void FlashConfig() {
        config.WriteToJsonFile("notes/" + thisNote.uuid + ".json", thisNote);
    }
}
