/*
 * SDK Pullenti Unitext, version 4.33, fabruary 2026. Copyright (c) 2013-2026, Pullenti. All rights reserved. 
 * Non-Commercial Freeware and Commercial Software. 
 * This class is generated using the converter UniSharping (www.unisharping.ru) from Pullenti C# project. 
 * The latest version of the code is available on the site www.pullenti.ru
 */

const fs = require('fs');
const Utils = require("./../../../unisharp/Utils");
const Stream = require("./../../../unisharp/Stream");
const FileStream = require("./../../../unisharp/FileStream");

const FileUpdateMode = require("./FileUpdateMode");
const BaseArchiveStorage = require("./BaseArchiveStorage");

/**
 * An <see cref="IArchiveStorage"/> implementation suitable for hard disks.
 */
class DiskArchiveStorage extends BaseArchiveStorage {
    
    /**
     * Initializes a new instance of the <see cref="DiskArchiveStorage"/> class.
     * @param file The file.
     * @param _updateMode The update mode.
     */
    constructor(file, _updateMode = FileUpdateMode.SAFE) {
        super(_updateMode);
        this.temporaryStream_ = null;
        this.fileName_ = null;
        this.temporaryName_ = null;
        if (file.name === null) 
            throw new Error("Cant handle non file archives");
        this.fileName_ = file.name;
    }
    
    /**
     * Gets a temporary output <see cref="Stream"/> for performing updates on.
     * @return Returns the temporary output stream.
     */
    getTemporaryOutput() {
        if (this.temporaryName_ !== null) {
            this.temporaryName_ = DiskArchiveStorage.getTempFileName(this.temporaryName_, true);
            this.temporaryStream_ = new FileStream(this.temporaryName_, "r+", false);
        }
        else {
            this.temporaryName_ = "temp.bin";
            this.temporaryStream_ = new FileStream(this.temporaryName_, "r+", false);
        }
        return this.temporaryStream_;
    }
    
    /**
     * Converts a temporary <see cref="Stream"/> to its final form.
     * @return Returns a <see cref="Stream"/> that can be used to read 
     * the final storage for the archive.
     */
    convertTemporaryToFinal() {
        if (this.temporaryStream_ === null) 
            throw new Error("No temporary stream has been created");
        let result = null;
        let moveTempName = DiskArchiveStorage.getTempFileName(this.fileName_, false);
        let newFileCreated = false;
        try {
            this.temporaryStream_.close();
            fs.renameSync(this.fileName_, moveTempName);
            fs.renameSync(this.temporaryName_, this.fileName_);
            newFileCreated = true;
            fs.unlinkSync(moveTempName);
            result = new FileStream(this.fileName_, "r", false);
        } catch (ex555) {
            result = null;
            if (!newFileCreated) {
                fs.renameSync(moveTempName, this.fileName_);
                fs.unlinkSync(this.temporaryName_);
            }
            throw ex555;
        }
        return result;
    }
    
    /**
     * Make a temporary copy of a stream.
     * @param stream The <see cref="Stream"/> to copy.
     * @return Returns a temporary output <see cref="Stream"/> that is a copy of the input.
     */
    makeTemporaryCopy(stream) {
        stream.close();
        this.temporaryName_ = DiskArchiveStorage.getTempFileName(this.fileName_, true);
        fs.copyFile(this.fileName_, this.temporaryName_);
        this.temporaryStream_ = new FileStream(this.temporaryName_, "r+", false);
        return this.temporaryStream_;
    }
    
    /**
     * Return a stream suitable for performing direct updates on the original source.
     * @param stream The current stream.
     * @return Returns a stream suitable for direct updating.
     */
    openForDirectUpdate(stream) {
        let result = null;
        if ((stream === null) || !stream.canWrite()) {
            if (stream !== null) 
                stream.close();
            return new FileStream(this.fileName_, "r+", false);
        }
        else 
            result = stream;
        return result;
    }
    
    /**
     * Disposes this instance.
     */
    close() {
        if (this.temporaryStream_ !== null) {
            this.temporaryStream_.close();
            this.temporaryStream_ = null;
        }
    }
    
    static getTempFileName(original, makeTempFile) {
        let result = null;
        if (original === null) 
            result = "temp.bin";
        else {
            let counter = 0;
            let suffixSeed = Utils.now().getSeconds();
            while (result === null) {
                counter += 1;
                let newName = (original + "." + suffixSeed + counter + ".tmp");
                if (!fs.existsSync(newName) && fs.statSync(newName).isFile()) {
                    if (makeTempFile) {
                        try {
                            let stream = new FileStream(newName, "w+", false); 
                            try {
                            }
                            finally {
                                stream.close();
                            }
                            result = newName;
                        } catch (ex556) {
                            suffixSeed = Utils.now().getSeconds();
                        }
                    }
                    else 
                        result = newName;
                }
            }
        }
        return result;
    }
}


module.exports = DiskArchiveStorage