/**
 * @file llsaleinfo.cpp
 * @brief
 *
 * $LicenseInfo:firstyear=2002&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, 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 <iostream>
#include "linden_common.h"

#include "llsaleinfo.h"

#include "llerror.h"
#include "message.h"
#include "llsdutil.h"

// use this to avoid temporary object creation
const LLSaleInfo LLSaleInfo::DEFAULT;

///----------------------------------------------------------------------------
/// Local function declarations, constants, enums, and typedefs
///----------------------------------------------------------------------------

const char* FOR_SALE_NAMES[] =
{
    "not",
    "orig",
    "copy",
    "cntn"
};

///----------------------------------------------------------------------------
/// Class llsaleinfo
///----------------------------------------------------------------------------

// Default constructor
LLSaleInfo::LLSaleInfo() :
    mSaleType(LLSaleInfo::FS_NOT),
    mSalePrice(DEFAULT_PRICE)
{
}

LLSaleInfo::LLSaleInfo(EForSale sale_type, S32 sale_price) :
    mSaleType(sale_type),
    mSalePrice(sale_price)
{
    mSalePrice = llclamp(mSalePrice, 0, S32_MAX);
}

BOOL LLSaleInfo::isForSale() const
{
    return (FS_NOT != mSaleType);
}

U32 LLSaleInfo::getCRC32() const
{
    U32 rv = (U32)mSalePrice;
    rv += (mSaleType * 0x07073096);
    return rv;
}

BOOL LLSaleInfo::exportLegacyStream(std::ostream& output_stream) const
{
    output_stream << "\tsale_info\t0\n\t{\n";
    output_stream << "\t\tsale_type\t" << lookup(mSaleType) << "\n";
    output_stream << "\t\tsale_price\t" << mSalePrice << "\n";
    output_stream <<"\t}\n";
    return TRUE;
}

LLSD LLSaleInfo::asLLSD() const
{
    LLSD sd = LLSD();
    sd["sale_type"] = lookup(mSaleType);
    sd["sale_price"] = mSalePrice;
    return sd;
}

bool LLSaleInfo::fromLLSD(const LLSD& sd, BOOL& has_perm_mask, U32& perm_mask)
{
    const char *w;

    if (sd["sale_type"].isString())
    {
        mSaleType = lookup(sd["sale_type"].asString().c_str());
    }
    else if(sd["sale_type"].isInteger())
    {
        S8 type = (U8)sd["sale_type"].asInteger();
        mSaleType = static_cast<LLSaleInfo::EForSale>(type);
    }

    mSalePrice = llclamp(sd["sale_price"].asInteger(), 0, S32_MAX);
    w = "perm_mask";
    if (sd.has(w))
    {
        has_perm_mask = TRUE;
        perm_mask = ll_U32_from_sd(sd[w]);
    }
    return true;
}

BOOL LLSaleInfo::importLegacyStream(std::istream& input_stream, BOOL& has_perm_mask, U32& perm_mask)
{
    has_perm_mask = FALSE;

    // *NOTE: Changing the buffer size will require changing the scanf
    // calls below.
    char buffer[MAX_STRING];    /* Flawfinder: ignore */
    char keyword[MAX_STRING];   /* Flawfinder: ignore */
    char valuestr[MAX_STRING];  /* Flawfinder: ignore */
    BOOL success = TRUE;

    keyword[0] = '\0';
    valuestr[0] = '\0';
    while(success && input_stream.good())
    {
        input_stream.getline(buffer, MAX_STRING);
        sscanf( /* Flawfinder: ignore */
            buffer,
            " %254s %254s",
            keyword, valuestr);
        if(!keyword[0])
        {
            continue;
        }
        if(0 == strcmp("{",keyword))
        {
            continue;
        }
        if(0 == strcmp("}", keyword))
        {
            break;
        }
        else if(0 == strcmp("sale_type", keyword))
        {
            mSaleType = lookup(valuestr);
        }
        else if(0 == strcmp("sale_price", keyword))
        {
            sscanf(valuestr, "%d", &mSalePrice);
            mSalePrice = llclamp(mSalePrice, 0, S32_MAX);
        }
        else if (!strcmp("perm_mask", keyword))
        {
            //LL_INFOS() << "found deprecated keyword perm_mask" << LL_ENDL;
            has_perm_mask = TRUE;
            sscanf(valuestr, "%x", &perm_mask);
        }
        else
        {
            LL_WARNS() << "unknown keyword '" << keyword
                    << "' in sale info import" << LL_ENDL;
        }
    }
    return success;
}

void LLSaleInfo::setSalePrice(S32 price)
{
    mSalePrice = price;
    mSalePrice = llclamp(mSalePrice, 0, S32_MAX);
}

