/* StreamHttpParser.cpp */

/* Copyright (C) 2011-2024 Michael Lugmair (Lucio Carreras)
 *
 * This file is part of sayonara player
 *
 * This program 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 3 of the License, or
 * (at your option) any later version.

 * This program 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/>.
 */

#include "StreamHttpParser.h"
#include "Utils/Algorithm.h"
#include "Utils/Logger/Logger.h"

#include <QRegExp>
#include <QStringList>

namespace Algorithm = Util::Algorithm;

struct StreamHttpParser::Private
{
	bool icy;
	QString host;

	Private()
	{
		icy = false;
	}
};

QString StreamHttpParser::answerString(StreamHttpParser::HttpAnswer answer)
{
	using Answer = StreamHttpParser::HttpAnswer;
	switch(answer)
	{
		case Answer::BG:
			return "Background image";
		case Answer::Fail:
			return "Fail";
		case Answer::Favicon:
			return "Favicon";
		case Answer::HTML5:
			return "Html5";
		case Answer::Ignore:
			return "Ignore";
		case Answer::MP3:
			return "MP3";
		case Answer::MetaData:
			return "Metadata";
		case Answer::OK:
			return "OK";
		case Answer::Playlist:
			return "Playlist";
		case Answer::Reject:
			return "Reject";
		default:
			return "Unknown answer";
	}
}

StreamHttpParser::StreamHttpParser()
{
	m = Pimpl::make<Private>();
}

StreamHttpParser::~StreamHttpParser() = default;

StreamHttpParser::HttpAnswer StreamHttpParser::parse(const QByteArray& data)
{
	bool getPlaylist = false;
	bool getReceived = false;
	bool getMp3 = false;
	bool getBg = false;
	bool getFavicon = false;
	bool getMetadata = false;
	bool icy = false;
	bool isBrowser = false;

	QString qmsg(data);
	QStringList lst;

	m->icy = false;
	m->host = "";

	if(data.isEmpty())
	{
		spLog(Log::Error, this) << "Fail.. Cannot read from socket";
		return HttpAnswer::Fail;
	}

	lst = qmsg.split("\r\n");

	spLog(Log::Verbose, this) << qmsg;

	for(const QString& str: Algorithm::AsConst(lst))
	{
		QRegExp regex("(GET|HEAD)(\\s|/)*HTTP", Qt::CaseInsensitive);
		QRegExp regexPl("(GET)(\\s|/)*(playlist.m3u)(\\s|/)*HTTP", Qt::CaseInsensitive);
		QRegExp regexMp3("(.*GET(\\s|/)*.*(\\.mp3)(\\s|/)*HTTP)", Qt::CaseInsensitive);
		QRegExp regexBg("(GET)(\\s|/)*(bg-checker.png)(\\s|/)*HTTP", Qt::CaseInsensitive);
		QRegExp regexFavicon("(GET)(\\s|/)*(favicon.ico)(\\s|/)*HTTP", Qt::CaseInsensitive);
		QRegExp regexMetadata("(GET)(\\s|/)*(metadata)(\\s|/)*HTTP", Qt::CaseInsensitive);

		if(str.contains(regex))
		{
			getReceived = true;
			continue;
		}

		if(str.contains(regexMetadata))
		{
			getMetadata = true;
		}

		if(str.contains(regexFavicon))
		{
			getFavicon = true;
			continue;
		}

		if(str.contains(regexPl))
		{
			getPlaylist = true;
			continue;
		}

		spLog(Log::Debug, this) << "Client asks for MP3? " << str.contains(regexMp3);
		if(str.contains(regexMp3))
		{
			getMp3 = true;
			continue;
		}

		if(str.contains(regexBg))
		{
			getBg = true;
			continue;
		}

		if(str.contains(QStringLiteral("host:"), Qt::CaseInsensitive))
		{
			const QStringList lst = str.split(":");
			if(lst.size() > 1)
			{
				m->host = lst[1].trimmed();
			}
		}

		if(str.contains(QStringLiteral("icy-metadata:"), Qt::CaseInsensitive))
		{
			if(str.contains(QStringLiteral(":1")) || str.contains(QStringLiteral(": 1")))
			{
				icy = true;
				continue;
			}
		}

		if(str.contains(QStringLiteral("user-agent"), Qt::CaseInsensitive))
		{
			if(str.size() > 11)
			{
				QString userAgent = str.right(str.size() - 11).toLower();
				if(userAgent.contains(QStringLiteral("firefox"), Qt::CaseInsensitive) ||
				   userAgent.contains(QStringLiteral("mozilla"), Qt::CaseInsensitive) ||
				   userAgent.contains(QStringLiteral("gecko"), Qt::CaseInsensitive) ||
				   userAgent.contains(QStringLiteral("webkit"), Qt::CaseInsensitive) ||
				   userAgent.contains(QStringLiteral("safari"), Qt::CaseInsensitive) ||
				   userAgent.contains(QStringLiteral("internet explorer"), Qt::CaseInsensitive) ||
				   userAgent.contains(QStringLiteral("opera"), Qt::CaseInsensitive) ||
				   userAgent.contains(QStringLiteral("chrom"), Qt::CaseInsensitive))
				{
					isBrowser = true;
				}

				if(userAgent.contains(QStringLiteral("sayonara"), Qt::CaseInsensitive))
				{
					getPlaylist = true;
					continue;
				}
			}
		}
	}

	if(isBrowser && getFavicon && !m->host.isEmpty())
	{
		return HttpAnswer::Favicon;
	}

	if(isBrowser && getBg && !m->host.isEmpty())
	{
		return HttpAnswer::BG;
	}

	if(isBrowser && getMetadata && !m->host.isEmpty())
	{
		return HttpAnswer::MetaData;
	}

	if(isBrowser && !getMp3 && !m->host.isEmpty())
	{
		return HttpAnswer::HTML5;
	}

	if(getMp3 && !m->host.isEmpty())
	{
		return HttpAnswer::MP3;
	}

	if(getPlaylist && !m->host.isEmpty())
	{
		return HttpAnswer::Playlist;
	}

	if(getReceived)
	{
		m->icy = icy;

		return HttpAnswer::OK;
	}

	return HttpAnswer::Fail;
}

bool StreamHttpParser::isIcyStream() const
{
	return m->icy;
}

QString StreamHttpParser::host() const
{
	return m->host;
}
