/////////////////////////////////////////////////////////////////////////////
// Name:        Cache.cpp
// Purpose:     Manage cache of transcoded files
// Author:      Alex Thuering
// Created:     29.08.2008
// RCS-ID:      $Id: Cache.cpp,v 1.3 2009/07/12 17:19:08 ntalex Exp $
// Copyright:   (c) Alex Thuering
// Licence:     GPL
/////////////////////////////////////////////////////////////////////////////

#include "Cache.h"
#include "Config.h"
#include <wx/log.h>
#include <wx/dir.h>
#include <wx/file.h>

const wxString CACHE_SUB_DIR = wxT("dvd-cache");

CacheEntry::CacheEntry(Vob* vob, DVD* dvd, int idx) {
	m_videoFile = vob->GetFilename();
	m_audioFiles = vob->GetAudioFilenames();
	for (unsigned int i = 0; i < vob->GetSubtitles().GetCount(); i++)
		m_subtitleFiles.Add(vob->GetSubtitles()[i]->GetFilename());
	for (unsigned int stIdx = 0; stIdx < vob->GetStreams().GetCount(); stIdx++)
		m_formats.Add(vob->GetStreams()[stIdx]->GetDestinationFormat());
	m_videoBitrate = dvd->GetVideoBitrate();
	m_audioBitrate = s_config.GetAudioBitrate();
	m_idx = idx;
	m_used = false;
}

bool CacheEntry::equals(const CacheEntry& entry) {
	if (m_videoFile != entry.m_videoFile
			|| m_audioFiles != entry.m_audioFiles
			|| m_subtitleFiles != entry.m_subtitleFiles
			|| m_videoBitrate != entry.m_videoBitrate
			|| m_audioBitrate != entry.m_audioBitrate
			|| m_formats.Count() != entry.m_formats.Count())
		return false;
	for (unsigned int i = 0; i < m_formats.GetCount(); i++)
		if (m_formats[i] != entry.m_formats[i])
			return false;
	return true;
}

Cache::Cache() {
	SetTempDir(s_config.GetTempDir());
}

Cache::~Cache() {
	for (CacheMap::iterator it = m_cacheMap.begin(); it != m_cacheMap.end(); it++)
		delete it->second;
	m_cacheMap.clear();
}

bool Cache::SetTempDir(wxString tempDir) {
	// check if temp dir exist 
	if (!wxDir::Exists(tempDir) && !wxMkdir(tempDir)) {
		wxLogError(_T("Can't create directory '%s'"), tempDir.c_str());
		return false;
	}
	// set new cache directory
	wxString cacheDir = tempDir + wxFILE_SEP_PATH + CACHE_SUB_DIR;
	if (!wxDir::Exists(cacheDir) && !wxMkdir(cacheDir)) {
		wxLogError(_T("Can't create directory '%s'"), cacheDir.c_str());
		return false;
	}
	// clear cache map if cache directory is changed
	if (m_dir.length() > 0 && m_dir != cacheDir)
		Clear();
	m_dir = cacheDir;
	return true;
}

wxString Cache::GetTempDir() {
	return m_dir.BeforeLast(wxFILE_SEP_PATH);
}

int Cache::GetCount() {
	return m_cacheMap.size();
}

wxString Cache::Find(Vob* vob, DVD* dvd, bool removeOld) {
	CacheMap::iterator it = m_cacheMap.find(vob->GetFilename());
	if (it == m_cacheMap.end())
		return wxT(""); // no entry for given source file exists
	// check if cache entry match
	CacheEntry* entry = it->second;
	wxString filename = GetFileName(entry->GetIdx());
	if (entry->equals(CacheEntry(vob, dvd, -1)) && wxFile::Exists(filename)) {
		entry->SetUsed(true);
		return filename;
	}
	// remove old entry
	if (removeOld) {
		if (!wxRemoveFile(filename))
			wxLogError(wxString::Format(_("Can't remove file '%s'"), filename.c_str()));
		m_cacheMap.erase(it->first);
		delete entry;
	}
	return wxT("");
}

wxString Cache::Add(Vob* vob, DVD* dvd) {
	int idx = GenerateIndex();
	CacheEntry* entry = new CacheEntry(vob, dvd, idx);
	m_cacheMap[vob->GetFilename()] = entry;
	wxString filename = GetFileName(idx);
	if (wxFileExists(filename) && !wxRemoveFile(filename))
		wxLogError(wxString::Format(_("Can't remove file '%s'"), filename.c_str()));
	return filename;
}

void Cache::Clear() {
	for (CacheMap::iterator it = m_cacheMap.begin(); it != m_cacheMap.end(); it++)
		delete it->second;
	m_cacheMap.clear();
	// remove files
	if (!wxDirExists(m_dir))
		return;
	wxDir d(m_dir);
	wxString fname;
	while (d.GetFirst(&fname, wxEmptyString, wxDIR_FILES))
		if (!wxRemoveFile(m_dir + wxFILE_SEP_PATH + fname))
			wxLogError(wxString::Format(_("Can't remove file '%s'"), fname.c_str()));
	wxRmdir(m_dir);
}

void Cache::BeginClean() {
	for (CacheMap::iterator it = m_cacheMap.begin(); it != m_cacheMap.end(); it++)
		it->second->SetUsed(false);
}

void Cache::EndClean() {
	for (CacheMap::iterator it = m_cacheMap.begin(); it != m_cacheMap.end(); it++) {
		CacheEntry* entry = it->second;
		if (!entry->IsUsed()) {
			wxRemoveFile(GetFileName(entry->GetIdx()));
			m_cacheMap.erase(it->first);
			delete entry;
		}
	}
}

wxString Cache::GetFileName(int index) {
	return m_dir + wxFILE_SEP_PATH + wxString::Format(wxT("entry%03d.vob"), index);
}

int Cache::GenerateIndex() {
	int idx = 0;
	for (CacheMap::iterator it = m_cacheMap.begin(); it != m_cacheMap.end(); it++) {
		if (it->second->GetIdx() > idx)
			idx = it->second->GetIdx();
	}
	return idx + 1;
}

bool Cache::Open() {
	// not implemented yet
	return true;
}

void Cache::Save() {
	// not implemented yet
}
