package ctbrec.recorder;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

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

import com.iheartradio.m3u8.Encoding;
import com.iheartradio.m3u8.Format;
import com.iheartradio.m3u8.ParseException;
import com.iheartradio.m3u8.PlaylistException;
import com.iheartradio.m3u8.PlaylistWriter;
import com.iheartradio.m3u8.data.MediaPlaylist;
import com.iheartradio.m3u8.data.Playlist;
import com.iheartradio.m3u8.data.PlaylistType;
import com.iheartradio.m3u8.data.TrackData;

public abstract class PlaylistGenerator {

    private static final Logger LOG = LoggerFactory.getLogger(PlaylistGenerator.class);
    private int lastPercentage;
    private List<ProgressListener> listeners = new ArrayList<>();

    public static PlaylistGenerator newInstance(boolean fast) {
        if(fast) {
            return new FastPlaylistGenerator();
        } else {
            return new AccuratePlaylistGenerator();
        }
    }

    public File generate(File directory) throws IOException, ParseException, PlaylistException {
        return generate(directory, "ts");
    }

    public abstract File generate(File directory, String fileSuffix) throws IOException, ParseException, PlaylistException;

    public void addProgressListener(ProgressListener l) {
        listeners.add(l);
    }

    public int getProgress() {
        return lastPercentage;
    }

    protected void updateProgressListeners(double percentage) {
        int p = (int) (percentage * 100);
        if (p > lastPercentage) {
            for (ProgressListener progressListener : listeners) {
                progressListener.update(p);
            }
            lastPercentage = p;
        }
    }

    public static class InvalidPlaylistException extends RuntimeException {
        public InvalidPlaylistException(String msg) {
            super(msg);
        }
    }

    public static class InvalidTrackLengthException extends RuntimeException {
        public InvalidTrackLengthException(String msg) {
            super(msg);
        }
    }

    public abstract void validate(File recDir) throws IOException, ParseException, PlaylistException;

    protected File[] scanDirectoryForSegments(File directory, String fileSuffix) {
        // get a list of all ts files and sort them by sequence
        File[] files = directory.listFiles(f -> f.getName().endsWith('.' + fileSuffix));
        if (files == null || files.length == 0) {
            LOG.debug("{} is empty. Not going to generate a playlist", directory);
            throw new InvalidPlaylistException("Directory is empty");
        }

        Arrays.sort(files, (f1, f2) -> {
            String n1 = f1.getName();
            String n2 = f2.getName();
            return n1.compareTo(n2);
        });
        return files;
    }

    protected File writePlaylistFile(File directory, List<TrackData> track) throws IOException, ParseException, PlaylistException {
        // create a media playlist
        float targetDuration = getAvgDuration(track);
        MediaPlaylist playlist = new MediaPlaylist.Builder()
                .withPlaylistType(PlaylistType.VOD)
                .withMediaSequenceNumber(0)
                .withTargetDuration((int) targetDuration)
                .withTracks(track).build();

        // create a master playlist containing the media playlist
        Playlist master = new Playlist.Builder()
                .withCompatibilityVersion(4)
                .withExtended(true)
                .withMediaPlaylist(playlist)
                .build();

        // write the playlist to a file
        File output = new File(directory, "playlist.m3u8");
        try (FileOutputStream fos = new FileOutputStream(output)) {
            PlaylistWriter writer = new PlaylistWriter.Builder()
                    .withFormat(Format.EXT_M3U)
                    .withEncoding(Encoding.UTF_8)
                    .withOutputStream(fos)
                    .build();
            writer.write(master);
            LOG.debug("Finished playlist generation for {}", directory);
        }
        return output;
    }

    private float getAvgDuration(List<TrackData> track) {
        float targetDuration = 0;
        for (TrackData trackData : track) {
            targetDuration += trackData.getTrackInfo().duration;
        }
        targetDuration /= track.size();
        return targetDuration;
    }
}
