1// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
2/*-
3 * Copyright (c) 2016-2016 elementary LLC.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * Authored by: Artem Anufrij <artem.anufrij@live.de>
19 *
20 */
21
22namespace Audience {
23    public class EpisodesPage : Gtk.Grid {
24        public Gtk.Image poster { get; set; }
25        Gtk.ScrolledWindow scrolled_window;
26        Gtk.FlowBox view_episodes;
27        Granite.Widgets.AlertView alert_view;
28        Gee.ArrayList<Audience.Objects.Video> shown_episodes;
29
30        public Audience.Services.LibraryManager manager;
31
32        string query;
33        Objects.Video poster_source;
34
35        construct {
36            query = "";
37            poster_source = null;
38
39            poster = new Gtk.Image ();
40            poster.margin = 24;
41            poster.margin_end = 0;
42            poster.valign = Gtk.Align.START;
43            poster.get_style_context ().add_class ("card");
44
45            view_episodes = new Gtk.FlowBox ();
46            view_episodes.margin = 24;
47            view_episodes.homogeneous = true;
48            view_episodes.valign = Gtk.Align.START;
49            view_episodes.selection_mode = Gtk.SelectionMode.NONE;
50            view_episodes.set_sort_func (episode_sort_func);
51            view_episodes.set_filter_func (episodes_filter_func);
52            view_episodes.child_activated.connect (play_video);
53            view_episodes.max_children_per_line = 1;
54
55            scrolled_window = new Gtk.ScrolledWindow (null, null);
56            scrolled_window.expand = true;
57            scrolled_window.add (view_episodes);
58
59            alert_view = new Granite.Widgets.AlertView ("", "", "");
60            alert_view.get_style_context ().remove_class (Gtk.STYLE_CLASS_VIEW);
61            alert_view.hide ();
62
63            expand = true;
64            attach (poster, 0, 1, 1, 1);
65            attach (scrolled_window, 1, 1, 1, 1);
66            attach (alert_view, 1, 1, 1, 1);
67
68            manager = Audience.Services.LibraryManager.get_instance ();
69            manager.video_file_deleted.connect (remove_item_from_path);
70            manager.video_file_detected.connect (add_item);
71        }
72
73        public void set_episodes_items (Gee.ArrayList<Audience.Objects.Video> episodes) {
74            view_episodes.forall ((item) => {
75                item.dispose ();
76            });
77            shown_episodes = new Gee.ArrayList<Audience.Objects.Video> ();
78            foreach (Audience.Objects.Video episode in episodes) {
79                view_episodes.add (new Audience.LibraryItem (episode, LibraryItemStyle.ROW));
80                shown_episodes.add (episode);
81            }
82            shown_episodes.sort ((a, b) => {
83                var item1 = (Audience.Objects.Video)a;
84                var item2 = (Audience.Objects.Video)b;
85                if (item1 != null && item2 != null) {
86                    return item1.file.collate (item2.file);
87                }
88                return 0;
89            });
90            if (poster_source != null) {
91                poster_source.poster_changed.disconnect (update_poster);
92            }
93            poster_source = episodes.first ();
94            update_poster (poster_source);
95            poster_source.poster_changed.connect (update_poster);
96        }
97
98        private void update_poster (Objects.Video episode) {
99            poster.pixbuf = episode.poster;
100        }
101
102        private void play_video (Gtk.FlowBoxChild item) {
103            var selected = (item as Audience.LibraryItem);
104            var video = selected.episodes.first ();
105            if (video.video_file.query_exists ()) {
106                string uri = video.video_file.get_uri ();
107                bool from_beginning = uri != settings.get_string ("current-video");
108                var window = App.get_instance ().mainwindow;// Clean playlist
109                window.clear_playlist ();
110                window.add_to_playlist (uri, false);
111                window.play_file (uri, Window.NavigationPage.EPISODES, from_beginning);
112                if (window.autoqueue_next_active ()) {
113                    // Add next from the current view to the queue
114                    int played_index = shown_episodes.index_of (video);
115                    foreach (Audience.Objects.Video episode in shown_episodes.slice (played_index, shown_episodes.size)) {
116                        window.append_to_playlist (episode.video_file);
117                    }
118                }
119            }
120        }
121
122        public void filter (string text) {
123             query = text.strip ();
124             view_episodes.invalidate_filter ();
125             if (!has_child ()) {
126                show_alert (_("No Results for “%s”").printf (text), _("Try changing search terms."), "edit-find-symbolic");
127             } else {
128                alert_view.hide ();
129             }
130        }
131
132        private bool episodes_filter_func (Gtk.FlowBoxChild child) {
133            if (query.length == 0) {
134                return true;
135            }
136
137            string[] filter_elements = query.split (" ");
138            var video_title = ((LibraryItem)(child)).get_title ();
139
140            foreach (string filter_element in filter_elements) {
141                if (!video_title.down ().contains (filter_element.down ())) {
142                    return false;
143                }
144            }
145            return true;
146        }
147
148        private int episode_sort_func (Gtk.FlowBoxChild child1, Gtk.FlowBoxChild child2) {
149            var item1 = (LibraryItem)child1;
150            var item2 = (LibraryItem)child2;
151            if (item1 != null && item2 != null) {
152                return item1.episodes.first ().file.collate (item2.episodes.first ().file);
153            }
154            return 0;
155        }
156
157        private void add_item (Audience.Objects.Video episode) {
158            if (view_episodes.get_children ().length () > 0 ) {
159                var first = (view_episodes.get_children ().first ().data as Audience.LibraryItem);
160                if (first != null && first.episodes.first ().video_file.get_parent ().get_path () == episode.video_file.get_parent ().get_path ()) {
161                    view_episodes.add (new Audience.LibraryItem (episode, LibraryItemStyle.ROW));
162                }
163            }
164        }
165
166        private async void remove_item_from_path (string path ) {
167            foreach (var child in view_episodes.get_children ()) {
168                if (((LibraryItem)(child)).episodes.size == 0 ||
169                    ((LibraryItem)(child)).episodes.first ().video_file.get_path ().has_prefix (path)) {
170
171                    child.dispose ();
172                }
173            }
174
175            if (Audience.App.get_instance ().mainwindow.get_visible_child () == this && view_episodes.get_children ().length () == 0) {
176                Audience.App.get_instance ().mainwindow.navigate_back ();
177            }
178        }
179
180        public bool has_child () {
181            if (view_episodes.get_children ().length () > 0) {
182               foreach (unowned Gtk.Widget child in view_episodes.get_children ()) {
183                   if (child.get_child_visible ()) {
184                       return true;
185                   }
186                }
187            }
188            return false;
189        }
190
191        public void show_alert (string primary_text, string secondary_text, string icon_name) {
192            alert_view.no_show_all = false;
193            alert_view.show_all ();
194            alert_view.title = primary_text;
195            alert_view.description = secondary_text;
196            alert_view.icon_name = icon_name;
197            alert_view.show ();
198        }
199    }
200}
201