package ctbrec.recorder.postprocessing;

import static ctbrec.StringUtil.*;
import static java.util.Optional.*;

import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import ctbrec.Config;
import ctbrec.Recording;
import ctbrec.sites.Site;

public abstract class AbstractPlaceholderAwarePostProcessor extends AbstractPostProcessor {

    public static final String[] PLACE_HOLDERS = {
            "${modelName}",
            "${modelDisplayName}",
            "${modelSanitizedName}",
            "${siteName}",
            "${siteSanitizedName}",
            "${utcDateTime}",
            "${localDateTime}",
            "${epochSecond}",
            "${fileSuffix}",
            "${modelNotes}",
            "${recordingNotes}",
            "${recordingsDir}",
            "${absolutePath}",
            "${absoluteParentPath}"
    };

    public String fillInPlaceHolders(String input, Recording rec, Config config) {
        // @formatter:off
        String output = input
                .replace("${modelName}", ofNullable(rec.getModel().getName()).orElse("modelName"))
                .replace("${modelDisplayName}", ofNullable(rec.getModel().getDisplayName()).orElse("displayName"))
                .replace("${modelSanitizedName}", ofNullable(rec.getModel().getSanitizedNamed()).orElse("sanitizedName"))
                .replace("${siteName}", ofNullable(rec.getModel().getSite()).map(Site::getName).orElse("site"))
                .replace("${siteSanitizedName}", getSanitizedSiteName(rec))
                .replace("${fileSuffix}", getFileSuffix(rec))
                .replace("${epochSecond}", Long.toString(rec.getStartDate().getEpochSecond()))
                .replace("${modelNotes}", sanitize(config.getModelNotes(rec.getModel())))
                .replace("${recordingNotes}", getSanitizedRecordingNotes(rec))
                .replace("${recordingsDir}", config.getSettings().recordingsDir)
                .replace("${absolutePath}", rec.getPostProcessedFile().getAbsolutePath())
                .replace("${absoluteParentPath}", rec.getPostProcessedFile().getParentFile().getAbsolutePath())
                ;

        output = replaceUtcDateTime(rec, output);
        output = replaceLocalDateTime(rec, output);

        return output;
        // @formatter:on
    }

    private String replaceUtcDateTime(Recording rec, String filename) {
        return replaceDateTime(rec, filename, "utcDateTime", ZoneOffset.UTC);
    }

    private String replaceLocalDateTime(Recording rec, String filename) {
        return replaceDateTime(rec, filename, "localDateTime", ZoneId.systemDefault());
    }

    private String replaceDateTime(Recording rec, String filename, String placeHolder, ZoneId zone) {
        String pattern = "yyyy-MM-dd_HH-mm-ss";
        Pattern regex = Pattern.compile("\\$\\{" + placeHolder + "(?:\\((.*?)\\))?\\}");
        Matcher m = regex.matcher(filename);
        while (m.find()) {
            String p = m.group(1);
            if (p != null) {
                pattern = p;
            }
            String formattedDate = getDateTime(rec, pattern, zone);
            filename = m.replaceFirst(formattedDate);
            m = regex.matcher(filename);
        }
        return filename;
    }

    private String getDateTime(Recording rec, String pattern, ZoneId zone) {
        return DateTimeFormatter.ofPattern(pattern)
                .withLocale(Locale.getDefault())
                .withZone(zone)
                .format(rec.getStartDate());
    }

    private CharSequence getFileSuffix(Recording rec) {
        if(rec.isSingleFile()) {
            String filename = rec.getPostProcessedFile().getName();
            return filename.substring(filename.lastIndexOf('.') + 1);
        } else {
            return "";
        }
    }

    private CharSequence getSanitizedSiteName(Recording rec) {
        return sanitize(ofNullable(rec.getModel().getSite()).map(Site::getName).orElse(""));
    }

    private CharSequence getSanitizedRecordingNotes(Recording rec) {
        return sanitize(ofNullable(rec.getNote()).orElse(""));
    }
}
