package ctbrec.recorder.download.hls;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.ExecutorService;
import java.util.regex.Pattern;

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

import com.iheartradio.m3u8.ParseException;
import com.iheartradio.m3u8.PlaylistException;

import ctbrec.Config;
import ctbrec.Model;
import ctbrec.Recording;
import ctbrec.Recording.State;
import ctbrec.io.HttpClient;
import ctbrec.io.IoUtils;
import ctbrec.recorder.PlaylistGenerator;

public class HlsDownload extends AbstractHlsDownload {

    private static final Logger LOG = LoggerFactory.getLogger(HlsDownload.class);

    protected transient Path downloadDir;

    public HlsDownload(HttpClient client) {
        super(client);
    }

    @Override
    public void init(Config config, Model model, Instant startTime, ExecutorService executorService) throws IOException {
        super.init(config, model, startTime, executorService);
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(Config.RECORDING_DATE_FORMAT);
        String formattedStartTime = formatter.format(ZonedDateTime.ofInstant(this.startTime, ZoneId.systemDefault()));
        Path modelDir = FileSystems.getDefault().getPath(config.getSettings().recordingsDir, model.getSanitizedNamed());
        downloadDir = FileSystems.getDefault().getPath(modelDir.toString(), formattedStartTime);
        createTargetDirectory();
    }

    protected void createTargetDirectory() throws IOException {
        if (!downloadDir.toFile().exists()) {
            Files.createDirectories(downloadDir);
        }
    }

    @Override
    public void finalizeDownload() {
        LOG.debug("Download for {} terminated", model);
    }

    @Override
    public void postprocess(Recording recording) {
        Thread.currentThread().setName("PP " + model.getName());
        recording.setStatusWithEvent(State.GENERATING_PLAYLIST);
        try {
            generatePlaylist(recording);
            recording.setStatusWithEvent(State.POST_PROCESSING);
        } catch (Exception e) {
            throw new PostProcessingException(e);
        }
    }

    protected File generatePlaylist(Recording recording) throws IOException, ParseException, PlaylistException {
        File recDir = recording.getAbsoluteFile();
        if (!config.getSettings().generatePlaylist) {
            return null;
        }
        PlaylistGenerator playlistGenerator = PlaylistGenerator.newInstance(config.getSettings().fastPlaylistGenerator);
        playlistGenerator.addProgressListener(recording::setProgress);
        File playlist = playlistGenerator.generate(recDir);
        if (playlist != null) {
            playlistGenerator.validate(recDir);
        }
        recording.setProgress(-1);
        return playlist;

    }

    @Override
    public void stop() {
        LOG.debug("Recording stopped");
        internalStop();
    }

    @Override
    protected void internalStop() {
        running = false;
    }

    @Override
    public File getTarget() {
        return downloadDir.toFile();
    }

    @Override
    public String getPath(Model model) {
        String absolutePath = getTarget().getAbsolutePath();
        String recordingsDir = Config.getInstance().getSettings().recordingsDir;
        String relativePath = absolutePath.replaceFirst(Pattern.quote(recordingsDir), "");
        return relativePath;
    }

    @Override
    public boolean isSingleFile() {
        return false;
    }

    @Override
    public long getSizeInByte() {
        return IoUtils.getDirectorySize(getTarget());
    }

    @Override
    protected OutputStream getSegmentOutputStream(String prefix, String fileName) throws FileNotFoundException {
        String prefixedFileName = prefix + '_' + fileName;
        int questionMarkPosition = prefixedFileName.indexOf('?');
        if (questionMarkPosition > 0) {
            prefixedFileName = prefixedFileName.substring(0, questionMarkPosition);
        }
        File file = FileSystems.getDefault().getPath(downloadDir.toAbsolutePath().toString(), prefixedFileName).toFile();
        return new FileOutputStream(file);
    }

    @Override
    protected void segmentDownloadFinished(SegmentDownload segmentDownload) {
        IoUtils.close(segmentDownload.getOutputStream(), "Couldn't close segment file");
    }
}
