/** 
 * @file lluriparser.cpp
 * @author Protey
 * @date 2014-10-07
 * @brief Implementation of the LLUriParser class.
 *
 * $LicenseInfo:firstyear=2014&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2014, Linden Research, Inc.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 * 
 * 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#include "linden_common.h"
#include "lluriparser.h"

LLUriParser::LLUriParser(const std::string& u) : mTmpScheme(false), mNormalizedTmp(false), mRes(0)
{
	mState.uri = &mUri;

	if (u.find("://") == std::string::npos)
	{
		mNormalizedUri = "http://";
		mTmpScheme = true;
	}

	mNormalizedUri += u.c_str();

	mRes = parse();
}

LLUriParser::~LLUriParser()
{
	uriFreeUriMembersA(&mUri);
}

S32 LLUriParser::parse()
{
	mRes = uriParseUriA(&mState, mNormalizedUri.c_str());
	return mRes;
}

const char * LLUriParser::scheme() const
{
	return mScheme.c_str();
}

void LLUriParser::sheme(const std::string& s)
{
	mTmpScheme = !s.size();
	mScheme = s;
}

const char * LLUriParser::port() const
{
	return mPort.c_str();
}

void LLUriParser::port(const std::string& s)
{
	mPort = s;
}

const char * LLUriParser::host() const
{
	return mHost.c_str();
}

void LLUriParser::host(const std::string& s)
{
	mHost = s;
}

const char * LLUriParser::path() const
{
	return mPath.c_str();
}

void LLUriParser::path(const std::string& s)
{
	mPath = s;
}

const char * LLUriParser::query() const
{
	return mQuery.c_str();
}

void LLUriParser::query(const std::string& s)
{
	mQuery = s;
}

const char * LLUriParser::fragment() const
{
	return mFragment.c_str();
}

void LLUriParser::fragment(const std::string& s)
{
	mFragment = s;
}

void LLUriParser::textRangeToString(UriTextRangeA& textRange, std::string& str)
{
	if (textRange.first != NULL && textRange.afterLast != NULL && textRange.first < textRange.afterLast)
	{
		const ptrdiff_t len = textRange.afterLast - textRange.first;
		str.assign(textRange.first, static_cast<std::string::size_type>(len));
	}
	else
	{
		str = LLStringUtil::null;
	}
}

void LLUriParser::extractParts()
{
	if (mTmpScheme || mNormalizedTmp)
	{
		mScheme.clear();
	}
	else
	{
		textRangeToString(mUri.scheme, mScheme);
	}
	
	textRangeToString(mUri.hostText, mHost);
	textRangeToString(mUri.portText, mPort);
	textRangeToString(mUri.query, mQuery);
	textRangeToString(mUri.fragment, mFragment);

	UriPathSegmentA * pathHead = mUri.pathHead;
	while (pathHead)
	{
		std::string partOfPath;
		textRangeToString(pathHead->text, partOfPath);

		mPath += '/';
		mPath += partOfPath;

		pathHead = pathHead->next;
	}
}

S32 LLUriParser::normalize()
{
	mNormalizedTmp = mTmpScheme;
	if (!mRes)
	{
		mRes = uriNormalizeSyntaxExA(&mUri, URI_NORMALIZE_SCHEME | URI_NORMALIZE_HOST);

		if (!mRes)
		{
			S32 chars_required;
			mRes = uriToStringCharsRequiredA(&mUri, &chars_required);

			if (!mRes)
			{
				chars_required++;
				std::vector<char> label_buf(chars_required);
				mRes = uriToStringA(&label_buf[0], &mUri, chars_required, NULL);

				if (!mRes)
				{
					mNormalizedUri = &label_buf[mTmpScheme ? 7 : 0];
					mTmpScheme = false;
				}
			}
		}
	}

	if(mTmpScheme)
	{
		mNormalizedUri = mNormalizedUri.substr(7);
		mTmpScheme = false;
	}

	return mRes;
}

void LLUriParser::glue(std::string& uri) const
{
	std::string first_part;
	glueFirst(first_part);

	std::string second_part;
	glueSecond(second_part);

	uri = first_part + second_part;
}

void LLUriParser::glueFirst(std::string& uri, bool use_scheme) const
{
	if (use_scheme && mScheme.size())
	{
		uri = mScheme;
		uri += "://";
	}
	else
	{
		uri.clear();
	}

	uri += mHost;
}

void LLUriParser::glueSecond(std::string& uri) const
{
	if (mPort.size())
	{
		uri = ':';
		uri += mPort;
	}
	else
	{
		uri.clear();
	}

	uri += mPath;

	if (mQuery.size())
	{
		uri += '?';
		uri += mQuery;
	}

	if (mFragment.size())
	{
		uri += '#';
		uri += mFragment;
	}
}

bool LLUriParser::test() const
{
	std::string uri;
	glue(uri);

	return uri == mNormalizedUri;
}

const char * LLUriParser::normalizedUri() const
{
	return mNormalizedUri.c_str();
}