#include "skype.h"

wchar_t *CSkypeProto::TransferFailureReasons[] =
{
	LPGENW("")															/* ---							*/,
	LPGENW("SENDER_NOT_AUTHORIZED")										/* SENDER_NOT_AUTHORISED		*/,
	LPGENW("REMOTELY_CANCELED")											/* REMOTELY_CANCELLED			*/,
	LPGENW("FAILED_READ")												/* FAILED_READ					*/,
	LPGENW("FAILED_REMOTE_READ")										/* FAILED_REMOTE_READ			*/,
	LPGENW("FAILED_WRITE")												/* FAILED_WRITE					*/,
	LPGENW("FAILED_REMOTE_WRITE")										/* FAILED_REMOTE_WRITE			*/,
	LPGENW("REMOTE_DOES_NOT_SUPPORT_FT")								/* REMOTE_DOES_NOT_SUPPORT_FT	*/,
	LPGENW("REMOTE_OFFLINE_FOR_TOO_LONG")								/* REMOTE_OFFLINE_FOR_TOO_LONG	*/,
	LPGENW("TOO_MANY_PARALLEL")											/* TOO_MANY_PARALLEL			*/,
	LPGENW("PLACEHOLDER_TIMEOUT")										/* PLACEHOLDER_TIMEOUT			*/
};

void CSkypeProto::OnTransferChanged(const TransferRef &transfer, int prop)
{
	switch (prop)
	{
	case Transfer::P_STATUS:
		{
			Transfer::FAILUREREASON reason;

			SEBinary guid;
			transfer->GetPropChatmsgGuid(guid);			

			MessageRef msgRef;
			this->GetMessageByGuid(guid, msgRef);

			uint oid = msgRef->getOID();

			uint fOid = transfer->getOID();

			Transfer::STATUS status;
			transfer->GetPropStatus(status);
			switch(status)
			{
			case Transfer::CONNECTING:
				this->SendBroadcast(this->transferts[oid].pfts.hContact, ACKTYPE_FILE, ACKRESULT_CONNECTING, (HANDLE)oid, 0);
				break;
			case Transfer::TRANSFERRING:
			case Transfer::TRANSFERRING_OVER_RELAY:
				this->SendBroadcast(this->transferts[oid].pfts.hContact, ACKTYPE_FILE, ACKRESULT_CONNECTED, (HANDLE)oid, 0);
				break;
			case Transfer::FAILED:				
				transfer->GetPropFailurereason(reason);
				this->Log(L"File transfer failed: %s", CSkypeProto::TransferFailureReasons[reason]);
				this->transferList.remove_val(transfer);
				this->transferts[oid].files[fOid].isCanceled = true;
				break;
			case Transfer::COMPLETED:
				this->transferList.remove_val(transfer);
				this->transferts[oid].files[fOid].isCompleted = true;
				this->transferts[oid].pfts.totalProgress += this->transferts[oid].files[fOid].size - this->transferts[oid].files[fOid].transfered;
				break;
			case Transfer::CANCELLED:
			case Transfer::CANCELLED_BY_REMOTE:
				transfer->GetPropFailurereason(reason);
				this->Log(L"File transfer cancelled: %s", CSkypeProto::TransferFailureReasons[reason]);
				this->transferList.remove_val(transfer);
				this->transferts[oid].files[fOid].isCanceled = true;
				break;
			}

			int isNotAll = false;
			for (auto i = this->transferts[oid].files.begin(); i != this->transferts[oid].files.end(); i++)
				if ( !(i->second.isCanceled || i->second.isCompleted))
				{
					isNotAll = true;
					break;
				}
			if (isNotAll)
			{
				SEString data;
				uint fOid = transfer->getOID();
				transfer->GetPropBytestransferred(data);
				Sid::uint64 tb = data.toUInt64();
				this->transferts[oid].pfts.totalProgress += tb;
				this->transferts[oid].files[fOid].transfered = tb;

				this->SendBroadcast(this->transferts[oid].pfts.hContact, ACKTYPE_FILE, ACKRESULT_DATA, (HANDLE)oid, (LPARAM)&this->transferts[oid].pfts);
			}
			else
			{
				this->SendBroadcast(this->transferts[oid].pfts.hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, (HANDLE)oid, 0);
				delete [] this->transferts[oid].pfts.ptszFiles;
				this->transferts.erase(this->transferts.find(oid));
			}			
		}
		break;

	case Transfer::P_BYTESTRANSFERRED:
		{
			SEString data;

			SEBinary guid;
			transfer->GetPropChatmsgGuid(guid);			

			MessageRef msgRef;
			this->GetMessageByGuid(guid, msgRef);

			uint oid = msgRef->getOID();

			uint fOid = transfer->getOID();
			transfer->GetPropBytestransferred(data);
			Sid::uint64 tb = data.toUInt64();
			this->transferts[oid].pfts.totalProgress += tb;
			this->transferts[oid].files[fOid].transfered = tb;

			this->SendBroadcast(this->transferts[oid].pfts.hContact, ACKTYPE_FILE, ACKRESULT_DATA, (HANDLE)oid, (LPARAM)&this->transferts[oid].pfts);
		}
		break;
	}
}

