///////////////////////////////////////////////////////////////////////////////
// Tyler Perkins
// 19-9-21
// weather panel
//

#include "weather.hpp"
#include "weather_config.hpp"

using namespace dashboard::panel;

///////////////////////////////////////////////////////////////////////////////
// Constructors ///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

weather::weather(){
    std::cerr << "WEATHER CONSTRUCTOR\n";
    _time_on_screen = WEATHER_DEFAULT_ON_SCREEN_TIME;
    _update_interval = std::chrono::milliseconds{WEATHER_UPDATE_INTERVAL};
    _texture = nullptr;
    _title = WEATHER_TITLE;
    api_curl = nullptr;
    weather_today = nullptr;
    weather_tommorow = nullptr;
    weather_day_after = nullptr;
    
    weather_string["clearday"]       = WEATHER_CLEAR_DAY;
    weather_string["clearnight"]     = WEATHER_CLEAR_NIGHT;
    weather_string["pcloudyday"]     = WEATHER_PCLOUDY_DAY;
    weather_string["pcloudynight"]   = WEATHER_PCLOUDY_NIGHT;
    weather_string["mcloudyday"]     = WEATHER_MCLOUDY_DAY;
    weather_string["mcloudynight"]   = WEATHER_MCLOUDY_NIGHT;
    weather_string["cloudyday"]      = WEATHER_CLOUDY_DAY;
    weather_string["cloudynight"]    = WEATHER_CLOUDY_NIGHT;
    weather_string["humidday"]       = WEATHER_HUMID_DAY;
    weather_string["humidnight"]     = WEATHER_HUMID_NIGHT;
    weather_string["lightrainday"]   = WEATHER_LRAIN_DAY;
    weather_string["lightrainnight"] = WEATHER_LRAIN_NIGHT;
    weather_string["rainday"]        = WEATHER_RAIN_DAY;
    weather_string["rainnight"]      = WEATHER_RAIN_NIGHT;
    weather_string["oshowerday"]     = WEATHER_OSHOWER_DAY;
    weather_string["oshowernight"]   = WEATHER_OSHOWER_NIGHT;
    weather_string["ishowerday"]     = WEATHER_ISHOWER_DAY;
    weather_string["ishowernight"]   = WEATHER_ISHOWER_NIGHT;
    weather_string["lightsnowday"]   = WEATHER_LSNOW_DAY;
    weather_string["lightsnownight"] = WEATHER_LSNOW_NIGHT;
    weather_string["snowday"]        = WEATHER_SNOW_DAY;
    weather_string["snownight"]      = WEATHER_SNOW_NIGHT;
    weather_string["rainsnowday"]    = WEATHER_RAINSNOW_DAY;
    weather_string["rainsnownight"]  = WEATHER_RAINSNOW_NIGHT;
}

weather::~weather(){
    std::cerr << "WEATHER DECONSTRUCTOR\n";
    if(_texture != nullptr){
        SDL_DestroyTexture(_texture);
    }
}

///////////////////////////////////////////////////////////////////////////////
// Draw function //////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

void weather::draw(){
    //create the texture if this is the first time running draw
    if(_texture == nullptr){
        initTexture();
        update();
        update_texture();
    }

    //check if its time to update
    if((std::chrono::high_resolution_clock::now() - _last_update) 
            > _update_interval){
        update();

        update_texture();
    }

    SDL_RenderCopy(board::getRenderer(), _texture, NULL, NULL);
}