LLSD LLSaleInfo::packMessage() const
{
    LLSD result;

    U8 sale_type = static_cast<U8>(mSaleType);
    result["sale-type"]     = (U8)sale_type;
    result["sale-price"]    = (S32)mSalePrice;
    //result[_PREHASH_NextOwnerMask] = mNextOwnerPermMask;
    return result;
}

void LLSaleInfo::packMessage(LLMessageSystem* msg) const
{
    U8 sale_type = static_cast<U8>(mSaleType);
    msg->addU8Fast(_PREHASH_SaleType, sale_type);
    msg->addS32Fast(_PREHASH_SalePrice, mSalePrice);
    //msg->addU32Fast(_PREHASH_NextOwnerMask, mNextOwnerPermMask);
}

void LLSaleInfo::unpackMessage(LLSD sales)
{
    U8 sale_type = (U8)sales["sale-type"].asInteger();
    mSaleType = static_cast<EForSale>(sale_type);

    mSalePrice = (S32)sales["sale-price"].asInteger();
    mSalePrice = llclamp(mSalePrice, 0, S32_MAX);
    //msg->getU32Fast(block, _PREHASH_NextOwnerMask, mNextOwnerPermMask);
}

void LLSaleInfo::unpackMessage(LLMessageSystem* msg, const char* block)
{
    U8 sale_type;
    msg->getU8Fast(block, _PREHASH_SaleType, sale_type);
    mSaleType = static_cast<EForSale>(sale_type);
    msg->getS32Fast(block, _PREHASH_SalePrice, mSalePrice);
    mSalePrice = llclamp(mSalePrice, 0, S32_MAX);
    //msg->getU32Fast(block, _PREHASH_NextOwnerMask, mNextOwnerPermMask);
}

void LLSaleInfo::unpackMultiMessage(LLMessageSystem* msg, const char* block,
                                    S32 block_num)
{
    U8 sale_type;
    msg->getU8Fast(block, _PREHASH_SaleType, sale_type, block_num);
    mSaleType = static_cast<EForSale>(sale_type);
    msg->getS32Fast(block, _PREHASH_SalePrice, mSalePrice, block_num);
    mSalePrice = llclamp(mSalePrice, 0, S32_MAX);
    //msg->getU32Fast(block, _PREHASH_NextOwnerMask, mNextOwnerPermMask, block_num);
}

LLSaleInfo::EForSale LLSaleInfo::lookup(const char* name)
{
    for(S32 i = 0; i < FS_COUNT; i++)
    {
        if(0 == strcmp(name, FOR_SALE_NAMES[i]))
        {
            // match
            return (EForSale)i;
        }
    }
    return FS_NOT;
}

const char* LLSaleInfo::lookup(EForSale type)
{
    if((type >= 0) && (type < FS_COUNT))
    {
        return FOR_SALE_NAMES[S32(type)];
    }
    else
    {
        return NULL;
    }
}

// Allow accumulation of sale info. The price of each is added,
// conflict in sale type results in FS_NOT, and the permissions are
// tightened.
void LLSaleInfo::accumulate(const LLSaleInfo& sale_info)
{
    if(mSaleType != sale_info.mSaleType)
    {
        mSaleType = FS_NOT;
    }
    mSalePrice += sale_info.mSalePrice;
    //mNextOwnerPermMask &= sale_info.mNextOwnerPermMask;
}

bool LLSaleInfo::operator==(const LLSaleInfo &rhs) const
{
    return (
        (mSaleType == rhs.mSaleType) &&
        (mSalePrice == rhs.mSalePrice)
        );
}

bool LLSaleInfo::operator!=(const LLSaleInfo &rhs) const
{
    return (
        (mSaleType != rhs.mSaleType) ||
        (mSalePrice != rhs.mSalePrice)
        );
}


///----------------------------------------------------------------------------
/// Local function definitions
///----------------------------------------------------------------------------

///----------------------------------------------------------------------------
/// exported functions
///----------------------------------------------------------------------------
static const std::string ST_TYPE_LABEL("sale_type");
static const std::string ST_PRICE_LABEL("sale_price");

LLSD ll_create_sd_from_sale_info(const LLSaleInfo& sale)
{
    LLSD rv;
    const char* type = LLSaleInfo::lookup(sale.getSaleType());
    if(!type) type = LLSaleInfo::lookup(LLSaleInfo::FS_NOT);
    rv[ST_TYPE_LABEL] = type;
    rv[ST_PRICE_LABEL] = sale.getSalePrice();
    return rv;
}

LLSaleInfo ll_sale_info_from_sd(const LLSD& sd)
{
    LLSaleInfo rv;
    rv.setSaleType(LLSaleInfo::lookup(sd[ST_TYPE_LABEL].asString().c_str()));
    rv.setSalePrice(llclamp((S32)sd[ST_PRICE_LABEL], 0, S32_MAX));
    return rv;
}