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 LibraryPage : Gtk.Grid {
24
25        public signal void filter_result_changed (bool has_results);
26        public signal void show_episodes (Audience.LibraryItem item, bool setup_only = false);
27
28        public Gtk.FlowBox view_movies;
29        public Audience.Services.LibraryManager manager;
30        public Gtk.ScrolledWindow scrolled_window;
31        bool posters_initialized = false;
32        string query;
33
34        public string last_filter { get; set; default = ""; }
35
36        public bool has_items { get { return view_movies.get_children ().length () > 0; } }
37
38        public static LibraryPage instance = null;
39        public static LibraryPage get_instance () {
40            if (instance == null) {
41                instance = new LibraryPage ();
42            }
43            return instance;
44        }
45
46        construct {
47            manager = Audience.Services.LibraryManager.get_instance ();
48
49            query = "";
50
51            scrolled_window = new Gtk.ScrolledWindow (null, null);
52            scrolled_window.expand = true;
53
54            view_movies = new Gtk.FlowBox ();
55            view_movies.margin = 24;
56            view_movies.homogeneous = true;
57            view_movies.row_spacing = 12;
58            view_movies.column_spacing = 12;
59            view_movies.valign = Gtk.Align.START;
60            view_movies.selection_mode = Gtk.SelectionMode.NONE;
61            view_movies.child_activated.connect (play_video);
62
63            scrolled_window.add (view_movies);
64
65            manager = Audience.Services.LibraryManager.get_instance ();
66            manager.video_file_detected.connect (add_item);
67            manager.video_file_deleted.connect (remove_item_from_path);
68            manager.video_moved_to_trash.connect ((video) => {
69                Audience.App.get_instance ().mainwindow.set_app_notification (_("Video '%s' Removed.").printf (Path.get_basename (video)));
70            });
71
72            manager.begin_scan ();
73
74            map.connect (() => {
75                if (!posters_initialized) {
76                    posters_initialized = true;
77                    poster_initialisation.begin ();
78                }
79            });
80
81            view_movies.set_sort_func (video_sort_func);
82            view_movies.set_filter_func (video_filter_func);
83
84            add (scrolled_window);
85        }
86
87        private void play_video (Gtk.FlowBoxChild item) {
88            var selected = (item as Audience.LibraryItem);
89
90            if (selected.episodes.size == 1) {
91                string uri = selected.episodes.first ().video_file.get_uri ();
92                bool same_video = uri == settings.get_string ("current-video");
93                bool playback_complete = settings.get_double ("last-stopped") == 0.0;
94                bool from_beginning = !same_video || playback_complete;
95                var window = App.get_instance ().mainwindow;
96                window.add_to_playlist (uri, !from_beginning);
97                window.play_file (uri, Window.NavigationPage.LIBRARY, from_beginning);
98            } else {
99                last_filter = query;
100                show_episodes (selected);
101            }
102        }
103
104        public void add_item (Audience.Objects.Video video) {
105            foreach (var child in view_movies.get_children ()) {
106                if (video.container != null && ((LibraryItem)(child)).episodes.first ().container == video.container) {
107                    ((LibraryItem)(child)).add_episode (video);
108                    return;
109                }
110            }
111            Audience.LibraryItem new_container = new Audience.LibraryItem (video, LibraryItemStyle.THUMBNAIL);
112            view_movies.add (new_container);
113            if (posters_initialized) {
114                video.initialize_poster.begin ();
115                new_container.show_all ();
116            }
117        }
118
119        private async void remove_item (LibraryItem item) {
120            foreach (var video in item.episodes) {
121                manager.clear_cache.begin (video.poster_cache_file);
122            }
123            item.dispose ();
124        }
125
126        private async void remove_item_from_path (string path ) {
127            foreach (var child in view_movies.get_children ()) {
128                if (((LibraryItem)(child)).episodes.size == 0 ||
129                    ((LibraryItem)(child)).episodes.first ().video_file.get_path ().has_prefix (path)) {
130
131                    remove_item.begin (child as LibraryItem);
132                }
133            }
134
135            if (view_movies.get_children ().length () == 0) {
136                Audience.App.get_instance ().mainwindow.navigate_back ();
137            }
138        }
139
140        private async void poster_initialisation () {
141            foreach (var child in view_movies.get_children ()) {
142                var first_episode = ((LibraryItem)(child)).episodes.first ();
143                if (!first_episode.poster_initialized) {
144                    first_episode.initialize_poster.begin ();
145                }
146            }
147        }
148
149        private bool video_filter_func (Gtk.FlowBoxChild child) {
150            if (query.length == 0) {
151                return true;
152            }
153
154            string[] filter_elements = query.split (" ");
155            var video_title = ((LibraryItem)(child)).get_title ();
156
157            foreach (string filter_element in filter_elements) {
158                if (!video_title.down ().contains (filter_element.down ())) {
159                    return false;
160                }
161            }
162            return true;
163        }
164
165        private int video_sort_func (Gtk.FlowBoxChild child1, Gtk.FlowBoxChild child2) {
166            var item1 = (LibraryItem)child1;
167            var item2 = (LibraryItem)child2;
168            if (item1 != null && item2 != null) {
169                return item1.get_title ().collate (item2.get_title ());
170            }
171            return 0;
172        }
173
174        public void filter (string text) {
175            query = text.strip ();
176            view_movies.invalidate_filter ();
177            filter_result_changed (has_child ());
178        }
179
180        public bool has_child () {
181            if (view_movies.get_children ().length () > 0) {
182               foreach (unowned Gtk.Widget child in view_movies.get_children ()) {
183                   if (child.get_child_visible ()) {
184                       return true;
185                   }
186                }
187            }
188            return false;
189        }
190
191        public Audience.Window.NavigationPage prepare_to_play (string file) {
192            if (!File.new_for_uri (file).has_prefix (File.new_for_path (settings.get_string ("library-folder")))) {
193                return Window.NavigationPage.WELCOME;
194            }
195
196            foreach (var child in view_movies.get_children ()) {
197                var item = child as LibraryItem;
198                var episodes = item.episodes;
199                foreach (var episode in episodes) {
200                    string ep_file = episode.video_file.get_uri ();
201                    if (ep_file == file) {
202                        if (episodes.size > 1) {
203                            var first_episode = episodes.first ();
204                            if (!first_episode.poster_initialized) {
205                                first_episode.initialize_poster.begin ();
206                            }
207                            show_episodes (item, true);
208                            return Window.NavigationPage.EPISODES;
209                        } else {
210                            return Window.NavigationPage.LIBRARY;
211                        }
212                    }
213                }
214            }
215            return Window.NavigationPage.WELCOME;
216        }
217    }
218}
219