#include "StdAfx.h"

CQuotesProviderGoogleFinance::CQuotesProviderGoogleFinance()
{
}

CQuotesProviderGoogleFinance::~CQuotesProviderGoogleFinance()
{
}

static tstring build_url(MCONTACT hContact, const tstring& rsURL)
{
	tostringstream o;
	o << rsURL << L"?q=" << Quotes_DBGetStringT(hContact, QUOTES_MODULE_NAME, DB_STR_QUOTE_ID);
	return o.str();
}

struct CGoogleInfo
{
	enum
	{
		giRate = 0x0001,
		giOpen = 0x0002,
		giPercentChangeAfterHours = 0x0004,
		giPercentChangeToYesterdayClose = 0x0008
	};

	CGoogleInfo()
		: m_dRate(0.0), m_dOpenValue(0.0), m_dPercentChangeAfterHours(0.0), m_dPercentChangeToYersterdayClose(0.0), m_nFlags(0)
	{
	}

	tstring m_sCmpName;
	double m_dRate;
	double m_dOpenValue;
	double m_dPercentChangeAfterHours;
	double m_dPercentChangeToYersterdayClose;
	byte m_nFlags;
};

tstring make_rate_id_value(const tstring& rsCmpID, int nFlags)
{
	tostringstream o;
	o << L"ref_" << rsCmpID;
	switch (nFlags) {
	default:
		assert(!"Unknown type of value");
	case CGoogleInfo::giRate:
		o << L"_l";
		break;
	case CGoogleInfo::giPercentChangeAfterHours:
		o << L"_ecp";
		break;
	case CGoogleInfo::giPercentChangeToYesterdayClose:
		o << L"_cp";
		break;
	}

	return o.str();
}

tstring get_var_value(const tstring& rsHTML, LPCTSTR pszVarName, size_t cVarNameLength)
{
	tstring sResult;
	tstring::size_type n = rsHTML.find(pszVarName);
	if (tstring::npos != n) {
		size_t cLengthHTML = rsHTML.size();
		for (size_t i = n + cVarNameLength; i < cLengthHTML; ++i) {
			wchar_t c = rsHTML[i];
			if (';' == c)
				break;

			sResult.push_back(c);
		}
	}

	return sResult;
}

tstring get_company_id(const tstring& rsHTML)
{
	static LPCTSTR pszVarName = L"setCompanyId(";
	static size_t cVarNameLength = mir_wstrlen(pszVarName);

	tstring sResult;
	tstring::size_type n = rsHTML.find(pszVarName);
	if (tstring::npos != n) {
		size_t cLengthHTML = rsHTML.size();
		for (size_t i = n + cVarNameLength; i < cLengthHTML; ++i) {
			wchar_t c = rsHTML[i];
			if (')' == c)
				break;

			sResult.push_back(c);
		}
	}
	return sResult;
}

tstring get_company_name(const tstring& rsHTML)
{
	static LPCTSTR pszVarName = L"var _companyName = ";
	static size_t cVarNameLength = mir_wstrlen(pszVarName);

	tstring s = get_var_value(rsHTML, pszVarName, cVarNameLength);
	if (s.size() > 0 && '\'' == s[0])
		s.erase(s.begin());

	if (s.size() > 0 && '\'' == s[s.size() - 1])
		s.erase(s.rbegin().base() - 1);

	return s;
}

bool get_double_value(const tstring& rsText, double& rdValue)
{
	tistringstream input(rsText);
	input.imbue(std::locale("English_United States.1252"));
	input >> rdValue;

	if ((true == input.bad()) || (true == input.fail())) {
		tistringstream inputSys(rsText);
		input.imbue(GetSystemLocale());
		input >> rdValue;
		return (false == inputSys.bad()) && (false == inputSys.fail());
	}

	return true;
}

bool get_rate(const IHTMLNode::THTMLNodePtr& pRate, CGoogleInfo& rInfo)
{
	tstring sRate = pRate->GetText();

	if (true == get_double_value(sRate, rInfo.m_dRate)) {
		rInfo.m_nFlags |= CGoogleInfo::giRate;
		return true;
	}

	return false;
}

bool get_inline_data(const IHTMLNode::THTMLNodePtr& pNode, CGoogleInfo& rInfo)
{
	size_t cChild = pNode->GetChildCount();
	for (size_t i = 0; i < cChild; ++i) {
		IHTMLNode::THTMLNodePtr pChild = pNode->GetChildPtr(i);
		size_t c = pChild->GetChildCount();
		assert(2 == c);
		if (c >= 2) {
			IHTMLNode::THTMLNodePtr pName = pChild->GetChildPtr(0);

			tstring sName = pName->GetText();
			if (0 == mir_wstrcmpi(sName.c_str(), L"Open")) {
				IHTMLNode::THTMLNodePtr pValue = pChild->GetChildPtr(1);
				tstring sValue = pValue->GetText();
				if (true == get_double_value(sValue, rInfo.m_dOpenValue)) {
					rInfo.m_nFlags |= CGoogleInfo::giOpen;
				}
				return true;
			}
		}
	}

	return false;
}

