/**
 * @file llpanelmediasettingsgeneral.cpp
 * @brief LLPanelMediaSettingsGeneral class implementation
 *
 * $LicenseInfo:firstyear=2009&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 "llpanelmediasettingsgeneral.h"

// library includes
#include "llcombobox.h"
#include "llcheckboxctrl.h"
#include "llnotificationsutil.h"
#include "llspinctrl.h"
#include "lluictrlfactory.h"

// project includes
#include "llagent.h"
#include "llviewerwindow.h"
#include "llviewermedia.h"
#include "llvovolume.h"
#include "llsdutil.h"
#include "llselectmgr.h"
#include "llbutton.h"
#include "lltexturectrl.h"
#include "llurl.h"
#include "llwindow.h"
#include "llmediaentry.h"
#include "llmediactrl.h"
#include "llpanelcontents.h"
#include "llpermissions.h"
#include "llpluginclassmedia.h"
#include "llfloatermediasettings.h"
#include "llfloatertools.h"
#include "lltrans.h"
#include "lltextbox.h"
#include "llpanelmediasettingssecurity.h"

const char *CHECKERBOARD_DATA_URL = "data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%22100%%22 height=%22100%%22 %3E%3Cdefs%3E%3Cpattern id=%22checker%22 patternUnits=%22userSpaceOnUse%22 x=%220%22 y=%220%22 width=%22128%22 height=%22128%22 viewBox=%220 0 128 128%22 %3E%3Crect x=%220%22 y=%220%22 width=%2264%22 height=%2264%22 fill=%22#ddddff%22 /%3E%3Crect x=%2264%22 y=%2264%22 width=%2264%22 height=%2264%22 fill=%22#ddddff%22 /%3E%3C/pattern%3E%3C/defs%3E%3Crect x=%220%22 y=%220%22 width=%22100%%22 height=%22100%%22 fill=%22url(#checker)%22 /%3E%3C/svg%3E";

////////////////////////////////////////////////////////////////////////////////
//
LLPanelMediaSettingsGeneral::LLPanelMediaSettingsGeneral() :
    mAutoLoop( NULL ),
    mFirstClick( NULL ),
    mAutoZoom( NULL ),
    mAutoPlay( NULL ),
    mAutoScale( NULL ),
    mWidthPixels( NULL ),
    mHeightPixels( NULL ),
    mHomeURL( NULL ),
    mCurrentURL( NULL ),
    mParent( NULL ),
    mMediaEditable(false)
{
    // build dialog from XML
    buildFromFile( "panel_media_settings_general.xml");
}

////////////////////////////////////////////////////////////////////////////////
//
bool LLPanelMediaSettingsGeneral::postBuild()
{
    // connect member vars with UI widgets
    mAutoLoop = getChild< LLCheckBoxCtrl >( LLMediaEntry::AUTO_LOOP_KEY );
    mAutoPlay = getChild< LLCheckBoxCtrl >( LLMediaEntry::AUTO_PLAY_KEY );
    mAutoScale = getChild< LLCheckBoxCtrl >( LLMediaEntry::AUTO_SCALE_KEY );
    mAutoZoom = getChild< LLCheckBoxCtrl >( LLMediaEntry::AUTO_ZOOM_KEY );
    mCurrentURL = getChild< LLTextBox >( LLMediaEntry::CURRENT_URL_KEY );
    mFirstClick = getChild< LLCheckBoxCtrl >( LLMediaEntry::FIRST_CLICK_INTERACT_KEY );
    mHeightPixels = getChild< LLSpinCtrl >( LLMediaEntry::HEIGHT_PIXELS_KEY );
    mHomeURL = getChild< LLLineEditor >( LLMediaEntry::HOME_URL_KEY );
    mWidthPixels = getChild< LLSpinCtrl >( LLMediaEntry::WIDTH_PIXELS_KEY );
    mPreviewMedia = getChild<LLMediaCtrl>("preview_media");
    mFailWhiteListText = getChild<LLTextBox>( "home_fails_whitelist_label" );

    // watch commit action for HOME URL
    childSetCommitCallback( LLMediaEntry::HOME_URL_KEY, onCommitHomeURL, this);
    childSetCommitCallback( "current_url_reset_btn",onBtnResetCurrentUrl, this);

    return true;
}

////////////////////////////////////////////////////////////////////////////////
// virtual
LLPanelMediaSettingsGeneral::~LLPanelMediaSettingsGeneral()
{
}

////////////////////////////////////////////////////////////////////////////////
// static
void LLPanelMediaSettingsGeneral::draw()
{
    // housekeeping
    LLPanel::draw();

    // TODO: we need to call this repeatedly until the floater panels are fully
    // created but once we have a valid answer, we should stop looking here - the
    // commit callback will handle it
    checkHomeUrlPassesWhitelist();

    // enable/disable pixel values image entry based on auto scale checkbox
    if (!mAutoScale->getValue().asBoolean())
    {
        getChildView( LLMediaEntry::WIDTH_PIXELS_KEY )->setEnabled( true );
        getChildView( LLMediaEntry::HEIGHT_PIXELS_KEY )->setEnabled( true );
    }
    else
    {
        getChildView( LLMediaEntry::WIDTH_PIXELS_KEY )->setEnabled( false );
        getChildView( LLMediaEntry::HEIGHT_PIXELS_KEY )->setEnabled( false );
    };

    // enable/disable UI based on type of media
    bool reset_button_is_active = true;
    if (mPreviewMedia)
    {
        if (LLPluginClassMedia* media_plugin = mPreviewMedia->getMediaPlugin())
        {
            // turn off volume (if we can) for preview. Note: this really only
            // works for QuickTime movies right now - no way to control the
            // volume of a flash app embedded in a page for example
            media_plugin->setVolume( 0 );

            // some controls are only appropriate for time or browser type plugins
            // so we selectively enable/disable them - need to do it in draw
            // because the information from plugins arrives assynchronously
            if (media_plugin->pluginSupportsMediaTime())
            {
                getChildView( LLMediaEntry::CURRENT_URL_KEY )->setEnabled( false );
                reset_button_is_active = false;
                getChildView("current_url_label")->setEnabled(false );
                getChildView( LLMediaEntry::AUTO_LOOP_KEY )->setEnabled( true );
            }
            else
            {
                getChildView( LLMediaEntry::CURRENT_URL_KEY )->setEnabled( true );
                reset_button_is_active = true;
                getChildView("current_url_label")->setEnabled(true );
                getChildView( LLMediaEntry::AUTO_LOOP_KEY )->setEnabled( false );
            };
        };
    };

    // current URL can change over time, update it here
    updateCurrentUrl();

    LLPermissions perm;
    bool user_can_press_reset = mMediaEditable;

    // several places modify this widget so we must collect states in one place
    if ( reset_button_is_active )
    {
        // user has perms to press reset button and it is active
        if ( user_can_press_reset )
        {
            getChildView("current_url_reset_btn")->setEnabled(true );
        }
        // user does not has perms to press reset button and it is active
        else
        {
            getChildView("current_url_reset_btn")->setEnabled(false );
        };
    }
    else
    // reset button is inactive so we just slam it to off - other states don't matter
    {
        getChildView("current_url_reset_btn")->setEnabled(false );
    };
}

////////////////////////////////////////////////////////////////////////////////
// static
void LLPanelMediaSettingsGeneral::clearValues( void* userdata, bool editable, bool update_preview)
{
    LLPanelMediaSettingsGeneral *self =(LLPanelMediaSettingsGeneral *)userdata;
    self->mAutoLoop->clear();
    self->mAutoPlay->clear();
    self->mAutoScale->clear();
    self->mAutoZoom ->clear();
    self->mCurrentURL->clear();
    self->mFirstClick->clear();
    self->mHeightPixels->clear();
    self->mHomeURL->clear();
    self->mWidthPixels->clear();
    self->mAutoLoop ->setEnabled(editable);
    self->mAutoPlay ->setEnabled(editable);
    self->mAutoScale ->setEnabled(editable);
    self->mAutoZoom  ->setEnabled(editable);
    self->mCurrentURL ->setEnabled(editable);
    self->mFirstClick ->setEnabled(editable);
    self->mHeightPixels ->setEnabled(editable);
    self->mHomeURL ->setEnabled(editable);
    self->mWidthPixels ->setEnabled(editable);
    if (update_preview)
    {
        self->updateMediaPreview();
    }
}

// static
bool LLPanelMediaSettingsGeneral::isMultiple()
{
    // IF all the faces have media (or all dont have media)
    if ( LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo )
    {
        if(LLFloaterMediaSettings::getInstance()->mMultipleMedia)
        {
            return true;
        }

    }
    else
    {
        if(LLFloaterMediaSettings::getInstance()->mMultipleValidMedia)
        {
            return true;
        }
    }
    return false;
}

////////////////////////////////////////////////////////////////////////////////
// static
void LLPanelMediaSettingsGeneral::initValues( void* userdata, const LLSD& _media_settings, bool editable)
{
    LLPanelMediaSettingsGeneral *self =(LLPanelMediaSettingsGeneral *)userdata;
    self->mMediaEditable = editable;

    LLSD media_settings = _media_settings;

    if ( LLPanelMediaSettingsGeneral::isMultiple() )
    {
        // *HACK:  "edit" the incoming media_settings
        media_settings[LLMediaEntry::CURRENT_URL_KEY] = LLTrans::getString("Multiple Media");
        media_settings[LLMediaEntry::HOME_URL_KEY] = LLTrans::getString("Multiple Media");
    }

    std::string base_key( "" );
    std::string tentative_key( "" );

    struct
    {
        std::string key_name;
        LLUICtrl* ctrl_ptr;
        std::string ctrl_type;

    } data_set [] =
    {
        { LLMediaEntry::AUTO_LOOP_KEY,              self->mAutoLoop,        "LLCheckBoxCtrl" },
        { LLMediaEntry::AUTO_PLAY_KEY,              self->mAutoPlay,        "LLCheckBoxCtrl" },
        { LLMediaEntry::AUTO_SCALE_KEY,             self->mAutoScale,       "LLCheckBoxCtrl" },
        { LLMediaEntry::AUTO_ZOOM_KEY,              self->mAutoZoom,        "LLCheckBoxCtrl" },
        { LLMediaEntry::CURRENT_URL_KEY,            self->mCurrentURL,      "LLTextBox" },
        { LLMediaEntry::HEIGHT_PIXELS_KEY,          self->mHeightPixels,    "LLSpinCtrl" },
        { LLMediaEntry::HOME_URL_KEY,               self->mHomeURL,         "LLLineEditor" },
        { LLMediaEntry::FIRST_CLICK_INTERACT_KEY,   self->mFirstClick,      "LLCheckBoxCtrl" },
        { LLMediaEntry::WIDTH_PIXELS_KEY,           self->mWidthPixels,     "LLSpinCtrl" },
        { "", NULL , "" }
    };

    for( int i = 0; data_set[ i ].key_name.length() > 0; ++i )
    {
        base_key = std::string( data_set[ i ].key_name );
        tentative_key = base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX );
        // TODO: CP - I bet there is a better way to do this using Boost
        if ( media_settings[ base_key ].isDefined() )
        {
            if ( data_set[ i ].ctrl_type == "LLLineEditor" )
            {
                static_cast< LLLineEditor* >( data_set[ i ].ctrl_ptr )->
                    setText( media_settings[ base_key ].asString() );
            }
            else
            if ( data_set[ i ].ctrl_type == "LLCheckBoxCtrl" )
                static_cast< LLCheckBoxCtrl* >( data_set[ i ].ctrl_ptr )->
                    setValue( media_settings[ base_key ].asBoolean() );
            else
            if ( data_set[ i ].ctrl_type == "LLComboBox" )
                static_cast< LLComboBox* >( data_set[ i ].ctrl_ptr )->
                    setCurrentByIndex( media_settings[ base_key ].asInteger() );
            else
            if ( data_set[ i ].ctrl_type == "LLSpinCtrl" )
                static_cast< LLSpinCtrl* >( data_set[ i ].ctrl_ptr )->
                    setValue( media_settings[ base_key ].asInteger() );

            data_set[ i ].ctrl_ptr->setEnabled(self->mMediaEditable);
            data_set[ i ].ctrl_ptr->setTentative( media_settings[ tentative_key ].asBoolean() );
        };
    };
}

////////////////////////////////////////////////////////////////////////////////
// Helper to set media control to media URL as required
void LLPanelMediaSettingsGeneral::updateMediaPreview()
{
    if(LLTrans::getString("Multiple Media") == mHomeURL->getValue().asString())
    {
        return;
    }
    if ( mHomeURL->getValue().asString().length() > 0 )
    {
        if(mPreviewMedia->getCurrentNavUrl() != mHomeURL->getValue().asString())
        {
            mPreviewMedia->navigateTo( mHomeURL->getValue().asString() );
        }
    }
    else
    // new home URL will be empty if media is deleted so display a
    // "preview goes here" data url page
    {
        if(mPreviewMedia->getCurrentNavUrl() != CHECKERBOARD_DATA_URL)
        {
            mPreviewMedia->navigateTo( CHECKERBOARD_DATA_URL );
        }
    };
}

////////////////////////////////////////////////////////////////////////////////

// virtual
void LLPanelMediaSettingsGeneral::onClose(bool app_quitting)
{
    if(mPreviewMedia)
    {
        mPreviewMedia->unloadMediaSource();
    }
}

////////////////////////////////////////////////////////////////////////////////
//
void LLPanelMediaSettingsGeneral::checkHomeUrlPassesWhitelist()
{
    // parent floater has not constructed the security panel yet
    if ( mParent->getPanelSecurity() == 0 )
        return;

    std::string home_url = getHomeUrl();
    if ( home_url.empty() || mParent->getPanelSecurity()->urlPassesWhiteList( home_url ) )
    {
        // Home URL is empty or passes the white list so hide the warning message
        mFailWhiteListText->setVisible( false );
    }
    else
    {
        // Home URL does not pass the white list so show the warning message
        mFailWhiteListText->setVisible( true );
    };
}

////////////////////////////////////////////////////////////////////////////////
// static
void LLPanelMediaSettingsGeneral::onCommitHomeURL( LLUICtrl* ctrl, void *userdata )
{
    LLPanelMediaSettingsGeneral* self =(LLPanelMediaSettingsGeneral *)userdata;

    // check home url passes whitelist and display warning if not
    self->checkHomeUrlPassesWhitelist();

    self->updateMediaPreview();
}

////////////////////////////////////////////////////////////////////////////////
// static
void LLPanelMediaSettingsGeneral::onBtnResetCurrentUrl(LLUICtrl* ctrl, void *userdata)
{
    LLPanelMediaSettingsGeneral* self =(LLPanelMediaSettingsGeneral *)userdata;
    self->navigateHomeSelectedFace(false);
}

////////////////////////////////////////////////////////////////////////////////
//
void LLPanelMediaSettingsGeneral::preApply()
{
    // Make sure the home URL entry is committed
    mHomeURL->onCommit();
}

////////////////////////////////////////////////////////////////////////////////
//
void LLPanelMediaSettingsGeneral::getValues( LLSD &fill_me_in, bool include_tentative )
{
    if (include_tentative || !mAutoLoop->getTentative()) fill_me_in[LLMediaEntry::AUTO_LOOP_KEY] = (LLSD::Boolean)mAutoLoop->getValue();
    if (include_tentative || !mAutoPlay->getTentative()) fill_me_in[LLMediaEntry::AUTO_PLAY_KEY] = (LLSD::Boolean)mAutoPlay->getValue();
    if (include_tentative || !mAutoScale->getTentative()) fill_me_in[LLMediaEntry::AUTO_SCALE_KEY] = (LLSD::Boolean)mAutoScale->getValue();
    if (include_tentative || !mAutoZoom->getTentative()) fill_me_in[LLMediaEntry::AUTO_ZOOM_KEY] = (LLSD::Boolean)mAutoZoom->getValue();
    //Don't fill in current URL: this is only supposed to get changed via navigate
    // if (include_tentative || !mCurrentURL->getTentative()) fill_me_in[LLMediaEntry::CURRENT_URL_KEY] = mCurrentURL->getValue();
    if (include_tentative || !mHeightPixels->getTentative()) fill_me_in[LLMediaEntry::HEIGHT_PIXELS_KEY] = (LLSD::Integer)mHeightPixels->getValue();
    // Don't fill in the home URL if it is the special "Multiple Media" string!
    if ((include_tentative || !mHomeURL->getTentative())
        && LLTrans::getString("Multiple Media") != mHomeURL->getValue())
            fill_me_in[LLMediaEntry::HOME_URL_KEY] = (LLSD::String)mHomeURL->getValue();
    if (include_tentative || !mFirstClick->getTentative()) fill_me_in[LLMediaEntry::FIRST_CLICK_INTERACT_KEY] = (LLSD::Boolean)mFirstClick->getValue();
    if (include_tentative || !mWidthPixels->getTentative()) fill_me_in[LLMediaEntry::WIDTH_PIXELS_KEY] = (LLSD::Integer)mWidthPixels->getValue();
}

////////////////////////////////////////////////////////////////////////////////
//
void LLPanelMediaSettingsGeneral::postApply()
{
    // Make sure to navigate to the home URL if the current URL is empty and
    // autoplay is on
    navigateHomeSelectedFace(true);
}


////////////////////////////////////////////////////////////////////////////////
//
void LLPanelMediaSettingsGeneral::setParent( LLFloaterMediaSettings* parent )
{
    mParent = parent;
};

////////////////////////////////////////////////////////////////////////////////
//
bool LLPanelMediaSettingsGeneral::navigateHomeSelectedFace(bool only_if_current_is_empty)
{
    struct functor_navigate_media : public LLSelectedTEGetFunctor< bool>
    {
        functor_navigate_media(bool flag) : only_if_current_is_empty(flag) {}
        bool get( LLViewerObject* object, S32 face )
        {
            if ( object && object->getTE(face) && object->permModify() )
            {
                const LLMediaEntry *media_data = object->getTE(face)->getMediaData();
                if ( media_data )
                {
                    if (!only_if_current_is_empty || (media_data->getCurrentURL().empty() && media_data->getAutoPlay()))
                    {
                        viewer_media_t media_impl =
                            LLViewerMedia::getInstance()->getMediaImplFromTextureID(object->getTE(face)->getMediaData()->getMediaID());
                        if (media_impl)
                        {
                            media_impl->setPriority(LLPluginClassMedia::PRIORITY_NORMAL);
                            media_impl->navigateHome();

                            if (!only_if_current_is_empty)
                            {
                                LLSD media_data;
                                media_data[LLMediaEntry::CURRENT_URL_KEY] = std::string();
                                object->getTE(face)->mergeIntoMediaData(media_data);
                            }
                            return true;
                        }
                    }
                }
            }
            return false;
        };
        bool only_if_current_is_empty;

    } functor_navigate_media(only_if_current_is_empty);

    bool all_face_media_navigated = false;
    LLObjectSelectionHandle selected_objects =LLSelectMgr::getInstance()->getSelection();
    selected_objects->getSelectedTEValue( &functor_navigate_media, all_face_media_navigated );

    if (all_face_media_navigated)
    {
        struct functor_sync_to_server : public LLSelectedObjectFunctor
        {
            virtual bool apply(LLViewerObject* object)
            {
                LLVOVolume *volume = dynamic_cast<LLVOVolume*>(object);
                if (volume)
                {
                    volume->sendMediaDataUpdate();
                }
                return true;
            }
        } sendfunc;
        selected_objects->applyToObjects(&sendfunc);
    }

    // Note: we don't update the 'current URL' field until the media data itself changes

    return all_face_media_navigated;
}

////////////////////////////////////////////////////////////////////////////////
//
const std::string LLPanelMediaSettingsGeneral::getHomeUrl()
{
    return mHomeURL->getValue().asString();
}

////////////////////////////////////////////////////////////////////////////////
//
void LLPanelMediaSettingsGeneral::updateCurrentUrl()
{
    // Get the current URL from the selection
    const LLMediaEntry default_media_data;
    std::string value_str = default_media_data.getCurrentURL();
    struct functor_getter_current_url : public LLSelectedTEGetFunctor< std::string >
    {
        functor_getter_current_url(const LLMediaEntry& entry): mMediaEntry(entry) {}

        std::string get( LLViewerObject* object, S32 face )
        {
            if ( object )
                if ( object->getTE(face) )
                    if ( object->getTE(face)->getMediaData() )
                        return object->getTE(face)->getMediaData()->getCurrentURL();
            return mMediaEntry.getCurrentURL();
        };

        const LLMediaEntry &  mMediaEntry;

    } func_current_url(default_media_data);
    bool identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func_current_url, value_str );
    mCurrentURL->setText(value_str);
    mCurrentURL->setTentative(identical);

    if ( LLPanelMediaSettingsGeneral::isMultiple() )
    {
        mCurrentURL->setText(LLTrans::getString("Multiple Media"));
    }
}