/*
	Regular merchants
*/
void CUser::MerchantOpen()
{
    int16 errorCode = 0;
    if (isDead())
        errorCode = MERCHANT_OPEN_DEAD;
    else if (isStoreOpen())
        errorCode = MERCHANT_OPEN_SHOPPING;
    else if (isTrading())
        errorCode = MERCHANT_OPEN_TRADING;
    else if (GetZoneID() > 21 || GetZoneID() <= ELMORAD)
        errorCode = MERCHANT_OPEN_INVALID_ZONE;
    else if (GetLevel() < 30)
        errorCode = MERCHANT_OPEN_UNDERLEVELED;
    else if (isMerchanting())
        errorCode = MERCHANT_OPEN_MERCHANTING;
    else
        errorCode = MERCHANT_OPEN_SUCCESS;

    Packet result(WIZ_MERCHANT, uint8(MERCHANT_OPEN));
    result << errorCode;
    Send(&result);

    // If we're already merchanting, user may be desynced
    // so we need to close our current merchant first.
    if (errorCode == MERCHANT_OPEN_MERCHANTING)
        MerchantClose();
}
void CUser::MerchantItemBuy(Packet & pkt)
{
    uint32 itemid, req_gold;
    uint16 item_count, leftover_count;
    uint8 item_slot, dest_slot;

    CUser *pMerchant = g_pMain->GetUserPtr(m_sMerchantsSocketID);
    if (pMerchant == nullptr)
        return;

    pkt >> itemid >> item_count >> item_slot >> dest_slot;

    // Make sure the slots are correct and that we're actually buying at least 1 item.
    if (item_slot >= MAX_MERCH_ITEMS
            || dest_slot >= HAVE_MAX
            || item_count == 0)
        return;

    // Grab pointers to the items.
    _MERCH_DATA *pMerch = &pMerchant->m_arMerchantItems[item_slot];
    _ITEM_DATA *pItem = GetItem(SLOT_MAX + dest_slot);

    // Make sure the merchant actually has that item in that slot
    // and that they have enough
    if (pMerch->nNum != itemid
            || pMerch->sCount < item_count)
        return;

    // If it's not stackable, and we're specifying something other than 1
    // we really don't care to handle this request...
    _ITEM_TABLE *proto = g_pMain->GetItemPtr(itemid);
    if (proto == nullptr
            || !proto->m_bCountable && item_count != 1)
        return;

    // Do we have enough coins?
    req_gold = pMerch->nPrice * item_count;
    if (m_iGold < req_gold)
        return;

    // If the slot's not empty
    if (pItem->nNum != 0
            // and we already have an item that isn't the same item
            // or it's the same item but the item's not stackable...
            && (pItem->nNum != itemid || !proto->m_bCountable))
        return;

    leftover_count = pMerch->sCount - item_count;
    pMerchant->GoldChange(GetSocketID(), req_gold);
    pItem->nNum = itemid;
    pItem->sCount += item_count;
    pItem->sDuration = pMerch->sDuration;
    pItem->nSerialNum = pMerch->nSerialNum;

    pMerch->sCount -= item_count;

    // TO-DO : Proper checks for the removal of the items in the array, we're now assuming everything gets bought
    if (pMerch->sCount == 0)
        memset(pMerch, 0, sizeof(_MERCH_DATA));

    SetSlotItemValue();
    pMerchant->SetSlotItemValue();

    SetUserAbility();
    pMerchant->SetUserAbility();

    SendStackChange(itemid, pItem->sCount, pItem->sDuration, dest_slot,
                    (pItem->sCount == item_count)); // is it a new item?

    pMerchant->SendStackChange(itemid, leftover_count, pMerch->sDuration,
                               pMerch->bOriginalSlot - SLOT_MAX);

    Packet result(WIZ_MERCHANT, uint8(MERCHANT_ITEM_PURCHASED));
    result << itemid << GetName();
    pMerchant->Send(&result);

    result.clear();

    result	<< uint8(MERCHANT_ITEM_BUY) << uint16(1)
            << itemid << leftover_count
            << item_slot << dest_slot;
    Send(&result);

    if (item_slot < 4 && leftover_count == 0)
    {
        result.Initialize(WIZ_MERCHANT_INOUT);
        result << uint8(2) << m_sMerchantsSocketID << uint16(item_slot);
        pMerchant->SendToRegion(&result);
    }

    int nItemsRemaining = 0;
    for (int i = 0; i < MAX_MERCH_ITEMS; i++)
    {
        if (pMerchant->m_arMerchantItems[i].nNum != 0)
            nItemsRemaining++;
    }

    if (nItemsRemaining == 0)
        MerchantClose();
}
void CUser::MerchantProcess(char *pBuf)
{
	int index = 0;
	BYTE subcommand = GetByte(pBuf, index);

	switch (subcommand)
	{
		// Regular merchants
		case MERCHANT_OPEN: 
			MerchantOpen(pBuf+index); 
			break;

		case MERCHANT_CLOSE: 
			MerchantClose(); 
			break;

		case MERCHANT_ITEM_ADD: 
			MerchantItemAdd(pBuf+index); 
			break;

		case MERCHANT_ITEM_CANCEL: 
			MerchantItemCancel(pBuf+index); 
			break;

		case MERCHANT_ITEM_LIST: 
			MerchantItemList(pBuf+index); 
			break;

		case MERCHANT_ITEM_BUY: 
			MerchantItemBuy(pBuf+index); 
			break;

		case MERCHANT_INSERT: 
			MerchantInsert(pBuf+index); 
			break;

		case MERCHANT_TRADE_CANCEL: 
			CancelMerchant(); 
			break;

#if __VERSION >= 1700
		// Buying merchants
		case MERCHANT_BUY_OPEN: 
			BuyingMerchantOpen(pBuf+index); 
			break;

		case MERCHANT_BUY_CLOSE: 
			BuyingMerchantClose(); 
			break;

		case MERCHANT_BUY_LIST: 
			BuyingMerchantList(pBuf+index); 
			break;

		case MERCHANT_BUY_INSERT: 
			BuyingMerchantInsert(pBuf+index); 
			break;

		case MERCHANT_BUY_BUY: // seeya!
			BuyingMerchantBuy(pBuf+index); 
			break;
#endif
	}
}
void CUser::MerchantProcess(Packet & pkt)
{
    uint8 opcode = pkt.read<uint8>();
    switch (opcode)
    {
    // Regular merchants
    case MERCHANT_OPEN:
        MerchantOpen();
        break;

    case MERCHANT_CLOSE:
        MerchantClose();
        break;

    case MERCHANT_ITEM_ADD:
        MerchantItemAdd(pkt);
        break;

    case MERCHANT_ITEM_CANCEL:
        MerchantItemCancel(pkt);
        break;

    case MERCHANT_ITEM_LIST:
        MerchantItemList(pkt);
        break;

    case MERCHANT_ITEM_BUY:
        MerchantItemBuy(pkt);
        break;

    case MERCHANT_INSERT:
        MerchantInsert(pkt);
        break;

    case MERCHANT_TRADE_CANCEL:
        CancelMerchant();
        break;

#if __VERSION >= 1700
    // Buying merchants
    case MERCHANT_BUY_OPEN:
        BuyingMerchantOpen(pkt);
        break;

    case MERCHANT_BUY_CLOSE:
        BuyingMerchantClose();
        break;

    case MERCHANT_BUY_LIST:
        BuyingMerchantList(pkt);
        break;

    case MERCHANT_BUY_INSERT:
        BuyingMerchantInsert(pkt);
        break;

    case MERCHANT_BUY_BUY: // seeya!
        BuyingMerchantBuy(pkt);
        break;
#endif
    }
}
void CUser::MerchantItemBuy(Packet & pkt)
{
	uint32 itemid, req_gold;
	uint16 item_count, leftover_count;
	uint8 item_slot, dest_slot;
	CUser * m_MerchantUser = NULL;

	if (m_sMerchantsSocketID < 0 || m_sMerchantsSocketID > MAX_USER)
		return;

	m_MerchantUser = g_pMain->GetUserPtr(m_sMerchantsSocketID);
	if (m_MerchantUser == NULL)
		return;

	pkt >> itemid >> item_count >> item_slot >> dest_slot;

	if (item_slot < 0 
		|| item_slot > MAX_MERCH_ITEMS
		|| item_count == 0)
		return;

	if (m_MerchantUser->m_arSellingItems[item_slot].nNum != itemid
		||m_MerchantUser->m_arSellingItems[item_slot].sCount < item_count)
		return;

	req_gold = m_MerchantUser->m_arSellingItems[item_slot].nPrice * item_count;
	if (m_pUserData->m_iGold < req_gold)
		return;

	if (m_pUserData->m_sItemArray[SLOT_MAX+dest_slot].nNum != 0 && m_pUserData->m_sItemArray[SLOT_MAX+dest_slot].nNum != itemid)
		return;

	leftover_count = m_MerchantUser->m_arSellingItems[item_slot].sCount - item_count;
	m_MerchantUser->GoldChange(GetSocketID(), req_gold);
	m_pUserData->m_sItemArray[SLOT_MAX+dest_slot].nNum = itemid;
	m_pUserData->m_sItemArray[SLOT_MAX+dest_slot].sCount += item_count;
	m_pUserData->m_sItemArray[SLOT_MAX+dest_slot].sDuration = m_MerchantUser->m_arSellingItems[item_slot].sDuration;
	m_pUserData->m_sItemArray[SLOT_MAX+dest_slot].nSerialNum = m_MerchantUser->m_arSellingItems[item_slot].nSerialNum;
	//TO-DO : Proper checks for the removal of the items in the array, we're now assuming everything gets bought
	if(item_count == m_MerchantUser->m_arSellingItems[item_slot].sCount)
		memset(&m_MerchantUser->m_arSellingItems[item_slot], 0, sizeof(_MERCH_DATA)); //Remove the item from the arSellingItems array.
	else
		m_MerchantUser->m_arSellingItems[item_slot].sCount -= item_count;

	SetSlotItemValue();
	m_MerchantUser->SetSlotItemValue();

	SetUserAbility();
	m_MerchantUser->SetUserAbility();


	if (m_pUserData->m_sItemArray[SLOT_MAX+dest_slot].sCount == item_count)
		SendStackChange(itemid, m_pUserData->m_sItemArray[SLOT_MAX+dest_slot].sCount, m_pUserData->m_sItemArray[SLOT_MAX+dest_slot].sDuration, dest_slot, true);
	else
		SendStackChange(itemid, m_pUserData->m_sItemArray[SLOT_MAX+dest_slot].sCount, m_pUserData->m_sItemArray[SLOT_MAX+dest_slot].sDuration, dest_slot);

	m_MerchantUser->SendStackChange(itemid, leftover_count, m_MerchantUser->m_arSellingItems[item_slot].sDuration, m_MerchantUser->m_arSellingItems[item_slot].bOriginalSlot - SLOT_MAX);

	Packet result(WIZ_MERCHANT, uint8(MERCHANT_ITEM_PURCHASED));
	result << itemid << m_pUserData->m_id;
	m_MerchantUser->Send(&result);

	result.clear();

	result	<< uint8(MERCHANT_ITEM_BUY) << uint16(1)
			<< itemid
			<< leftover_count
			<< item_slot << dest_slot;
	Send(&result);

	if(item_slot < 4 && leftover_count == 0)
	{
		result.Initialize(WIZ_MERCHANT_INOUT);
		result << uint8(2) << m_sMerchantsSocketID << uint16(item_slot);
		m_MerchantUser->SendToRegion(&result);
	}

	int n = 0;
	for(int i = 0; i < MAX_MERCH_ITEMS; i++)
		if(m_MerchantUser->m_arSellingItems[i].nNum == 0)
			n++;
	if(n == 0)
		MerchantClose();
}