///////////////////////////////////////////////////////////////////////////////
// Helper functions ///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////
// Update the information of the obj
// This DOES NOT update the display
void weather::update() {
    std::cerr << "WEATHER::UPDATE\n";
    _last_update = std::chrono::high_resolution_clock::now();
    temp_today = 0; temp_tommorow = 0; temp_day_after = 0;
    weather_today = nullptr;
    weather_tommorow = nullptr;
    weather_day_after = nullptr;


    //fetch updates
    //do curl setup and cleanup
    //We cleanup after each operation to save on memory.
    //This is because by default we only fetch every hour
    if(api_curl == nullptr){
        std::cerr << "GRABING WEATHER DATA\n";
        api_curl = curl_easy_init();
        curl_easy_setopt(api_curl, CURLOPT_URL, WEATHER_URL_SOURCE);
        curl_easy_setopt(api_curl, CURLOPT_WRITEFUNCTION,
                dashboard::panel::weather::curl_callback);
        curl_easy_setopt(api_curl, CURLOPT_WRITEDATA, &json_string);
        curl_easy_perform(api_curl);
        curl_easy_cleanup(api_curl);
        api_curl = nullptr;
    }

    //parse the response
    //We clean it up at then end because its wasting space if we dont
    json_doc = new rapidjson::Document();
    json_doc->Parse(json_string.c_str());

    //update internal state
    const rapidjson::Value& curr_entry = (*json_doc)["dataseries"];
    //get all entries
    weather_today = &weather_string.at(curr_entry[0]["weather"].GetString());
    weather_tommorow = &weather_string.at(curr_entry[7]["weather"].GetString());
    weather_day_after = &weather_string.at(curr_entry[15]["weather"].GetString());

    temp_today = curr_entry[0]["temp2m"].GetInt();
    temp_tommorow = curr_entry[7]["temp2m"].GetInt();
    temp_day_after = curr_entry[15]["temp2m"].GetInt();


    if(!WEATHER_METRIC){
        temp_today = (int)(temp_today * 1.8) + 32;
        temp_tommorow = (int)(temp_tommorow * 1.8) + 32;
        temp_day_after = (int)(temp_day_after * 1.8) + 32;
    }

    json_string.clear();
    delete json_doc;
}

