/**
 * @file llwearablelist.cpp
 * @brief LLWearableList class implementation
 *
 * $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 "llviewerprecompiledheaders.h"

#include "llwearablelist.h"

#include "message.h"
#include "llassetstorage.h"
#include "llagent.h"
#include "llvoavatar.h"
#include "llviewerstats.h"
#include "llnotificationsutil.h"
#include "llinventorymodel.h"
#include "lltrans.h"
#include "llappviewer.h"

// Callback struct
struct LLWearableArrivedData
{
    LLWearableArrivedData(LLAssetType::EType asset_type,
        const std::string& wearable_name,
        LLAvatarAppearance* avatarp,
        void(*asset_arrived_callback)(LLViewerWearable*, void* userdata),
                          void* userdata) :
        mAssetType( asset_type ),
        mCallback( asset_arrived_callback ),
        mUserdata( userdata ),
        mName( wearable_name ),
        mRetries(0),
        mAvatarp(avatarp)
        {}

    LLAssetType::EType mAssetType;
    void    (*mCallback)(LLViewerWearable*, void* userdata);
    void*   mUserdata;
    std::string mName;
    S32 mRetries;
    LLAvatarAppearance *mAvatarp;
};

////////////////////////////////////////////////////////////////////////////
// LLWearableList

LLWearableList::~LLWearableList()
{
    cleanup();
}

void LLWearableList::cleanup()
{
    for_each(mList.begin(), mList.end(), DeletePairedPointer());
    mList.clear();
}

void LLWearableList::getAsset(const LLAssetID& assetID, const std::string& wearable_name, LLAvatarAppearance* avatarp, LLAssetType::EType asset_type, void(*asset_arrived_callback)(LLViewerWearable*, void* userdata), void* userdata)
{
    llassert( (asset_type == LLAssetType::AT_CLOTHING) || (asset_type == LLAssetType::AT_BODYPART) );
    LLViewerWearable* instance = get_if_there(mList, assetID, (LLViewerWearable*)NULL );
    if( instance )
    {
        LL_DEBUGS("Avatar") << "wearable " << assetID << " found in LLWearableList" << LL_ENDL;
        asset_arrived_callback( instance, userdata );
    }
    else
    {
        gAssetStorage->getAssetData(assetID,
            asset_type,
            LLWearableList::processGetAssetReply,
            (void*)new LLWearableArrivedData( asset_type, wearable_name, avatarp, asset_arrived_callback, userdata ),
            true);
    }
}

// static
void LLWearableList::processGetAssetReply( const char* filename, const LLAssetID& uuid, void* userdata, S32 status, LLExtStat ext_status )
{
    if (!LLCoros::on_main_coro())
    {
        // if triggered from a coroutine, dispatch to main thread before accessing app state
        std::string filename_in = filename;
        LLUUID uuid_in = uuid;

        LLAppViewer::instance()->postToMainCoro([=]()
            {
                processGetAssetReply(filename_in.c_str(), uuid_in, userdata, status, ext_status);
            });

        return;
    }

    LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR;

    bool isNewWearable = false;
    LLWearableArrivedData* data = (LLWearableArrivedData*) userdata;
    LLViewerWearable* wearable = NULL; // NULL indicates failure
    LLAvatarAppearance *avatarp = data->mAvatarp;

    if( !filename )
    {
        LL_WARNS("Wearable") << "Bad Wearable Asset: missing file." << LL_ENDL;
    }
    else if(!avatarp)
    {
        LL_WARNS("Wearable") << "Bad asset request: missing avatar pointer." << LL_ENDL;
    }
    else if (status >= 0)
    {
        // read the file
        llifstream ifs(filename, llifstream::binary);
        if( !ifs.is_open() )
        {
            LL_WARNS("Wearable") << "Bad Wearable Asset: unable to open file: '" << filename << "'" << LL_ENDL;
        }
        else
        {
            wearable = new LLViewerWearable(uuid);
            LLWearable::EImportResult result = wearable->importStream(
                                                ifs, avatarp );
            if (LLWearable::SUCCESS != result)
            {
                if (wearable->getType() == LLWearableType::WT_COUNT)
                {
                    isNewWearable = true;
                }
                delete wearable;
                wearable = NULL;
            }

            if(filename)
            {
                if (ifs.is_open())
                {
                    ifs.close();
                }
                LLFile::remove(std::string(filename));
            }
        }
    }
    else
    {
        if(filename)
        {
            LLFile::remove(std::string(filename));
        }

        LL_WARNS("Wearable") << "Wearable download failed: " << LLAssetStorage::getErrorString( status ) << " " << uuid << LL_ENDL;
        switch( status )
        {
          case LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE:
          {
              // Fail
              break;
        }
          default:
        {
              static const S32 MAX_RETRIES = 3;
              if (data->mRetries < MAX_RETRIES)
              {
              // Try again
                  data->mRetries++;
              gAssetStorage->getAssetData(uuid,
                                          data->mAssetType,
                                          LLWearableList::processGetAssetReply,
                                          userdata);  // re-use instead of deleting.
              return;
        }
              else
              {
                  // Fail
                  break;
              }
          }
    }
    }

    if (wearable) // success
    {
        LLWearableList::instance().mList[ uuid ] = wearable;
        LL_DEBUGS("Wearable") << "processGetAssetReply()" << LL_ENDL;
        LL_DEBUGS("Wearable") << wearable << LL_ENDL;
    }
    else
    {
        LLSD args;
        args["TYPE"] =LLTrans::getString(LLAssetType::lookupHumanReadable(data->mAssetType));
        if (isNewWearable)
        {
            LLNotificationsUtil::add("InvalidWearable");
        }
        else if (data->mName.empty())
        {
            LLNotificationsUtil::add("FailedToFindWearableUnnamed", args);
        }
        else
        {
            args["DESC"] = data->mName;
            LLNotificationsUtil::add("FailedToFindWearable", args);
        }
    }
    // Always call callback; wearable will be NULL if we failed
    {
        if( data->mCallback )
        {
            data->mCallback( wearable, data->mUserdata );
        }
    }
    delete data;
}


LLViewerWearable* LLWearableList::createCopy(const LLViewerWearable* old_wearable, const std::string& new_name)
{
    LL_DEBUGS() << "LLWearableList::createCopy()" << LL_ENDL;

    LLViewerWearable *wearable = generateNewWearable();
    wearable->copyDataFrom(old_wearable);

    LLPermissions perm(old_wearable->getPermissions());
    perm.setOwnerAndGroup(LLUUID::null, gAgent.getID(), LLUUID::null, true);
    wearable->setPermissions(perm);

    if (!new_name.empty()) wearable->setName(new_name);

    // Send to the dataserver
    wearable->saveNewAsset();

    return wearable;
}

LLViewerWearable* LLWearableList::createNewWearable( LLWearableType::EType type, LLAvatarAppearance *avatarp )
{
    LL_DEBUGS() << "LLWearableList::createNewWearable()" << LL_ENDL;

    LLViewerWearable *wearable = generateNewWearable();
    wearable->setType( type, avatarp );

    // LLWearableType has pre-translated getTypeLabel(), but it returns 'name', not 'New Name'.
    std::string name = LLTrans::getString( LLWearableType::getInstance()->getTypeDefaultNewName(wearable->getType()) );
    wearable->setName( name );

    LLPermissions perm;
    perm.init(gAgent.getID(), gAgent.getID(), LLUUID::null, LLUUID::null);
    perm.initMasks(PERM_ALL, PERM_ALL, PERM_NONE, PERM_NONE, PERM_MOVE | PERM_TRANSFER);
    wearable->setPermissions(perm);

    wearable->setDefinitionVersion(LLWearable::getCurrentDefinitionVersion());

    // Description and sale info have default values.
    wearable->setParamsToDefaults();
    wearable->setTexturesToDefaults();

    //mark all values (params & images) as saved
    wearable->saveValues();

    // Send to the dataserver
    wearable->saveNewAsset();


    return wearable;
}

LLViewerWearable *LLWearableList::generateNewWearable()
{
    LLTransactionID tid;
    tid.generate();
    LLAssetID new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID());

    LLViewerWearable* wearable = new LLViewerWearable(tid);
    mList[new_asset_id] = wearable;
    return wearable;
}