/**
  @file
  @author Stefan Frings
*/

#include "staticfilecontroller.h"
#include <QFileInfo>
#include <QDir>
#include <QDateTime>
#include "httpsession.h"
#include "static.h"
#include <QCoreApplication>


StaticFileController::StaticFileController(QSettings* settings, QObject* parent)
    :HttpRequestHandler(parent)
{
    maxAge=settings->value("maxAge","60000").toInt();
    encoding=settings->value("encoding","UTF-8").toString();
    docroot=settings->value("path","./server/docroot").toString();
    // Convert relative path to absolute, based on the directory of the config file.
#ifdef Q_OS_WIN32
    if (QDir::isRelativePath(docroot) && settings->format()!=QSettings::NativeFormat)
#else
        if (QDir::isRelativePath(docroot))
#endif
        {
#if defined Q_OS_UNIX && ! defined Q_OS_MAC
	QFileInfo configFile(QString(DATADIR)+"/yacreader");
        docroot=QFileInfo(QString(DATADIR)+"/yacreader",docroot).absoluteFilePath();
#else
        QFileInfo configFile(QCoreApplication::applicationDirPath());
        docroot=QFileInfo(QCoreApplication::applicationDirPath(),docroot).absoluteFilePath();
#endif
    }
    qDebug("StaticFileController: docroot=%s, encoding=%s, maxAge=%i",qPrintable(docroot),qPrintable(encoding),maxAge);
    maxCachedFileSize=settings->value("maxCachedFileSize","65536").toInt();
    cache.setMaxCost(settings->value("cacheSize","1000000").toInt());
    cacheTimeout=settings->value("cacheTime","60000").toInt();
    qDebug("StaticFileController: cache timeout=%i, size=%i",cacheTimeout,cache.maxCost());
}


void StaticFileController::service(HttpRequest& request, HttpResponse& response) {
    QByteArray path=request.getPath();
    // Forbid access to files outside the docroot directory
    if (path.startsWith("/..")) {
        qWarning("StaticFileController: somebody attempted to access a file outside the docroot directory");
        response.setStatus(403,"forbidden");
        response.write("403 forbidden",true);
    }

	//TODO(DONE) carga sensible al dispositivo y a la localización
	QString stringPath = path;
	QStringList paths = QString(path).split('/');
	QString fileName = paths.last();
	stringPath.remove(fileName);
	HttpSession session=Static::sessionStore->getSession(request,response,false);
	QString device = session.getDeviceType();
    QString display = session.getDisplayType();
    if(fileName.endsWith(".png"))
        fileName = getDeviceAwareFileName(fileName, device, display, request.getHeader("Accept-Language"), stringPath);
    else
        fileName = getDeviceAwareFileName(fileName, device, request.getHeader("Accept-Language"), stringPath);
	QString newPath = stringPath.append(fileName);
	path = newPath.toLocal8Bit();

	//CAMBIADO
	response.setHeader("Connection","close");
	//END_TODO

    // Check if we have the file in cache
    //qint64 now=QDateTime::currentMSecsSinceEpoch();
   // mutex.lock();
   // CacheEntry* entry=cache.object(path);
    //if (entry && (cacheTimeout==0 || entry->created>now-cacheTimeout)) {       
    //    QByteArray document=entry->document; //copy the cached document, because other threads may destroy the cached entry immediately after mutex unlock.
    //    mutex.unlock();
    //    qDebug("StaticFileController: Cache hit for %s",path.data());
    //    setContentType(path,response);
    //    response.setHeader("Cache-Control","max-age="+QByteArray::number(maxAge/1000));
    //    response.write(document);
    //}
    //else {
		
    //    mutex.unlock();
        //qDebug("StaticFileController: Cache miss for %s",path.data());
        // The file is not in cache.
        // If the filename is a directory, append index.html.
        if (QFileInfo(docroot+path).isDir()) {
            path+="/index.html";
        }


        QFile file(docroot+path);
        if (file.exists()) {
            qDebug("StaticFileController: Open file %s",qPrintable(file.fileName()));
            if (file.open(QIODevice::ReadOnly)) {
                setContentType(path,response);
                //response.setHeader("Cache-Control","max-age="+QByteArray::number(maxAge/1000));
                //if (file.size()<=maxCachedFileSize) {
                //    // Return the file content and store it also in the cache
                //    entry=new CacheEntry();
                //    while (!file.atEnd() && !file.error()) {
                //        QByteArray buffer=file.read(65536);
                //        response.write(buffer);
                //        entry->document.append(buffer);
                //    }
                //    entry->created=now;
                //    mutex.lock();
                //    cache.insert(request.getPath(),entry,entry->document.size());
                //    mutex.unlock();
                //}
                //else {
                    // Return the file content, do not store in cache*/
                    while (!file.atEnd() && !file.error()) {
                        response.write(file.read(131072));
                    //}
                }
                file.close();
            }
            else {
                qWarning("StaticFileController: Cannot open existing file %s for reading",qPrintable(file.fileName()));
                response.setStatus(403,"forbidden");
                response.write("403 forbidden",true);
            }
        }
        else {
            response.setStatus(404,"not found");
            response.write("404 not found",true);
        }
    //}
}

