/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include <mediacontrol.hxx>
#include <strings.hrc>
#include <mediamisc.hxx>
#include <avmedia/mediawindow.hxx>
#include <avmedia/mediaplayer.hxx>
#include <helpids.h>
#include <tools/time.hxx>
#include <svtools/miscopt.hxx>
#include <vcl/svapp.hxx>
#include <vcl/settings.hxx>
#include <vcl/lstbox.hxx>
#include <vcl/weld.hxx>
#include <unotools/syslocale.hxx>
#include <sfx2/viewfrm.hxx>
#include <math.h>
#include <algorithm>
#include <avmedia/MediaControlBase.hxx>

namespace avmedia
{

MediaControl::MediaControl( vcl::Window* pParent, MediaControlStyle eControlStyle ) :
    Control( pParent ),
    MediaControlBase(),
    maIdle( "avmedia MediaControl Idle" ),
    maItem( 0, AVMediaSetMask::ALL ),
    mbLocked( false ),
    meControlStyle( eControlStyle ),
    mfTime(0.0)
{
    mpPlayToolBox =  VclPtr<ToolBox>::Create(this, WB_3DLOOK) ;
    mpTimeSlider = VclPtr<Slider>::Create(this, WB_HORZ | WB_DRAG | WB_3DLOOK) ;
    mpTimeSlider->SetScrollTypeSet(true);
    mpMuteToolBox = VclPtr<ToolBox>::Create(this, WB_3DLOOK) ;
    mpVolumeSlider = VclPtr<Slider>::Create(this, WB_HORZ | WB_DRAG) ;
    mpVolumeSlider->SetScrollTypeSet(true);
    mpZoomToolBox = VclPtr<ToolBox>::Create(this, WB_3DLOOK) ;
    mpZoomListBox = VclPtr<ListBox>::Create( mpZoomToolBox.get(), WB_BORDER | WB_DROPDOWN | WB_AUTOHSCROLL | WB_3DLOOK ) ;
    mpTimeEdit = VclPtr<Edit>::Create(this, WB_CENTER | WB_READONLY | WB_BORDER | WB_3DLOOK ) ;
    mpMediaPath = VclPtr<FixedText>::Create(this, WB_VCENTER | WB_READONLY | WB_BORDER | WB_3DLOOK ) ;

    SetBackground();
    SetPaintTransparent( true );
    SetParentClipMode( ParentClipMode::NoClip );

    InitializeWidgets();

    mpPlayToolBox->SetSelectHdl( LINK( this, MediaControl, implSelectHdl ) );
    mpPlayToolBox->SetSizePixel( mpPlayToolBox->CalcWindowSizePixel() );
    mpPlayToolBox->Show();
    maMinSize = mpPlayToolBox->GetSizePixel();

    mpTimeSlider->SetSlideHdl( LINK( this, MediaControl, implTimeHdl ) );
    mpTimeSlider->SetEndSlideHdl( LINK( this, MediaControl, implTimeEndHdl ) );
    mpTimeSlider->SetSizePixel( Size( 128, mpPlayToolBox->GetSizePixel().Height() ) );
    mpTimeSlider->Show();
    maMinSize.AdjustWidth(mpTimeSlider->GetSizePixel().Width() );

    const OUString aTimeText( " 00:00:00/00:00:00 " );
    mpTimeEdit->SetSizePixel( Size( mpTimeEdit->GetTextWidth( aTimeText ) + 8, mpPlayToolBox->GetSizePixel().Height() ) );
    mpTimeEdit->SetControlBackground( Application::GetSettings().GetStyleSettings().GetWindowColor() );
    maMinSize.AdjustWidth(mpTimeEdit->GetSizePixel().Width() );

    mpMuteToolBox->SetSelectHdl( LINK( this, MediaControl, implSelectHdl ) );
    mpMuteToolBox->SetSizePixel( mpMuteToolBox->CalcWindowSizePixel() );
    mpMuteToolBox->Show();
    maMinSize.AdjustWidth(mpMuteToolBox->GetSizePixel().Width() );

    mpVolumeSlider->SetSlideHdl( LINK( this, MediaControl, implVolumeHdl ) );
    mpVolumeSlider->SetSizePixel( Size( 48, mpPlayToolBox->GetSizePixel().Height() ) );
    mpVolumeSlider->Show();
    maMinSize.AdjustWidth(mpVolumeSlider->GetSizePixel().Width() );

    mpZoomListBox->SetSizePixel( Size( mpTimeEdit->GetSizePixel().Width(), 260 ) );
    mpZoomListBox->SetSelectHdl( LINK( this, MediaControl, implZoomSelectHdl ) );

    mpZoomToolBox->InsertItem( AVMEDIA_TOOLBOXITEM_ZOOM, AvmResId( AVMEDIA_STR_ZOOM ) );
    mpZoomToolBox->SetHelpId( AVMEDIA_TOOLBOXITEM_ZOOM, HID_AVMEDIA_ZOOMLISTBOX );

    mpZoomToolBox->SetItemWindow( AVMEDIA_TOOLBOXITEM_ZOOM, mpZoomListBox );
    mpZoomToolBox->SetSelectHdl( LINK( this, MediaControl, implSelectHdl ) );
    mpZoomToolBox->SetSizePixel( mpZoomToolBox->CalcWindowSizePixel() );
    mpZoomToolBox->Show();
    maMinSize.AdjustWidth(mpZoomToolBox->GetSizePixel().Width() );

    const OUString aMediaPath( AvmResId( AVMEDIA_MEDIA_PATH_DEFAULT ) );
    mpMediaPath->SetText(aMediaPath);
    mpMediaPath->SetUpdateMode( false );
    mpMediaPath->SetSizePixel( Size( mpMediaPath->GetTextWidth( aMediaPath ) + 400, mpPlayToolBox->GetSizePixel().Height() ) ); // maybe extend the no. 400 to span the screen width
    mpMediaPath->SetControlBackground( Application::GetSettings().GetStyleSettings().GetWindowColor() );
    mpMediaPath->Show();
    maMinSize.AdjustWidth(mpMediaPath->GetSizePixel().Width() );

    if( meControlStyle == MEDIACONTROLSTYLE_MULTILINE )
    {
        maMinSize.setWidth( 256 );
        maMinSize.setHeight( ( maMinSize.Height() << 1 ) + AVMEDIA_CONTROLOFFSET );
        mpZoomToolBox->SetBackground();
        mpZoomToolBox->SetPaintTransparent( true );
    }
    // we want time field + progress slider to update as the media plays
    // give this task a lower prio than REPAINT so that UI updates are not starved
    maIdle.SetPriority( TaskPriority::POST_PAINT );
    maIdle.SetInvokeHandler( LINK( this, MediaControl, implTimeoutHdl ) );
}

void MediaControl::InitializeWidgets()
{
    if( meControlStyle != MEDIACONTROLSTYLE_SINGLELINE )
    {
        mpPlayToolBox->InsertItem( AVMEDIA_TOOLBOXITEM_OPEN, GetImage(AVMEDIA_TOOLBOXITEM_OPEN), AvmResId( AVMEDIA_STR_OPEN ) );
        mpPlayToolBox->SetHelpId( AVMEDIA_TOOLBOXITEM_OPEN, HID_AVMEDIA_TOOLBOXITEM_OPEN );
        mpPlayToolBox->InsertItem( AVMEDIA_TOOLBOXITEM_INSERT, GetImage(AVMEDIA_TOOLBOXITEM_INSERT), AvmResId( AVMEDIA_STR_INSERT ) );
        mpPlayToolBox->SetHelpId( AVMEDIA_TOOLBOXITEM_INSERT, HID_AVMEDIA_TOOLBOXITEM_INSERT );
        mpPlayToolBox->InsertSeparator();
    }
    else
    {
        mpZoomListBox->SetBackground();
        mpPlayToolBox->SetBackground();
        mpPlayToolBox->SetPaintTransparent( true );
        mpMuteToolBox->SetBackground();
        mpMuteToolBox->SetPaintTransparent( true );
        mpMuteToolBox->InsertSeparator();
    }
    avmedia::MediaControlBase::InitializeWidgets();

    if( meControlStyle == MEDIACONTROLSTYLE_SINGLELINE )
        mpPlayToolBox->InsertSeparator();
}

MediaControl::~MediaControl()
{
    disposeOnce();
}

void MediaControl::dispose()
{
    mpZoomToolBox->SetItemWindow( AVMEDIA_TOOLBOXITEM_ZOOM, nullptr );
    mpZoomListBox.disposeAndClear();
    mpTimeEdit.disposeAndClear();
    mpMediaPath.disposeAndClear();
    mpZoomToolBox.disposeAndClear();
    mpVolumeSlider.disposeAndClear();
    mpMuteToolBox.disposeAndClear();
    mpTimeSlider.disposeAndClear();
    mpPlayToolBox.disposeAndClear();
    Control::dispose();
}

const Size& MediaControl::getMinSizePixel() const
{
    return maMinSize;
}

void MediaControl::UpdateURLField(MediaItem const & tempItem)
{
    const OUString aURL( AvmResId(AVMEDIA_MEDIA_PATH) + ":  " + tempItem.getURL() ) ;
    mpMediaPath->SetText(aURL);
    mpMediaPath->SetUpdateMode( false );
    mpMediaPath->SetSizePixel( Size( mpMediaPath->GetTextWidth( aURL ) + 8, mpPlayToolBox->GetSizePixel().Height() ) );
    mpMediaPath->SetControlBackground( Application::GetSettings().GetStyleSettings().GetWindowColor() );
    mpMediaPath->Show();
    maMinSize.AdjustWidth(mpMediaPath->GetSizePixel().Width() );
}

void MediaControl::Resize()
{
    Point           aPos( 0, 0 );
    const sal_Int32 nPlayToolBoxWidth = mpPlayToolBox->GetSizePixel().Width();
    const sal_Int32 nMuteToolBoxWidth = mpMuteToolBox->GetSizePixel().Width();
    const sal_Int32 nVolumeSliderWidth = mpVolumeSlider->GetSizePixel().Width();
    const sal_Int32 nZoomToolBoxWidth = mpZoomToolBox->GetSizePixel().Width();
    const sal_Int32 nTimeEditWidth = mpTimeEdit->GetSizePixel().Width();
    const sal_Int32 nMediaPathWidth = mpMediaPath->GetSizePixel().Width();
    const sal_Int32 nTimeSliderHeight = mpTimeSlider->GetSizePixel().Height();

    if( meControlStyle == MEDIACONTROLSTYLE_SINGLELINE )
    {
        const sal_Int32 nTimeSliderWidth = GetSizePixel().Width() - ( AVMEDIA_CONTROLOFFSET * 4 ) -
                                           nPlayToolBoxWidth - nMuteToolBoxWidth - nVolumeSliderWidth - nTimeEditWidth - nZoomToolBoxWidth - nMediaPathWidth;

        mpPlayToolBox->SetPosSizePixel( aPos, mpPlayToolBox->GetSizePixel() );

        aPos.AdjustX(nPlayToolBoxWidth );
        mpTimeSlider->SetPosSizePixel( aPos, Size( nTimeSliderWidth, nTimeSliderHeight ) );

        aPos.AdjustX(nTimeSliderWidth + AVMEDIA_CONTROLOFFSET );
        mpTimeEdit->SetPosSizePixel( aPos, mpTimeEdit->GetSizePixel() );

        aPos.AdjustX(nTimeEditWidth + AVMEDIA_CONTROLOFFSET );
        mpMuteToolBox->SetPosSizePixel( aPos, mpMuteToolBox->GetSizePixel() );

        aPos.AdjustX(nMuteToolBoxWidth );
        mpVolumeSlider->SetPosSizePixel( aPos, mpVolumeSlider->GetSizePixel() );

        aPos.AdjustX(nVolumeSliderWidth + AVMEDIA_CONTROLOFFSET );
        mpZoomToolBox->SetPosSizePixel( aPos, mpZoomToolBox->GetSizePixel() );

        aPos.AdjustX(nZoomToolBoxWidth + AVMEDIA_CONTROLOFFSET );
        mpMediaPath->SetPosSizePixel( aPos, mpMediaPath->GetSizePixel() );
    }
    else
    {
        const sal_Int32 nTimeSliderWidth = GetSizePixel().Width() - AVMEDIA_CONTROLOFFSET - nTimeEditWidth;

        mpTimeSlider->SetPosSizePixel( aPos, Size( nTimeSliderWidth, nTimeSliderHeight ) );

        aPos.AdjustX(nTimeSliderWidth + AVMEDIA_CONTROLOFFSET );
        mpTimeEdit->SetPosSizePixel( aPos, mpTimeEdit->GetSizePixel() );

        aPos.setX( 0 );
        aPos.AdjustY(nTimeSliderHeight + AVMEDIA_CONTROLOFFSET );
        mpPlayToolBox->SetPosSizePixel( aPos, mpPlayToolBox->GetSizePixel() );

        aPos.setX( GetSizePixel().Width() - nVolumeSliderWidth - nMuteToolBoxWidth - nZoomToolBoxWidth - AVMEDIA_CONTROLOFFSET );
        mpMuteToolBox->SetPosSizePixel( aPos, mpMuteToolBox->GetSizePixel() );

        aPos.AdjustX(nMuteToolBoxWidth );
        mpVolumeSlider->SetPosSizePixel( aPos, mpVolumeSlider->GetSizePixel() );

        aPos.setX( GetSizePixel().Width() - nZoomToolBoxWidth );
        mpZoomToolBox->SetPosSizePixel( aPos, mpZoomToolBox->GetSizePixel() );
    }
}


void MediaControl::setState( const MediaItem& rItem )
{
    double fTime = rItem.getTime();
    if( !mbLocked && fTime != mfTime)
    {
        mfTime = fTime;
        maItem.merge( rItem );
        if( rItem.getURL().isEmpty() && meControlStyle == MEDIACONTROLSTYLE_SINGLELINE )
            mpPlayToolBox->Disable();
        UpdateToolBoxes( maItem );
        UpdateTimeSlider( maItem );
        UpdateVolumeSlider( maItem );
        UpdateTimeField( maItem, maItem.getTime() );
        UpdateURLField(maItem);
    }
}

IMPL_LINK( MediaControl, implTimeHdl, Slider*, p, void )
{
    mbLocked = true;
    maIdle.Stop();
    UpdateTimeField( maItem, p->GetThumbPos() * maItem.getDuration() / AVMEDIA_TIME_RANGE );
}


IMPL_LINK( MediaControl, implTimeEndHdl, Slider*, p, void )
{
    MediaItem aExecItem;

    aExecItem.setTime( p->GetThumbPos() * maItem.getDuration() / AVMEDIA_TIME_RANGE );
    execute( aExecItem );
    update();
    maIdle.Start();
    mbLocked = false;
}


IMPL_LINK( MediaControl, implVolumeHdl, Slider*, p, void )
{
    MediaItem aExecItem;

    aExecItem.setVolumeDB( static_cast< sal_Int16 >( p->GetThumbPos() ) );
    execute( aExecItem );
    update();
}


IMPL_LINK( MediaControl, implSelectHdl, ToolBox*, p, void )
{
    if( p )
    {
        MediaItem aExecItem;
        if( p->GetCurItemId() == AVMEDIA_TOOLBOXITEM_OPEN )
        {
            OUString aURL;
            if (MediaWindow::executeMediaURLDialog(GetFrameWeld(), aURL, nullptr))
            {
                if( !MediaWindow::isMediaURL( aURL, ""/*TODO?*/, true ) )
                    MediaWindow::executeFormatErrorBox(GetFrameWeld());
                else
                {
                    aExecItem.setURL( aURL, "", ""/*TODO?*/ );
                    aExecItem.setState( MediaState::Play );
                }
            }
        }
        else
            SelectPlayToolBoxItem( aExecItem, maItem, p->GetCurItemId() );

        if (aExecItem.getState() == MediaState::Play)
            maIdle.Start();
        else if (aExecItem.getState() == MediaState::Pause ||
                 aExecItem.getState() == MediaState::Stop)
            maIdle.Stop();

        if( aExecItem.getMaskSet() != AVMediaSetMask::NONE )
            execute( aExecItem );
    }

    update();
    if(p)
    {
        p->Invalidate( InvalidateFlags::Update );
    }
}


IMPL_LINK( MediaControl, implZoomSelectHdl, ListBox&, p, void )
{
    MediaItem aExecItem;
    css::media::ZoomLevel eLevel;

    switch( p.GetSelectedEntryPos() )
    {
        case AVMEDIA_ZOOMLEVEL_50: eLevel = css::media::ZoomLevel_ZOOM_1_TO_2; break;
        case AVMEDIA_ZOOMLEVEL_100: eLevel = css::media::ZoomLevel_ORIGINAL; break;
        case AVMEDIA_ZOOMLEVEL_200: eLevel = css::media::ZoomLevel_ZOOM_2_TO_1; break;
        case AVMEDIA_ZOOMLEVEL_FIT: eLevel = css::media::ZoomLevel_FIT_TO_WINDOW_FIXED_ASPECT; break;
        case AVMEDIA_ZOOMLEVEL_SCALED: eLevel = css::media::ZoomLevel_FIT_TO_WINDOW; break;

        default: eLevel = css::media::ZoomLevel_NOT_AVAILABLE; break;
    }

    aExecItem.setZoom( eLevel );
    execute( aExecItem );
    update();
}


IMPL_LINK_NOARG(MediaControl, implTimeoutHdl, Timer *, void)
{
    update();
    maIdle.Start();
}

} // namespace avmedia

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
