package de.duehl.basics.io.textfile.big;

/*
 * Copyright 2026 Christian Dühl. All rights reserved.
 *
 * This program is free software. You can redistribute it and/or
 * modify it under the same terms as perl:
 *
 * general:  http://dev.perl.org/licenses/
 * GPL:      http://dev.perl.org/licenses/gpl1.html
 * artistic: http://dev.perl.org/licenses/artistic.html
 */

import java.util.ArrayList;
import java.util.List;

import de.duehl.basics.io.FineFileReader;
import de.duehl.basics.io.FineFileWriter;
import de.duehl.basics.io.Reader;
import de.duehl.basics.io.Writer;
import de.duehl.basics.io.textfile.big.data.InteresstingLineCriterion;

/**
 * Diese Klasse liest eine sehr große Datei ein, um Abschnitte mit interessanten Zeilen zu
 * extrahieren.
 *
 * @version 1.01     2026-06-30
 * @author Christian Dühl
 */

public class BigFilePartsWithSpecialEndsHandler {

    /** Der Dateiname der einzulesenden Datei. */
    private final String filename;

    /** Gibt an, ob eine eingelesene Zeile interessant ist. */
    private final InteresstingLineCriterion criterion;

    /** Die Anzahl an Zeilen vor und nach interessanten Zeilen, welche mit ausgegeben werden. */
    private final int numberOfLinesBevoreAndAfter;

    /** Der Dateiname der zu schreibenden Ergebnisdatei. */
    private final String outputFilename;

    /** Liest die Eingabedatei ein. */
    private Reader reader;

    /** Schreibt die Ausgabedatei. */
    private Writer writer;

    /** Gibt an, ob zuletzt eine interessante Zeile gelesen wurde. */
    private boolean interessting;

    /** Zählt, wieviele Zeilen nach interessanten Zeilen bereits ausgegeben wurden. */
    private int countAfterInteressting;

    /** Speichert die letzten Zeilen potentiell vor einer interessante Zeile. */
    private List<String> linesBefore;

    /**
     * Konstruktor.
     *
     * @param filename
     *            Der Dateiname der einzulesenden Datei.
     * @param criterion
     *            Gibt an, ob eine eingelesene Zeile interessant ist.
     * @param numberOfLinesBevoreAndAfter
     *            Die Anzahl an Zeilen vor und nach interessanten Zeilen, welche mit ausgegeben
     *            werden.
     * @param outputFilename
     *            Der Dateiname der zu schreibenden Ergebnisdatei.
     */
    public BigFilePartsWithSpecialEndsHandler(String filename, InteresstingLineCriterion criterion,
            int numberOfLinesBevoreAndAfter, String outputFilename) {
        this.filename = filename;
        this.criterion = criterion;
        this.numberOfLinesBevoreAndAfter = numberOfLinesBevoreAndAfter;
        this.outputFilename = outputFilename;
    }

    /** Behandelt die Datei. */
    public void handle() {
        openIo();
        workOnFile();
        closeIo();
    }

    private void openIo() {
        reader = new FineFileReader(filename);
        writer = new FineFileWriter(outputFilename);
    }

    private void workOnFile() {
        String line = reader.readNextLine(); // titles
        writer.writeln(line);

        linesBefore = new ArrayList<>();
        interessting = false;
        countAfterInteressting = 0;

        while ((line = reader.readNextLine()) != null) {
            workOnLine(line);
        }
    }

    private void workOnLine(String line) {
        if (criterion.isInteresting(line)) {
            if (interessting) {
                countAfterInteressting = numberOfLinesBevoreAndAfter;
            }
            else {
                writer.writeAllLines(linesBefore);
                linesBefore.clear();
                countAfterInteressting = numberOfLinesBevoreAndAfter;
                interessting = true;
            }
            writer.writeln(line);
        }
        else {
            interessting = false;
            if (countAfterInteressting > 0) {
                writer.writeln(line);
                --countAfterInteressting;
            }
            else {
                linesBefore.add(line);
                if (linesBefore.size() > numberOfLinesBevoreAndAfter) {
                    linesBefore.remove(0);
                }
            }
        }
    }

    private void closeIo() {
        writer.close();
        reader.close();
    }

}
