/**********************************************************************************************
*
*   raylib - Advance Game template
*
*   Gameplay Screen Functions Definitions (Init, Update, Draw, Unload)
*
*   Copyright (c) 2014-2018 Ramon Santamaria (@raysan5)
*
*   This software is provided "as-is", without any express or implied warranty. In no event
*   will the authors be held liable for any damages arising from the use of this software.
*
*   Permission is granted to anyone to use this software for any purpose, including commercial
*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
*
*     1. The origin of this software must not be misrepresented; you must not claim that you
*     wrote the original software. If you use this software in a product, an acknowledgment
*     in the product documentation would be appreciated but is not required.
*
*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
*     as being the original software.
*
*     3. This notice may not be removed or altered from any source distribution.
*
**********************************************************************************************/

#include "raylib.h"
#include "screens.h"

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

//#define MAX_CODING_WORDS    12
//#define MAX_MISSION_WORDS    8
#define MAX_LINE_CHAR  30

/*
// NOTE: Coding words are generic and the same words 
// are used for all missions, 
typedef enum CodingWords { 
    POLLO = 0,
    CONEJO,
    HUEVO,
    NIDO,
    AIRE,
    ARMARIO,
    AGUJERO,
    COSA,
    WORD,
} CodingWords;
*/

static char *codingWords[MAX_CODING_WORDS] = { 
    "pollo\0", 
    "conejo\0", 
    "huevo\0",
    "nido\0", 
    "aire\0", 
    "armario\0", 
    "agujero\0",
    "platano\0",
    "pastel\0",
    "mercado\0",
    "raton\0",
    "melon\0"
};

// Words to be coded or coding words
/*typedef struct Word {
    int id;
    Rectangle rec;
    Rectangle iniRec;
    bool hover;
    bool picked;
    char text[32];          // text
} Word;*/

/*
// Mission information
typedef struct Mission {
    int id;
    char brief[512];        // Mission briefing
    char key[32];           // Mission keyword
    char msg[256];          // Message to be coded
    int wordsCount;         // Number of words to coded
    int sols[8];            // Solution code, depends on wordsCount
} Mission;
*/
//----------------------------------------------------------------------------------
// Global Variables Definition (local to this module)
//----------------------------------------------------------------------------------

// Gameplay screen global variables
static int framesCounter;
static int finishScreen;

static Texture2D texBackground;
static Font fontMessage;
static Texture2D texWordsAtlas;
static Texture2D texVignette;

static Sound fxGrab;
static Sound fxPlace;
static Sound fxLeave;

static Music musSpy;

static Word words[MAX_CODING_WORDS] = { 0 };

// Hay que hacerlo global, para poder consultar el resultado desde la endingscreen
//static Word messageWords[MAX_MISSION_WORDS] = { 0 };

static Mission *missions = NULL;

static bool canSend = false;

Vector2 msgOffset = { 430, 300 };

//----------------------------------------------------------------------------------
// Gameplay Screen Functions Definition
//----------------------------------------------------------------------------------

