//
// Actions.cs
//
// Authors:
//   Gabriel Burt <gburt@novell.com>
//
// Copyright (C) 2009-2010 Novell, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

using System;
using System.Collections.Generic;
using System.Linq;

using Mono.Unix;
using Gtk;

using Hyena;
using Hyena.Widgets;

using Banshee.ServiceStack;
using Banshee.Collection.Database;
using Banshee.Gui;

namespace Banshee.Audiobook
{
    public class Actions : Banshee.Gui.BansheeActionGroup
    {
        private AudiobookLibrarySource library;

        public Actions (AudiobookLibrarySource library) : base ("Audiobook")
        {
            this.library = library;

            Add (
                new ActionEntry ("AudiobookBookPopup", null, null, null, null, (o, a) => ShowContextMenu ("/AudiobookBookPopup")),
                new ActionEntry ("AudiobookOpen", null, Catalog.GetString ("Open Book"), null, null, OnOpen),
                new ActionEntry ("AudiobookMerge", null, Catalog.GetString ("Merge Discs..."), null, null, OnMerge),
                new ActionEntry ("AudiobookSwitchToGrid", null, "", "Escape", null, (o, a) => library.SwitchToGridView ()),
                new ActionEntry ("AudiobookEdit", Stock.Edit,
                    Catalog.GetString ("_Edit Track Information"), "E", null, OnEdit),
                new ActionEntry ("AudiobookResumeSelected", Stock.MediaPlay,
                    Catalog.GetString ("Resume"), null, Catalog.GetString ("Resume playback of this audiobook"), OnResume)
            );

            Add (new ActionEntry ("AudiobookResume", Stock.MediaPlay,
                Catalog.GetString ("Resume"), null, Catalog.GetString ("Resume playback of this audiobook"), OnResume));

            AddUiFromFile ("GlobalUI.xml");

            Register ();

            UpdateActions ();
            library.BooksModel.Selection.Changed += (o, a) => UpdateActions ();
            library.BooksModel.Selection.FocusChanged += (o, a) => UpdateActions ();

            this["AudiobookSwitchToGrid"].Visible = false;
        }

        internal void UpdateActions ()
        {
            var selection = library.BooksModel.Selection;
            bool has_selection = selection.Count > 0;
            bool has_single_selection = selection.Count == 1;

            UpdateAction ("AudiobookMerge", !has_single_selection, true);
            UpdateAction ("AudiobookEdit", true, has_selection);

            bool can_resume = false;
            if (has_single_selection) {
                var book = library.ActiveBook;
                if (book != null && library.GetLastPlayedBookmark (book.DbId) != null) {
                    var playback_book = library.PlaybackSource.Book;
                    if (playback_book == null || book.DbId != playback_book.DbId) {
                        can_resume = true;
                    }
                }
            }
            UpdateAction ("AudiobookResume", library.CurrentViewBook != null, can_resume);
            UpdateAction ("AudiobookResumeSelected", can_resume, true);
        }

        private void OnResume (object to, EventArgs a)
        {
            var book = library.ActiveBook;
            var bookmark = library.GetLastPlayedBookmark (book.DbId);
            if (bookmark != null) {
                Log.DebugFormat ("Audiobook Library jumping to last-played position in active book: {0}", bookmark.Name);
                library.PlaybackSource.Book = book;
                ServiceManager.PlaybackController.Source = library;
                bookmark.JumpTo ();
            }
        }

        private void OnOpen (object o, EventArgs a)
        {
            var index = library.BooksModel.Selection.FocusedIndex;
            if (index > -1) {
                var book = library.BooksModel[index];
                library.SwitchToBookView (book as DatabaseAlbumInfo);
            }
        }

        private void OnEdit (object o, EventArgs a)
        {
            library.TrackModel.Selection.SelectAll ();
            Actions.TrackActions["TrackEditorAction"].Activate ();
        }

        private void OnMerge (object o, EventArgs a)
        {
            var discs = library.BooksModel.SelectedItems.OrderBy (d => d.Title).ToList ();
            var author = DatabaseArtistInfo.Provider.FetchSingle ((discs[0] as DatabaseAlbumInfo).ArtistId);

            var dialog = new HigMessageDialog (
                ServiceManager.Get<GtkElementsService> ().PrimaryWindow,
                DialogFlags.DestroyWithParent, MessageType.Question, ButtonsType.OkCancel,

                String.Format (Catalog.GetPluralString (
                    "Merge the {0} selected discs into one book?",
                    "Merge the {0} selected discs into one book?",
                    discs.Count), discs.Count),

                Catalog.GetString (
                    "This will ensure the disc numbers are all " + 
                    "set properly, and then set the author and book title for all tracks " +
                    "on all these discs to the values below")
            );

            var table = new SimpleTable<int> ();

            var author_entry = new Entry () { Text = discs[0].ArtistName };
            table.AddRow (0,
                new Label (Catalog.GetString ("Author:")) { Xalign = 0 },
                author_entry
            );

            var trimmings = new char [] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' ', '-' };
            var title_entry  = new Entry () { Text = discs[0].Title.Trim (trimmings) };
            table.AddRow (1,
                new Label (Catalog.GetString ("Book Title:")) { Xalign = 0 },
                title_entry
            );

            dialog.LabelVBox.PackStart (table, false, false, 0);

            dialog.ShowAll ();
            var response = dialog.Run ();
            string title = title_entry.Text;
            string author_name = author_entry.Text;
            dialog.Destroy ();

            if (response == (int)Gtk.ResponseType.Ok && !String.IsNullOrEmpty (title)) {
                if (author_name != author.Name) {
                    author = DatabaseArtistInfo.FindOrCreate (author_name, null);
                }
                var book = DatabaseAlbumInfo.FindOrCreate (author, title, null, false);

                int disc_num = 1;
                foreach (DatabaseAlbumInfo disc in discs) {
                    // Update the disc num/count field for all tracks on this 'book' (actually just one disc of a book)
                    ServiceManager.DbConnection.Execute (
                        @"UPDATE CoreTracks SET AlbumID = ?, Disc = ?, DiscCount = ?, DateUpdatedStamp = ?
                            WHERE PrimarySourceID = ? AND AlbumID = ?",
                        book.DbId, disc_num++, discs.Count, DateTime.Now,
                        library.DbId, disc.DbId
                    );
                }

                // Update the MetadataHash for all those tracks
                DatabaseTrackInfo.UpdateMetadataHash (
                    book.Title, author.Name,
                    String.Format ("PrimarySourceId = {0} AND AlbumID = {1}", library.DbId, book.DbId)
                );

                library.NotifyTracksChanged ();
            }
        }
    }
}
