/**********************************************************************
 * LeechCraft - modular cross-platform feature rich internet client.
 * Copyright (C) 2006-2014  Georg Rudoy
 *
 * Boost Software License - Version 1.0 - August 17th, 2003
 *
 * Permission is hereby granted, free of charge, to any person or organization
 * obtaining a copy of the software and accompanying documentation covered by
 * this license (the "Software") to use, reproduce, display, distribute,
 * execute, and transmit the Software, and to prepare derivative works of the
 * Software, and to permit third-parties to whom the Software is furnished to
 * do so, all subject to the following:
 *
 * The copyright notices in the Software and this entire statement, including
 * the above license grant, this restriction and the following disclaimer,
 * must be included in all copies of the Software, in whole or in part, and
 * all derivative works of the Software, unless such copies or derivative
 * works are solely in the form of machine-executable object code generated by
 * a source language processor.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 **********************************************************************/

#include "desktopparser.h"
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/adapted.hpp>

namespace LeechCraft
{
namespace Util
{
namespace XDG
{
	namespace
	{
		typedef boost::variant<std::string, std::vector<std::string>> FieldVal_t;
		typedef boost::optional<std::string> Lang_t;
		struct Field
		{
			std::string Name_;
			Lang_t Lang_;
			FieldVal_t Val_;
		};
		typedef std::vector<Field> Fields_t;

		struct Group
		{
			std::string Name_;
			Fields_t Fields_;
		};
		typedef std::vector<Group> Groups_t;

		struct File
		{
			Groups_t Groups_;
		};
	}
}
}
}

BOOST_FUSION_ADAPT_STRUCT (LeechCraft::Util::XDG::Field,
		(std::string, Name_)
		(LeechCraft::Util::XDG::Lang_t, Lang_)
		(LeechCraft::Util::XDG::FieldVal_t, Val_));

BOOST_FUSION_ADAPT_STRUCT (LeechCraft::Util::XDG::Group,
		(std::string, Name_)
		(LeechCraft::Util::XDG::Fields_t, Fields_));

BOOST_FUSION_ADAPT_STRUCT (LeechCraft::Util::XDG::File,
		(LeechCraft::Util::XDG::Groups_t, Groups_));

namespace LeechCraft
{
namespace Util
{
namespace XDG
{
	namespace
	{
		namespace ascii = boost::spirit::ascii;
		namespace qi = boost::spirit::qi;
		namespace phoenix = boost::phoenix;

		template<typename Iter>
		struct Parser : qi::grammar<Iter, File ()>
		{
			qi::rule<Iter, File ()> Start_;
			qi::rule<Iter, Group ()> Group_;
			qi::rule<Iter, std::string ()> GroupName_;
			qi::rule<Iter, std::string ()> Lang_;
			qi::rule<Iter, void ()> KeyValSep_;
			qi::rule<Iter, std::string ()> LineValSingle_;
			qi::rule<Iter, FieldVal_t ()> LineVal_;
			qi::rule<Iter, Field ()> Line_;
			qi::rule<Iter, void ()> Comment_;

			Parser ()
			: Parser::base_type (Start_)
			{
				auto eol = qi::lit ("\n");
				Comment_ %= qi::lit ("#") >> *(qi::char_ - '\r' - '\n') >> eol;

				Lang_ %= '[' >> qi::lexeme [+(qi::char_ ("a-zA-Z0-9@_-"))] >> ']';

				KeyValSep_ %= *(qi::lit (' ')) >> '=' >> *(qi::lit (' '));

				LineValSingle_ %= qi::lexeme [+((qi::lit ("\\;") | (qi::char_ - ';' - '\r' - '\n')))];
				LineVal_ %= ((LineValSingle_ % ';') >> -qi::lit (";")) | LineValSingle_;

				Line_ %= qi::lexeme [+(qi::char_ ("a-zA-Z0-9-"))] >>
						-Lang_ >>
						KeyValSep_ >>
						-LineVal_ >>
						eol;

				GroupName_ %= '[' >> qi::lexeme [+(qi::char_ ("a-zA-Z0-9 "))] >> ']';

				Group_ %= GroupName_ >> eol >>
						*(Comment_ | Line_ | eol);

				Start_ %= *Comment_ >> +Group_;

				qi::on_error<qi::fail> (Start_,
						std::cout << phoenix::val ("Error! Expecting") << qi::_4
								<< phoenix::val (" here: \"") << phoenix::construct<std::string> (qi::_3, qi::_2)
								<< phoenix::val ("\"") << std::endl);
			}
		};

		template<typename Iter>
		File Parse (Iter begin, Iter end)
		{
			File res;
			qi::parse (begin, end, Parser<Iter> (), res);
			return res;
		}

		QString ToUtf8 (const std::string& str)
		{
			return QString::fromUtf8 (str.c_str ());
		}

		struct ValGetter : public boost::static_visitor<QStringList>
		{
			QStringList operator() (const std::string& str) const
			{
				return QStringList (ToUtf8 (str));
			}

			QStringList operator() (const std::vector<std::string>& vec) const
			{
				QStringList result;
				std::transform (vec.begin (), vec.end (), std::back_inserter (result), ToUtf8);
				return result;
			}
		};
	}

	auto DesktopParser::operator() (const QByteArray& data) -> Result_t
	{
		const auto& file = Parse (data.begin (), data.end ());

		Result_t result;
		for (const auto& item : file.Groups_)
		{
			Group_t group;
			for (const auto& field : item.Fields_)
			{
				const auto& values = boost::apply_visitor (ValGetter (), field.Val_);
				const auto& lang = field.Lang_ ? ToUtf8 (*field.Lang_) : QString ();
				group [ToUtf8 (field.Name_)] [lang] = values;
			}
			result [ToUtf8 (item.Name_)] = group;
		}
		return result;
	}
}
}
}