void StaticFileController::setContentType(QString fileName, HttpResponse& response) const {
    if (fileName.endsWith(".png")) {
        response.setHeader("Content-Type", "image/png");
    }
    else if (fileName.endsWith(".jpg")) {
        response.setHeader("Content-Type", "image/jpeg");
    }
    else if (fileName.endsWith(".gif")) {
        response.setHeader("Content-Type", "image/gif");
    }
    else if (fileName.endsWith(".pdf")) {
        response.setHeader("Content-Type", "application/pdf");
    }
    else if (fileName.endsWith(".txt")) {
        response.setHeader("Content-Type", qPrintable("text/plain; charset="+encoding));
    }
    else if (fileName.endsWith(".html") || fileName.endsWith(".htm")) {
        response.setHeader("Content-Type", qPrintable("text/html; charset="+encoding));
    }
    else if (fileName.endsWith(".css")) {
        response.setHeader("Content-Type", "text/css");
    }
    else if (fileName.endsWith(".js")) {
        response.setHeader("Content-Type", "text/javascript");
    }
    // Todo: add all of your content types
}

bool StaticFileController::exists(QString localizedName, QString path) const
{
	QString fileName=docroot+"/"+path + localizedName;
	QFile file(fileName);
	return file.exists();
}

//retorna fileName si no se encontró alternativa traducida ó fileName-locale.extensión si se encontró
QString StaticFileController::getLocalizedFileName(QString fileName, QString locales, QString path) const
{
	QSet<QString> tried; // used to suppress duplicate attempts
	QStringList locs=locales.split(',',QString::SkipEmptyParts);
	QStringList fileNameParts = fileName.split('.');
	QString file = fileNameParts.first();
	QString extension = fileNameParts.last();
	// Search for exact match
	foreach (QString loc,locs) {
		loc.replace(QRegExp(";.*"),"");
		loc.replace('-','_');
		QString localizedName=file+"-"+loc.trimmed()+"."+extension;
		if (!tried.contains(localizedName)) {
			if(exists(localizedName, path))
				return localizedName;
			tried.insert(localizedName);
		}
	}

	// Search for correct language but any country
	foreach (QString loc,locs) {
		loc.replace(QRegExp("[;_-].*"),"");
		QString localizedName=file+"-"+loc.trimmed()+"."+extension;
		if (!tried.contains(localizedName)) {
			if(exists(localizedName, path))
				return localizedName;
			tried.insert(localizedName);
		}
	}

	return fileName;
}

QString StaticFileController::getDeviceAwareFileName(QString fileName, QString device, QString locales, QString path) const
{
	QFileInfo fi(fileName);
	QString baseName = fi.baseName();
	QString extension = fi.completeSuffix();

	QString completeFileName = getLocalizedFileName(baseName+"_"+device+"."+extension,locales,path);

	if(QFile(docroot+"/"+path+completeFileName).exists())
		return completeFileName; //existe un archivo específico para este dispositivo y locales
	else
        return getLocalizedFileName(fileName,locales,path); //no hay archivo específico para el dispositivo, pero puede haberlo para estas locales
}

QString StaticFileController::getDeviceAwareFileName(QString fileName, QString device, QString display, QString locales, QString path) const
{
    QFileInfo fi(fileName);
    QString baseName = fi.baseName();
    QString extension = fi.completeSuffix();

    QString completeFileName = baseName+display+"."+extension;
    if(QFile(docroot+"/"+path+completeFileName).exists())
        return completeFileName;
    else
    {
        completeFileName = baseName+"_"+device+display+"."+extension;
        if((QFile(docroot+"/"+path+completeFileName).exists()))
            return completeFileName;
    }

    return fileName;
}
