/**
 **
 ** LogTee.java - PrintStream tee that copies output to both a stream and a log
 **
 ** Copyright (C) 2026 Elphel, Inc.
 **
 ** -----------------------------------------------------------------------------**
 **
 **  LogTee.java is free software: you can redistribute it and/or modify
 **  it under the terms of the GNU General Public License as published by
 **  the Free Software Foundation, either version 3 of the License, or
 **  (at your option) any later version.
 **
 **  This program is distributed in the hope that it will be useful,
 **  but WITHOUT ANY WARRANTY; without even the implied warranty of
 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 **  GNU General Public License for more details.
 **
 **  You should have received a copy of the GNU General Public License
 **  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ** -----------------------------------------------------------------------------**
 **
 */
package com.elphel.imagej.common;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.nio.file.Path;

/**
 * Tee System.out/System.err to a per-scene log file while preserving console output.
 * Usage:
 *   LogTee.install(); // once at startup
 *   LogTee.setSceneLog(path); // enable per-scene logging (append)
 *   LogTee.clearSceneLog();   // stop file logging
 */
public final class LogTee {
    private static final Object LOCK = new Object();
    private static final PrintStream ORIGINAL_OUT = System.out;
    private static final PrintStream ORIGINAL_ERR = System.err;
    private static volatile PrintStream fileStream = null;
    private static volatile Path sceneLogPath = null;
    private static volatile boolean installed = false;

    private LogTee() {
    }

    public static void install() {
        synchronized (LOCK) {
            if (installed) return;
            System.setOut(new PrintStream(new TeeStream(ORIGINAL_OUT), true));
            System.setErr(new PrintStream(new TeeStream(ORIGINAL_ERR), true));
            installed = true;
        }
    }

    public static void setSceneLog(Path path) throws IOException {
        if (path == null) {
            clearSceneLog();
            return;
        }
        synchronized (LOCK) {
            closeFileStream();
            fileStream = new PrintStream(new FileOutputStream(path.toFile(), true), true);
            sceneLogPath = path;
        }
    }

    public static void clearSceneLog() {
        synchronized (LOCK) {
            closeFileStream();
            fileStream = null;
            sceneLogPath = null;
        }
    }

    public static Path getSceneLog() {
        return sceneLogPath;
    }

    private static void closeFileStream() {
        if (fileStream != null) {
            fileStream.flush();
            fileStream.close();
        }
    }

    private static final class TeeStream extends OutputStream {
        private final PrintStream console;

        private TeeStream(PrintStream console) {
            this.console = console;
        }

        @Override
        public void write(int b) throws IOException {
            console.write(b);
            PrintStream fs = fileStream;
            if (fs != null) fs.write(b);
        }

        @Override
        public void write(byte[] b, int off, int len) throws IOException {
            console.write(b, off, len);
            PrintStream fs = fileStream;
            if (fs != null) fs.write(b, off, len);
        }

        @Override
        public void flush() throws IOException {
            console.flush();
            PrintStream fs = fileStream;
            if (fs != null) fs.flush();
        }
    }
}