// Gameplay Screen Initialization logic
void InitGameplayScreen(void)
{
    framesCounter = 0;
    finishScreen = 0;
    
    fontMessage = LoadFontEx("resources/fonts/traveling_typewriter.ttf", 30, 250, 0);
    
    texBackground = LoadTexture("resources/textures/message_background.png");
    texVignette = LoadTexture("resources/textures/message_vignette.png");
    
    fxGrab = LoadSound("resources/audio/fx_grab.ogg");
    fxPlace = LoadSound("resources/audio/fx_place.ogg");
    fxLeave = LoadSound("resources/audio/fx_leave.ogg");
    
    musSpy = LoadMusicStream("resources/audio/s_p_y.xm");
    PlayMusicStream(musSpy);
    
#if defined(PLATFORM_WEB)
    #define WORD_ATLAS_FROM_FILE
#endif
#if defined(WORD_ATLAS_FROM_FILE)
    texWordsAtlas = LoadTexture("resources/textures/mission_words.png");
#else
    // Generate coding words atlas directly from text
    Image imWordsBase = LoadImage("resources/textures/words_base.png");
    Image imWords = GenImageColor(imWordsBase.width, imWordsBase.height*MAX_CODING_WORDS, WHITE);
    
    for (int i = 0; i < MAX_CODING_WORDS; i++)
    {
        ImageDraw(&imWords, imWordsBase, 
                  (Rectangle){ 0, 0, imWordsBase.width, imWordsBase.height },
                  (Rectangle){ 0, imWordsBase.height*i, imWordsBase.width, imWordsBase.height });
                  
        ImageDrawTextEx(&imWords,(Vector2){ imWordsBase.width/2 - MeasureTextEx(fontMessage, codingWords[i], 
                        fontMessage.baseSize, 0).x/2, imWordsBase.height*i }, fontMessage, codingWords[i], 
                        fontMessage.baseSize, 0, BLACK); 
    }
    
    texWordsAtlas = LoadTextureFromImage(imWords);
    
    UnloadImage(imWordsBase);
    UnloadImage(imWords);
#endif

    // Initialize missions
    // WARNING: Some problem with imWords image generation (memory leak?) could cause
    // that loading missions before/after generation breaks game, on web is the other way round... :(
    missions = LoadMissions("resources/missions.txt");
    TraceLog(LOG_WARNING, "Words count %i", missions[currentMission].wordsCount);

    // Initialize coding words
    for (int i = 0; i < MAX_CODING_WORDS; i++)
    {
        words[i].id = -1;          // Not placed anywhere

        words[i].rec.x = 110 + 940*(i/(MAX_CODING_WORDS/2));
        words[i].rec.y = 200 + 60*(i%(MAX_CODING_WORDS/2));
        words[i].rec.width = 140;  // texWordsAtlas.width/MAX_MISSIONS
        words[i].rec.height = 35;  // texWordsAtlas.height/MAX_MISSION_WORDS
        words[i].iniRec = words[i].rec;
        words[i].hover = false;    // Mouse hover detected
        words[i].picked = false;   // Mouse picked
        
        //words[i].text = ''; //codingWords[i];     // Fill text if required...
    }
    
    // Analize missions[currentMission].msg string for words!
    int msgLen = strlen(missions[currentMission].msg);
    
    // Add '/' each MAX_LINE_CHAR chars
    int currentLine = 1;
    int i = currentLine * MAX_LINE_CHAR;

    while (i < msgLen - 1)
    {
        if (missions[currentMission].msg[i] == ' ')
        {
            missions[currentMission].msg[i] = '/';
            currentLine++;
            i = currentLine*MAX_LINE_CHAR;
        }
        else i++;
    }      
    
    int currentWord = 0;
    int offsetX = 0;
    int offsetY = 0;
    bool foundWord = false;
    int wordInitPosX = 0;
    int wordInitPosY = 0;
    
    // TODO: messageWords should be reseted every mission
    //memcpy(messageWords, 0, sizeof(Word)*MAX_MISSION_WORDS);

    for (int i = 0; i < msgLen; i++)
    {        
        char c = missions[currentMission].msg[i];
        if (foundWord && (c == ' ' || c == '.'))
        {
            foundWord = false;
            
            messageWords[currentWord - 1].rec.width = (int)MeasureTextEx(fontMessage, SubText(missions[currentMission].msg, wordInitPosX, (i - wordInitPosX)), 30, 0).x;
            messageWords[currentWord - 1].rec.height = fontMessage.baseSize;
            
            //TODO: Guardar en message
            strncpy(messageWords[currentWord - 1].text, SubText(missions[currentMission].msg, wordInitPosX, (i - wordInitPosX)), i - wordInitPosX);
        }
        
        if (c == '@') // One word to change
        {
            foundWord = true;
            missions[currentMission].msg[i] = ' ';

            offsetX = (int)MeasureTextEx(fontMessage, SubText(missions[currentMission].msg, wordInitPosY, (i + 1) - wordInitPosY), 30, 0).x;
            
            messageWords[currentWord].rec.x = offsetX;
            messageWords[currentWord].rec.y = offsetY;

            wordInitPosX = i + 1;

            currentWord++;
        }
        else if (c == '/')
        {
            missions[currentMission].msg[i] = '\n';
            wordInitPosY = i;
            offsetY += (fontMessage.baseSize + fontMessage.baseSize/2);     // raylib internal increment on line break...
        }
    }

    for (int i = 0; i < missions[currentMission].wordsCount; i++)
    {
        messageWords[i].id = -1;          // Not required for message words, id is the array position
        
        // Recalculate words rectangles considering text offset on screen
        messageWords[i].rec.x += msgOffset.x;
        messageWords[i].rec.y += msgOffset.y;
        
        // Recalculate words rectangle considering new width height
        messageWords[i].rec.x -= (texWordsAtlas.width - messageWords[i].rec.width)/2;
        messageWords[i].rec.y -= ((texWordsAtlas.height / MAX_CODING_WORDS) - messageWords[i].rec.height)/2;
        
        //Recalculate width height 
        messageWords[i].rec.width =  texWordsAtlas.width;
        messageWords[i].rec.height = texWordsAtlas.height / MAX_CODING_WORDS;

        messageWords[i].hover = false;    // Mouse hover detected
        messageWords[i].picked = false;   // Mouse picked
    }
}