///////////////////////////////////////
// Update the texture that is being
// displayed, based on data in the json feed
void weather::update_texture(){
    std::cerr << "WEATHER::UPDATE_TEXTURE\n";
    SDL_Rect tgt;
    std::string temp;

    SDL_SetRenderTarget(board::getRenderer(), _texture);
    SDL_RenderClear(board::getRenderer());

    //background image
    SDL_RenderCopy(board::getRenderer(),
            board::getImage("weather_background.png"), NULL, NULL);

    //current weather
    TTF_SizeText(board::getFont({ "Roboto_Mono/RobotoMono-Medium.ttf", 50 }),
            "Currently,",
            &tgt.w, &tgt.h);
    tgt.y = (3*SCREEN_HEIGHT / 4) - (tgt.h / 2);
    tgt.x = (SCREEN_WIDTH / 6) - (tgt.w / 2);
    SDL_RenderCopy(board::getRenderer(),
            board::getString("Currently,",
                { "Roboto_Mono/RobotoMono-Medium.ttf", 50 }), NULL, &tgt);
    tgt.y += tgt.h;
    TTF_SizeText(board::getFont({ "Roboto_Mono/RobotoMono-Medium.ttf", 50 }),
            weather_today->first.c_str(),
            &tgt.w, &tgt.h);
    tgt.x = (SCREEN_WIDTH / 6) - (tgt.w / 2);
    SDL_RenderCopy(board::getRenderer(),
            board::getString(weather_today->first,
                { "Roboto_Mono/RobotoMono-Medium.ttf", 50 }), NULL, &tgt);
    tgt.y += tgt.h;
    temp = std::to_string(temp_today);
    if(WEATHER_METRIC)
        temp += "*C";
    else
        temp += "*F";
    TTF_SizeText(board::getFont({ "Roboto_Mono/RobotoMono-Medium.ttf", 50 }),
            temp.c_str(),
            &tgt.w, &tgt.h);
    tgt.x = (SCREEN_WIDTH / 2) - (tgt.w / 2);
    SDL_RenderCopy(board::getRenderer(),
            board::getString(temp,
                { "Roboto_Mono/RobotoMono-Medium.ttf", 50 }), NULL, &tgt);

    //icon
    tgt.w = (SCREEN_WIDTH / 3) - 50;
    tgt.h = tgt.w;
    tgt.y = DEF_OVERLAY_BAR_HEIGHT + 25;
    tgt.x = 0;
    SDL_RenderCopy(board::getRenderer(),
            board::getImage(weather_today->second),
            NULL, &tgt);

    //tommorow's weather
    TTF_SizeText(board::getFont({ "Roboto_Mono/RobotoMono-Medium.ttf", 50 }),
            "Tommorow,",
            &tgt.w, &tgt.h);
    tgt.y = (3*SCREEN_HEIGHT / 4) - (tgt.h / 2);
    tgt.x = (SCREEN_WIDTH / 2) - (tgt.w / 2);
    SDL_RenderCopy(board::getRenderer(),
            board::getString("Tommorow,",
                { "Roboto_Mono/RobotoMono-Medium.ttf", 50 }), NULL, &tgt);
    tgt.y += tgt.h;
    TTF_SizeText(board::getFont({ "Roboto_Mono/RobotoMono-Medium.ttf", 50 }),
            weather_tommorow->first.c_str(),
            &tgt.w, &tgt.h);
    tgt.x = (SCREEN_WIDTH / 2) - (tgt.w / 2);
    SDL_RenderCopy(board::getRenderer(),
            board::getString(weather_tommorow->first,
                { "Roboto_Mono/RobotoMono-Medium.ttf", 50 }), NULL, &tgt);
    tgt.y += tgt.h;
    temp = std::to_string(temp_tommorow);
    if(WEATHER_METRIC)
        temp += "*C";
    else
        temp += "*F";
    TTF_SizeText(board::getFont({ "Roboto_Mono/RobotoMono-Medium.ttf", 50 }),
            temp.c_str(),
            &tgt.w, &tgt.h);
    tgt.x = (SCREEN_WIDTH / 6) - (tgt.w / 2);
    SDL_RenderCopy(board::getRenderer(),
            board::getString(temp,
                { "Roboto_Mono/RobotoMono-Medium.ttf", 50 }), NULL, &tgt);
    //icon
    tgt.w = (SCREEN_WIDTH / 3) - 50;
    tgt.h = tgt.w;
    tgt.y = DEF_OVERLAY_BAR_HEIGHT + 25;
    tgt.x = (SCREEN_WIDTH / 2) - (tgt.w / 2);

    SDL_RenderCopy(board::getRenderer(),
            board::getImage(weather_tommorow->second),
            NULL, &tgt);


    //day after's weather
    TTF_SizeText(board::getFont({ "Roboto_Mono/RobotoMono-Medium.ttf", 50 }),
            "Day after,",
            &tgt.w, &tgt.h);
    tgt.y = (3*SCREEN_HEIGHT / 4) - (tgt.h / 2);
    tgt.x = (5*SCREEN_WIDTH / 6) - (tgt.w / 2);
    SDL_RenderCopy(board::getRenderer(),
            board::getString("Day after,",
                { "Roboto_Mono/RobotoMono-Medium.ttf", 50 }), NULL, &tgt);
    tgt.y += tgt.h;
    TTF_SizeText(board::getFont({ "Roboto_Mono/RobotoMono-Medium.ttf", 50 }),
            weather_day_after->first.c_str(),
            &tgt.w, &tgt.h);
    tgt.x = (5*SCREEN_WIDTH / 6) - (tgt.w / 2);
    SDL_RenderCopy(board::getRenderer(),
            board::getString(weather_day_after->first.c_str(),
                { "Roboto_Mono/RobotoMono-Medium.ttf", 50 }), NULL, &tgt);
    tgt.y += tgt.h;
    temp = std::to_string(temp_day_after);
    if(WEATHER_METRIC)
        temp += "*C";
    else
        temp += "*F";
    TTF_SizeText(board::getFont({ "Roboto_Mono/RobotoMono-Medium.ttf", 50 }),
            temp.c_str(),
            &tgt.w, &tgt.h);
    tgt.x = (5*SCREEN_WIDTH / 6) - (tgt.w / 2);
    SDL_RenderCopy(board::getRenderer(),
            board::getString(temp.c_str(),
                { "Roboto_Mono/RobotoMono-Medium.ttf", 50 }), NULL, &tgt);
    //icon
    tgt.w = (SCREEN_WIDTH / 3) - 50;
    tgt.h = tgt.w;
    tgt.y = DEF_OVERLAY_BAR_HEIGHT + 25;
    tgt.x = (2*SCREEN_WIDTH / 3);

    SDL_RenderCopy(board::getRenderer(),
            board::getImage(weather_day_after->second),
            NULL, &tgt);

    SDL_SetRenderTarget(board::getRenderer(), NULL);
}

///////////////////////////////////////
// Lazy load the texture object
// This is to make sure that all of SDL
// is init before we attempt to draw anything
void weather::initTexture(){
    if(_texture == nullptr){
        _texture = SDL_CreateTexture(board::getRenderer(),
                SDL_PIXELFORMAT_RGBA8888, 
                SDL_TEXTUREACCESS_TARGET, 
                SCREEN_WIDTH, SCREEN_HEIGHT);

        SDL_SetTextureBlendMode(_texture, SDL_BLENDMODE_BLEND);
    }
}

///////////////////////////////////////
// Curl callback function
size_t dashboard::panel::weather::curl_callback(void* contents, size_t size, 
        size_t nmemb, void* userp){
    ((std::string*)userp)->append((char*)contents, size * nmemb);
    return size * nmemb;
}