bool get_dif_value(const IHTMLNode::THTMLNodePtr& pNode, CGoogleInfo& rInfo, int nItem)
{
	tstring sDiff = pNode->GetText();
	// this value is in brackets and it has percentage sign. 
	// Remove these symbols.
	for (tstring::iterator i = sDiff.begin(); i != sDiff.end();) {
		wchar_t s = *i;
		if ('(' == s || ')' == s || '%' == s)
			i = sDiff.erase(i);
		else
			++i;
	}

	double* pValue = NULL;
	switch (nItem) {
	case CGoogleInfo::giPercentChangeAfterHours:
		pValue = &rInfo.m_dPercentChangeAfterHours;
		break;
	case CGoogleInfo::giPercentChangeToYesterdayClose:
		pValue = &rInfo.m_dPercentChangeToYersterdayClose;
		break;
	}

	assert(pValue);

	if ((pValue) && (true == get_double_value(sDiff, *pValue))) {
		rInfo.m_nFlags |= nItem;
		return true;
	}

	return false;
}

bool parse_responce(const tstring& rsHTML, CGoogleInfo& rInfo)
{
	IHTMLEngine::THTMLParserPtr pHTMLParser = CModuleInfo::GetHTMLEngine()->GetParserPtr();
	IHTMLNode::THTMLNodePtr pRoot = pHTMLParser->ParseString(rsHTML);
	if (pRoot) {
		tstring sCmpID = get_company_id(rsHTML);
		if (false == sCmpID.empty()) {
			tstring sRateID = make_rate_id_value(sCmpID, CGoogleInfo::giRate);
			IHTMLNode::THTMLNodePtr pRate = pRoot->GetElementByID(sRateID);
			if (pRate && get_rate(pRate, rInfo)) {
				rInfo.m_sCmpName = get_company_name(rsHTML);

				IHTMLNode::THTMLNodePtr pInline = pRoot->GetElementByID(L"snap-data");
				if (pInline) {
					get_inline_data(pInline, rInfo);
				}

				tstring sDiffID = make_rate_id_value(sCmpID, CGoogleInfo::giPercentChangeAfterHours);
				IHTMLNode::THTMLNodePtr pDiff = pRoot->GetElementByID(sDiffID);
				if (pDiff) {
					get_dif_value(pDiff, rInfo, CGoogleInfo::giPercentChangeAfterHours);
				}

				sDiffID = make_rate_id_value(sCmpID, CGoogleInfo::giPercentChangeToYesterdayClose);
				pDiff = pRoot->GetElementByID(sDiffID);
				if (pDiff) {
					get_dif_value(pDiff, rInfo, CGoogleInfo::giPercentChangeToYesterdayClose);
				}

				return true;
			}
		}
	}

	return false;
}

void CQuotesProviderGoogleFinance::RefreshQuotes(TContracts& anContacts)
{
	CHTTPSession http;
	tstring sURL = GetURL();
	bool bUseExtendedStatus = CModuleInfo::GetInstance().GetExtendedStatusFlag();

	for (TContracts::const_iterator i = anContacts.begin(); i != anContacts.end() && IsOnline(); ++i) {
		MCONTACT hContact = *i;

		if (bUseExtendedStatus)
			SetContactStatus(hContact, ID_STATUS_OCCUPIED);

		tstring sFullURL = build_url(hContact, sURL);

		if ((true == http.OpenURL(sFullURL)) && (true == IsOnline())) {
			tstring sHTML;
			if ((true == http.ReadResponce(sHTML)) && (true == IsOnline())) {
				CGoogleInfo Info;
				parse_responce(sHTML, Info);
				if (true == IsOnline()) {
					if (Info.m_nFlags&CGoogleInfo::giRate) {
						if (Info.m_nFlags&CGoogleInfo::giOpen) {
							Quotes_DBWriteDouble(hContact, QUOTES_MODULE_NAME, DB_STR_GOOGLE_FINANCE_OPEN_VALUE, Info.m_dOpenValue);
						}
						if (Info.m_nFlags&CGoogleInfo::giPercentChangeAfterHours) {
							Quotes_DBWriteDouble(hContact, QUOTES_MODULE_NAME, DB_STR_GOOGLE_FINANCE_DIFF, Info.m_dPercentChangeAfterHours);
						}
						if (Info.m_nFlags&CGoogleInfo::giPercentChangeToYesterdayClose) {
							Quotes_DBWriteDouble(hContact, QUOTES_MODULE_NAME, DB_STR_GOOGLE_FINANCE_PERCENT_CHANGE_TO_YERSTERDAY_CLOSE, Info.m_dPercentChangeToYersterdayClose);
						}
						if (false == Info.m_sCmpName.empty()) {
							db_set_ws(hContact, QUOTES_MODULE_NAME, DB_STR_QUOTE_DESCRIPTION, Info.m_sCmpName.c_str());
						}

						WriteContactRate(hContact, Info.m_dRate);
						continue;
					}
				}
			}
		}

		SetContactStatus(hContact, ID_STATUS_NA);
	}
}

void CQuotesProviderGoogleFinance::Accept(CQuotesProviderVisitor& visitor)const
{
	CQuotesProviderFinance::Accept(visitor);
	visitor.Visit(*this);
}