void CSkypeProto::OnFileEvent(const ConversationRef &conversation, const MessageRef &message)
{
	SEString data;
	Transfer::TYPE transferType;
	Transfer::STATUS transferStatus;

	ContactRef author;
	message->GetPropAuthor(data);
	this->GetContact(data, author);

	TransferRefs transfers;
	message->GetTransfers(transfers);

	FileTransferParam ftp;
	ftp.pfts.flags = PFTS_RECEIVING | PFTS_UNICODE;
	ftp.pfts.hContact = this->AddContact(author, true);
	ftp.pfts.totalFiles = (int)transfers.size();
	ftp.pfts.ptszFiles = new wchar_t*[transfers.size() + 1];

	int nifc = 0;
	for (size_t i = 0; i < transfers.size(); i++)
	{
		auto transfer = transfers[i];

		// For incomings, we need to check for transfer status, just to be sure.
		// In some cases, a transfer can appear with STATUS == PLACEHOLDER
		// As such transfers cannot be accepted, we will need to just store
		// the reference to Transfer Object and then check for further
		// status changes in Transfer::OnChange
		transfer->GetPropStatus(transferStatus);
		if (transferStatus == Transfer::NEW || transferStatus == Transfer::PLACEHOLDER)
		{
			transfer->GetPropType(transferType);
			if (transferType == Transfer::INCOMING)
			{
				transfer->GetPropFilename(data);
				ftp.pfts.ptszFiles[nifc++] = ::mir_utf8decodeW(data);

				transfer.fetch();
				this->transferList.append(transfer);
				//transfer.fetch();

				transfer->GetPropFilesize(data);
				Sid::uint64 size = data.toUInt64();

				ftp.files.insert(std::make_pair(transfer->getOID(), FileParam(size)));
				ftp.pfts.totalBytes += size;		
			}
		}
	}

	if (nifc > 0)
	{
		uint timestamp;
		message->GetPropTimestamp(timestamp);

		auto oid = message->getOID();
		
		this->transferts.insert(std::make_pair(oid, ftp));

		PROTORECVFILET pre = {0};
		pre.flags = PREF_TCHAR;
		pre.fileCount = ftp.pfts.totalFiles;
		pre.timestamp = timestamp;
		pre.tszDescription = L"";
		pre.ptszFiles = ftp.pfts.ptszFiles;
		pre.lParam = (LPARAM)oid;
		::ProtoChainRecvFile(ftp.pfts.hContact, &pre);
	}
	else
	{
		delete [] ftp.pfts.ptszFiles;
	}
}