// Gameplay Screen Update logic
void UpdateGameplayScreen(void)
{
    UpdateMusicStream(musSpy);
    
    for (int i = 0; i < MAX_CODING_WORDS; i++)
    {
        if (CheckCollisionPointRec(GetMousePosition(), words[i].rec))
        {
            words[i].hover = true;
            
            if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
            {
                words[i].picked = true;
                PlaySound(fxGrab);
            }
        }
        else words[i].hover = false;
        
        
        if (words[i].picked)
        {  
            for (int j = 0; j < missions[currentMission].wordsCount; j++)
            {               
                if (CheckCollisionPointRec(GetMousePosition(), messageWords[j].rec)) messageWords[j].hover = true;
                else messageWords[j].hover = false;
            }
            
            if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON))
            {              
                words[i].picked = false;
                
                for (int j = 0; j < missions[currentMission].wordsCount; j++)
                {     
                    messageWords[j].hover = false;
                    
                    if (CheckCollisionPointRec(GetMousePosition(), messageWords[j].rec))
                    {
                        PlaySound(fxPlace);
                        
                        words[i].rec.x = messageWords[j].rec.x;
                        words[i].rec.y = messageWords[j].rec.y;
                        
                        if (messageWords[j].id != -1)
                        {
                            int id = messageWords[j].id;
                            words[id].rec = words[id].iniRec;
                        }
                        
                        messageWords[j].id = i;
                        for (int k = 0; k < missions[currentMission].wordsCount; k++)
                        {
                            if (j != k && messageWords[j].id == messageWords[k].id) 
                            {
                                messageWords[k].id = -1;
                                break;
                            }
                        }
                        break;
                    }
                    else 
                    {
                        PlaySound(fxLeave);
                        
                        words[i].rec = words[i].iniRec;
                        if (i == messageWords[j].id) messageWords[j].id = -1;
                    }
                } 
            }            
        }
        
        // TODO: Move word picked with mouse
        if (words[i].picked)
        {
            words[i].rec.x = GetMouseX() - words[i].rec.width/2;
            words[i].rec.y = GetMouseY() - words[i].rec.height/2;
            
            // TODO: Check if label is placed in some mission word position
            //if (CheckCollisionRecs(words[i].rec))
        }
        else
        {
            //if (words[i].id != -1)
        }
    }
    
    canSend = true;
    for(int j = 0; j < missions[currentMission].wordsCount; j++)
    {               
        if(messageWords[j].id == -1)
        {
            canSend = false;
            break;
        }
    }
    
    if (canSend && (IsKeyPressed(KEY_ENTER) || IsButtonPressed())) 
    {       
        finishScreen = true;
    } 
}

// Gameplay Screen Draw logic
void DrawGameplayScreen(void)
{
    DrawTexture(texBackground, 0, 0, WHITE);
    
    DrawTextEx(fontMessage, missions[currentMission].msg, msgOffset, fontMessage.baseSize, 0, BLACK);
    
    for (int i = 0; i < missions[currentMission].wordsCount; i++)
    {
        Rectangle recLines = messageWords[i].rec;
        DrawRectangleLines(recLines.x, recLines.y, recLines.width, recLines.height, Fade(RED, 0.35f));
        if(messageWords[i].hover) DrawRectangleRec(messageWords[i].rec, Fade(RED, 0.30f));
        DrawText(FormatText("%i", messageWords[i].id), i*25, 0, 30, RED);
    }
    for (int i = 0; i < MAX_CODING_WORDS; i++)
    {
        if (words[i].picked) DrawTextureRec(texWordsAtlas, (Rectangle){ 0, i*35, 140, 35 }, (Vector2){ words[i].rec.x, words[i].rec.y }, MAROON);
        else if (words[i].hover) DrawTextureRec(texWordsAtlas, (Rectangle){ 0, i*35, 140, 35 }, (Vector2){ words[i].rec.x, words[i].rec.y }, RED);
        else DrawTextureRec(texWordsAtlas, (Rectangle){ 0, i*35, 140, 35 }, (Vector2){ words[i].rec.x, words[i].rec.y }, WHITE);
    }
    
    DrawTexturePro(texVignette, (Rectangle){0,0,texVignette.width, texVignette.height}, (Rectangle){0,0,GetScreenWidth(), GetScreenHeight()}, (Vector2){0,0}, 0, WHITE);
    
    if (canSend) DrawButton("enviar");
}

// Gameplay Screen Unload logic
void UnloadGameplayScreen(void)
{
    UnloadTexture(texBackground);
    UnloadTexture(texVignette);
    UnloadTexture(texWordsAtlas);
    
    UnloadSound(fxGrab);
    UnloadSound(fxLeave);
    UnloadSound(fxPlace);
    
    UnloadMusicStream(musSpy);
    
    free(missions);
}

// Gameplay Screen should finish?
int FinishGameplayScreen(void)
{
    return finishScreen;
}