/* Copyright 2016 Software Freedom Conservancy Inc.
 *
 * This software is licensed under the GNU LGPL (version 2.1 or later).
 * See the COPYING file in this distribution.
 */

public class VideoSourceCollection : MediaSourceCollection {
    public enum State {
        UNKNOWN,
        ONLINE,
        OFFLINE,
        TRASH
    }

    public override TransactionController transaction_controller {
        get {
            if (_transaction_controller == null)
                _transaction_controller = new MediaSourceTransactionController(this);

            return _transaction_controller;
        }
    }

    private TransactionController _transaction_controller = null;
    private Gee.MultiMap<uint64?, Video> filesize_to_video =
        new Gee.TreeMultiMap<uint64?, Video>(uint64_compare);

    public VideoSourceCollection() {
        base("VideoSourceCollection", get_video_key);

        get_trashcan().contents_altered.connect(on_trashcan_contents_altered);
        get_offline_bin().contents_altered.connect(on_offline_contents_altered);
    }

    protected override MediaSourceHoldingTank create_trashcan() {
        return new MediaSourceHoldingTank(this, is_video_trashed, get_video_key);
    }

    protected override MediaSourceHoldingTank create_offline_bin() {
        return new MediaSourceHoldingTank(this, is_video_offline, get_video_key);
    }

    public override MediaMonitor create_media_monitor(Workers workers, Cancellable cancellable) {
        return new VideoMonitor(cancellable);
    }

    public override bool holds_type_of_source(DataSource source) {
        return source is Video;
    }

    public override string get_typename() {
        return Video.TYPENAME;
    }

    public override bool is_file_recognized(File file) {
        return VideoReader.is_supported_video_file(file);
    }

    private void on_trashcan_contents_altered(Gee.Collection<DataSource>? added,
        Gee.Collection<DataSource>? removed) {
        trashcan_contents_altered((Gee.Collection<Video>?) added,
            (Gee.Collection<Video>?) removed);
    }

    private void on_offline_contents_altered(Gee.Collection<DataSource>? added,
        Gee.Collection<DataSource>? removed) {
        offline_contents_altered((Gee.Collection<Video>?) added,
            (Gee.Collection<Video>?) removed);
    }

    protected override MediaSource? fetch_by_numeric_id(int64 numeric_id) {
        return fetch(VideoID(numeric_id));
    }

    public static int64 get_video_key(DataSource source) {
        Video video = (Video) source;
        VideoID video_id = video.get_video_id();

        return video_id.id;
    }

    public static bool is_video_trashed(DataSource source) {
        return ((Video) source).is_trashed();
    }

    public static bool is_video_offline(DataSource source) {
        return ((Video) source).is_offline();
    }

    public Video fetch(VideoID video_id) {
        return (Video) fetch_by_key(video_id.id);
    }

    public override Gee.Collection<string> get_event_source_ids(EventID event_id){
        return VideoTable.get_instance().get_event_source_ids(event_id);
    }

    public Video? get_state_by_file(File file, out State state) {
        Video? video = (Video?) fetch_by_master_file(file);
        if (video != null) {
            state = State.ONLINE;

            return video;
        }

        video = (Video?) get_trashcan().fetch_by_master_file(file);
        if (video != null) {
            state = State.TRASH;

            return video;
        }

        video = (Video?) get_offline_bin().fetch_by_master_file(file);
        if (video != null) {
            state = State.OFFLINE;

            return video;
        }

        state = State.UNKNOWN;

        return null;
    }

    private void compare_backing(Video video, FileInfo info, Gee.Collection<Video> matching_master) {
        if (video.get_filesize() != info.get_size())
            return;

        if (video.get_timestamp().equal(coarsify_date_time(info.get_modification_date_time())))
            matching_master.add(video);
    }

    public void fetch_by_matching_backing(FileInfo info, Gee.Collection<Video> matching_master) {
        foreach (DataObject object in get_all())
            compare_backing((Video) object, info, matching_master);

        foreach (MediaSource media in get_offline_bin_contents())
            compare_backing((Video) media, info, matching_master);
    }

    protected override void notify_contents_altered(Gee.Iterable<DataObject>? added,
        Gee.Iterable<DataObject>? removed) {
        if (added != null) {
            foreach (DataObject object in added) {
                Video video = (Video) object;

                filesize_to_video.set(video.get_master_filesize(), video);
            }
        }

        if (removed != null) {
            foreach (DataObject object in removed) {
                Video video = (Video) object;

                filesize_to_video.remove(video.get_master_filesize(), video);
            }
        }

        base.notify_contents_altered(added, removed);
    }

    public VideoID get_basename_filesize_duplicate(string basename, uint64 filesize) {
        foreach (Video video in filesize_to_video.get(filesize)) {
            if (utf8_ci_compare(video.get_master_file().get_basename(), basename) == 0)
                return video.get_video_id();
        }

        return VideoID(); // the default constructor of the VideoID struct creates an invalid
                          // video id, which is just what we want in this case
    }

    public bool has_basename_filesize_duplicate(string basename, uint64 filesize) {
        return get_basename_filesize_duplicate(basename, filesize).is_valid();
    }
}