package ctbrec.recorder;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.io.Files;
import com.iheartradio.m3u8.Encoding;
import com.iheartradio.m3u8.Format;
import com.iheartradio.m3u8.ParseException;
import com.iheartradio.m3u8.ParsingMode;
import com.iheartradio.m3u8.PlaylistException;
import com.iheartradio.m3u8.PlaylistParser;
import com.iheartradio.m3u8.data.MediaPlaylist;
import com.iheartradio.m3u8.data.Playlist;
import com.iheartradio.m3u8.data.TrackData;
import com.iheartradio.m3u8.data.TrackInfo;

import ctbrec.MpegUtil;


public class AccuratePlaylistGenerator extends PlaylistGenerator {
    private static final Logger LOG = LoggerFactory.getLogger(AccuratePlaylistGenerator.class);

    AccuratePlaylistGenerator() {
    }

    @Override
    public File generate(File directory, String fileSuffix) throws IOException, ParseException, PlaylistException {
        LOG.info("Starting playlist generation for {}", directory);
        File[] files = scanDirectoryForSegments(directory, fileSuffix);

        // create a track containing all files
        List<TrackData> track = new ArrayList<>();
        int total = files.length;
        int done = 0;
        for (File file : files) {
            try {
                float duration = 0;
                if (file.getName().toLowerCase().endsWith(".ts")) {
                    duration = (float) MpegUtil.getFileDurationInSecs(file);
                    if (duration <= 0) {
                        throw new InvalidTrackLengthException("Track has negative duration: " + file.getName());
                    }
                }

                track.add(new TrackData.Builder()
                        .withUri(file.getName())
                        .withTrackInfo(new TrackInfo(duration, file.getName()))
                        .build());
            } catch (Exception | AssertionError e) {
                LOG.warn("Couldn't determine duration for {}. Skipping this file.", file.getName());
                File corruptedFile = new File(directory, file.getName() + ".corrupt");
                Files.move(file, corruptedFile);
            }
            done++;
            double percentage = (double) done / (double) total;
            updateProgressListeners(percentage);
        }

        return writePlaylistFile(directory, track);
    }

    @Override
    public void validate(File recDir) throws IOException, ParseException, PlaylistException {
        File playlist = new File(recDir, "playlist.m3u8");
        if (playlist.exists()) {
            try (FileInputStream fin = new FileInputStream(playlist)) {
                PlaylistParser playlistParser = new PlaylistParser(fin, Format.EXT_M3U, Encoding.UTF_8, ParsingMode.LENIENT);
                Playlist m3u = playlistParser.parse();
                MediaPlaylist mediaPlaylist = m3u.getMediaPlaylist();
                int playlistSize = mediaPlaylist.getTracks().size();
                File[] segments = recDir.listFiles((FilenameFilter) (dir, name) -> name.endsWith(".ts"));
                if (segments.length == 0) {
                    throw new InvalidPlaylistException("No segments found. Playlist is empty");
                } else if (segments.length != playlistSize) {
                    throw new InvalidPlaylistException("Playlist size and amount of segments differ (" + segments.length + " != " + playlistSize + ")");
                } else {
                    LOG.debug("Generated playlist looks good");
                }
            }
        } else {
            throw new FileNotFoundException(playlist.getAbsolutePath() + " does not exist");
        }
    }
}
