
import { Buffer } from 'buffer';
const pako = require('pako');

interface PNGChunk {
    length: number;
    type: string;
    data: Buffer;
    crc: Buffer;
}

export class QuestLog {
    private static PNG_SIGNATURE = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
    private static readonly formatChecker = /^[\x00-\x7F]*$/;
    static isValidMessage(message: string): boolean {
        if (!message || message.length === 0) {
            return false;
        }
        return QuestLog.formatChecker.test(message);
    }

    static extractMessageFromUint8Array(uint8Array:Uint8Array): string {
        return QuestLog.extractMessageFromBuffer(Buffer.from(uint8Array));
    }
    /**
     * Extracts hidden message from a PNG file
     */
    static extractMessageFromBuffer(fileData:Buffer): string {
        // const fileData = await fs.readFile(imagePath);
        
        if (!this.isPNG(fileData)) {
            throw new Error('File is not a valid PNG');
        }

        const chunks = this.parseChunks(fileData);
        console.log("chunks length:",chunks.length);
        const idatChunk = chunks.find(chunk => chunk.type === 'IDAT');
        const ihdrChunk = chunks.find(chunk => chunk.type === 'IHDR');
        
        if (!idatChunk || !ihdrChunk) {
            throw new Error('Invalid PNG structure');
        }

        console.log("Decompress data");
        // Decompress the image data
        const decompressedData = pako.inflate(idatChunk.data);
        
        let binaryMessage = '';
        let currentByte = '';
        let message = '';

        console.log("read width");
        const width = ihdrChunk.data.readUInt32BE(0) * 3;

        console.log("read compressed data");
        for (let i = 0; i < decompressedData.length; i++) {
            // Skip filter byte at the start of each scanline
            if (i % (width + 1) === 0) continue;

            const bit = decompressedData[i] & 1;
            currentByte += bit;

            if (currentByte.length === 8) {
                const charCode = parseInt(currentByte, 2);
                if (charCode === 0) break; // Found null terminator

                message += String.fromCharCode(charCode);
                currentByte = '';
            }
        }

        if(message.endsWith("ED")){
            message = message.substring(0, message.length - 2);
        }
        if (!this.isValidMessage(message)) {
            return null;
        }
        console.log("mSg", message);
        return message;
    }

    private static isPNG(buffer: Buffer): boolean {
        return buffer.slice(0, 8).equals(this.PNG_SIGNATURE);
    }

    private static parseChunks(buffer: Buffer): PNGChunk[] {
        const chunks: PNGChunk[] = [];
        let offset = 8; // Skip PNG signature

        while (offset < buffer.length) {
            const length = buffer.readUInt32BE(offset);
            const type = buffer.toString('ascii', offset + 4, offset + 8);
            const data = buffer.slice(offset + 8, offset + 8 + length);
            const crc = buffer.slice(
                offset + 8 + length,
                offset + 8 + length + 4
            );

            chunks.push({ length, type, data, crc });
            offset += 12 + length;
        }

        return chunks;
    }

    private static serializeChunk(chunk: PNGChunk): Buffer {
        const lengthBuf = Buffer.alloc(4);
        lengthBuf.writeUInt32BE(chunk.data.length);

        return Buffer.concat([
            lengthBuf,
            Buffer.from(chunk.type),
            chunk.data,
            chunk.crc
        ]);
    }
}