/*
    Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
    Copyright (C) 2001 Dirk Mueller (mueller@kde.org)
    Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
    Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
    Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library 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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.
*/

#include "config.h"
#include "CachedResource.h"

#include "Cache.h"
#include "DocLoader.h"
#include "Frame.h"
#include "FrameLoader.h"
#include "KURL.h"
#include "Request.h"
#include "SystemTime.h"
#include <wtf/Vector.h>

namespace WebCore {

CachedResource::CachedResource(const String& URL, Type type, bool forCache, bool sendResourceLoadCallbacks)
    : m_lastDecodedAccessTime(0)
    , m_sendResourceLoadCallbacks(sendResourceLoadCallbacks)
#if PRELOAD_SCANNER_ENABLED
    , m_preloadCount(0)
    , m_preloadResult(PreloadNotReferenced)
    , m_requestedFromNetworkingLayer(false)
#endif
    , m_inCache(forCache)
    , m_docLoader(0)
{
    m_url = URL;
    m_type = type;
    m_status = Pending;
    m_encodedSize = 0;
    m_decodedSize = 0;
    m_request = 0;

    m_accessCount = 0;
    m_inLiveDecodedResourcesList = false;
    
    m_nextInAllResourcesList = 0;
    m_prevInAllResourcesList = 0;
    
    m_nextInLiveResourcesList = 0;
    m_prevInLiveResourcesList = 0;

#ifndef NDEBUG
    m_deleted = false;
    m_lruIndex = 0;
#endif
    m_errorOccurred = false;
    m_shouldTreatAsLocal = FrameLoader::shouldTreatURLAsLocal(m_url);
}

CachedResource::~CachedResource()
{
    ASSERT(!inCache());
    ASSERT(!m_deleted);
#ifndef NDEBUG
    m_deleted = true;
#endif
    
    if (m_docLoader)
        m_docLoader->removeCachedResource(this);
}

void CachedResource::finish()
{
    m_status = Cached;
}

bool CachedResource::isExpired() const
{
    if (!m_response.expirationDate())
        return false;
    time_t now = time(0);
    return (difftime(now, m_response.expirationDate()) >= 0);
}

void CachedResource::setRequest(Request* request)
{
    if (request && !m_request)
        m_status = Pending;
    m_request = request;
    if (canDelete() && !inCache())
        delete this;
}

void CachedResource::ref(CachedResourceClient *c)
{
#if PRELOAD_SCANNER_ENABLED
    if (m_preloadResult == PreloadNotReferenced) {
        if (isLoaded())
            m_preloadResult = PreloadReferencedWhileComplete;
        else if (m_requestedFromNetworkingLayer)
            m_preloadResult = PreloadReferencedWhileLoading;
        else
            m_preloadResult = PreloadReferenced;
    }
#endif
    if (!referenced() && inCache())
        cache()->addToLiveResourcesSize(this);
    m_clients.add(c);
}

void CachedResource::deref(CachedResourceClient *c)
{
    ASSERT(m_clients.contains(c));
    m_clients.remove(c);
    if (canDelete() && !inCache())
        delete this;
    else if (!referenced() && inCache()) {
        cache()->removeFromLiveResourcesSize(this);
        cache()->removeFromLiveDecodedResourcesList(this);
        allReferencesRemoved();
        cache()->prune();
    }
}

void CachedResource::setDecodedSize(unsigned size)
{
    if (size == m_decodedSize)
        return;

    int delta = size - m_decodedSize;

    // The object must now be moved to a different queue, since its size has been changed.
    // We have to remove explicitly before updating m_decodedSize, so that we find the correct previous
    // queue.
    if (inCache())
        cache()->removeFromLRUList(this);
    
    m_decodedSize = size;
   
    if (inCache()) { 
        // Now insert into the new LRU list.
        cache()->insertInLRUList(this);
        
        // Insert into or remove from the live decoded list if necessary.
        if (m_decodedSize && !m_inLiveDecodedResourcesList && referenced())
            cache()->insertInLiveDecodedResourcesList(this);
        else if (!m_decodedSize && m_inLiveDecodedResourcesList)
            cache()->removeFromLiveDecodedResourcesList(this);

        // Update the cache's size totals.
        cache()->adjustSize(referenced(), delta);
    }
}

void CachedResource::setEncodedSize(unsigned size)
{
    if (size == m_encodedSize)
        return;

    // The size cannot ever shrink (unless it is being nulled out because of an error).  If it ever does, assert.
    ASSERT(size == 0 || size >= m_encodedSize);
    
    int delta = size - m_encodedSize;

    // The object must now be moved to a different queue, since its size has been changed.
    // We have to remove explicitly before updating m_encodedSize, so that we find the correct previous
    // queue.
    if (inCache())
        cache()->removeFromLRUList(this);
    
    m_encodedSize = size;
   
    if (inCache()) { 
        // Now insert into the new LRU list.
        cache()->insertInLRUList(this);
        
        // Update the cache's size totals.
        cache()->adjustSize(referenced(), delta);
    }
}

void CachedResource::didAccessDecodedData(double timeStamp)
{
    m_lastDecodedAccessTime = timeStamp;
    
    if (inCache()) {
        if (m_inLiveDecodedResourcesList) {
            cache()->removeFromLiveDecodedResourcesList(this);
            cache()->insertInLiveDecodedResourcesList(this);
        }
        cache()->prune();
    }
}

}
