/*
 * File Name: images_pages_cache.cpp
 */

/*
 * This file is part of uds-plugin-images.
 *
 * uds-plugin-images is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * uds-plugin-images is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * Copyright (C) 2008 iRex Technologies B.V.
 * All rights reserved.
 */

#include "images_pages_cache.h"
#include "log.h"

namespace images
{

PagesCache::PagesCache(void)
: total_length(DEFAULT_MEMORY_LIMIT)
, used_length(0)
, pages()
, cache_mutex(0)
{
    cache_mutex = g_mutex_new();
}

PagesCache::~PagesCache(void)
{
    g_mutex_lock(cache_mutex);
    clear();
    g_mutex_unlock(cache_mutex);
    g_mutex_free(cache_mutex);
}

bool PagesCache::set_memory_limit(const int bytes)
{
    g_mutex_lock(cache_mutex);

    if (total_length > static_cast<unsigned int>(bytes))
    {
        //remove the redundant pages
        while (used_length > static_cast<int>(bytes))
        {
            if (!remove_page())
            {
                g_mutex_unlock(cache_mutex);
                return false;
            }
        }
    }

    total_length = static_cast<unsigned int>(bytes);
    
    g_mutex_unlock(cache_mutex);
    return true;
}

bool PagesCache::make_enough_memory(const int length)
{
    g_mutex_lock(cache_mutex);
    
    int sum = used_length + length;
    if (sum < static_cast<int>(total_length))
    {
        g_mutex_unlock(cache_mutex);
        return true;
    }

    while (sum > static_cast<int>(total_length))
    {
        if (!remove_page())
        {
            // Remove fails, because:
            // 1. There is no any cached images any more.
            // 2. The image is in use and can't be removed.
            g_mutex_unlock(cache_mutex);
            return false;
        }
        sum = used_length + length;
    }

    g_mutex_unlock(cache_mutex);
    return true;
}

bool PagesCache::add_page(ImagePage * p)
{
    if (!p) { return false; }

    g_mutex_lock(cache_mutex);

    // remove the redudant page
    while(used_length + static_cast<int>(p->length()) >=
        static_cast<int>(total_length))
    {
        if (!remove_page()) 
        {
            g_mutex_unlock(cache_mutex);
            return false; 
        }
    }

    // insert the new page into cache
    // printf("Insert Page %p into cache!\n", p);
    pages[(*p)()] = p;
    used_length += p->length();
    
    g_mutex_unlock(cache_mutex);
    return true;
}

ImagePage * PagesCache::get_page(const size_t idx)
{
    g_mutex_lock(cache_mutex);
    
    ImagePage * p = 0;
    PagesIter iter = pages.find(idx);
    if (iter != pages.end())
    {
        p = iter->second;
    }

    g_mutex_unlock(cache_mutex);
    return p;
}

void PagesCache::lock(void)
{
    g_mutex_lock(cache_mutex);
}

void PagesCache::unlock(void)
{
    g_mutex_unlock(cache_mutex);
}

void PagesCache::clear(void)
{
    PagesIter begin = pages.begin();
    PagesIter end = pages.end();
    PagesIter iter = begin;
    for(; iter != end; ++iter)
    {
        delete iter->second;
    }
    pages.clear();
    used_length = 0;
}

bool PagesCache::remove_page(void)
{
    if (pages.empty())
    {
        return false;
    }

    // remove the out-of-date page based on the remove strategy
    PagesIter begin = pages.begin();
    PagesIter end = pages.end();
    PagesIter iter = begin;
    PagesIter remove_iter = iter;

    while(iter != end)
    {
        if ((*iter->second) < (*remove_iter->second))
        {
            remove_iter = iter;
        }
        iter++;
    }

    bool b_removed;
    if (!remove_iter->second->get_in_use_flag())
    {
        // sub the used_length
        used_length -= remove_iter->second->length();
        
        // delete the page
        delete remove_iter->second;
        pages.erase(remove_iter);

        b_removed = true;
    }
    else
    {
        b_removed = false;
    }

    return b_removed;
}
};


