'use strict';

var gameAssetTypes = require('./gameAssetTypes.js');

/**
 * gameAssetUtils.ts — Utilities for GameAsset2dEditor
 */
/* ────────────────────────────────────────────────────────────────
   ID GENERATION
──────────────────────────────────────────────────────────────── */
let idCounter = 0;
const generateId = (prefix = 'id') => `${prefix}_${++idCounter}_${Date.now().toString(36)}`;
/* ────────────────────────────────────────────────────────────────
   IMAGE UTILITIES
──────────────────────────────────────────────────────────────── */
const imageDataToDataUrl = (imageData) => {
    const canvas = document.createElement('canvas');
    canvas.width = imageData.width;
    canvas.height = imageData.height;
    const ctx = canvas.getContext('2d');
    ctx.putImageData(imageData, 0, 0);
    return canvas.toDataURL('image/png');
};
const dataUrlToImageData = async (dataUrl) => {
    return new Promise((resolve, reject) => {
        const img = new Image();
        img.onload = () => {
            const canvas = document.createElement('canvas');
            canvas.width = img.width;
            canvas.height = img.height;
            const ctx = canvas.getContext('2d');
            ctx.drawImage(img, 0, 0);
            resolve(ctx.getImageData(0, 0, img.width, img.height));
        };
        img.onerror = reject;
        img.src = dataUrl;
    });
};
const loadImage = (src) => {
    return new Promise((resolve, reject) => {
        const img = new Image();
        img.onload = () => resolve(img);
        img.onerror = reject;
        img.src = src;
    });
};
/* ────────────────────────────────────────────────────────────────
   TILE UTILITIES
──────────────────────────────────────────────────────────────── */
const createTileFromImage = (imageData, name) => {
    return {
        id: generateId('tile'),
        name,
        image: imageData,
        thumbnail: imageDataToDataUrl(imageData),
        width: imageData.width,
        height: imageData.height,
        properties: {},
    };
};
const createEmptyTileLayer = (name, mapWidth, mapHeight) => {
    const tiles = [];
    for (let y = 0; y < mapHeight; y++) {
        tiles.push(new Array(mapWidth).fill(null));
    }
    return {
        id: generateId('layer'),
        name,
        visible: true,
        locked: false,
        opacity: 1,
        tiles,
        parallaxX: 1,
        parallaxY: 1,
        offsetX: 0,
        offsetY: 0,
        zIndex: 0,
    };
};
const createEmptyTileMap = (name, width, height, tileWidth, tileHeight, mode = '2d') => {
    const mapMode = mode === 'tilemap' ? '2d' : mode;
    return {
        id: generateId('map'),
        name,
        mode: mapMode,
        width,
        height,
        tileWidth,
        tileHeight,
        layers: [createEmptyTileLayer('Layer 1', width, height)],
        properties: {},
        backgroundColor: '#1a1a2e',
    };
};
/* ────────────────────────────────────────────────────────────────
   SPRITE SHEET UTILITIES
──────────────────────────────────────────────────────────────── */
const autoSliceSpriteSheet = (imageData, frameWidth, frameHeight, padding = 0) => {
    const frames = [];
    const cols = Math.floor((imageData.width + padding) / (frameWidth + padding));
    const rows = Math.floor((imageData.height + padding) / (frameHeight + padding));
    for (let row = 0; row < rows; row++) {
        for (let col = 0; col < cols; col++) {
            frames.push({
                id: generateId('frame'),
                name: `frame_${row}_${col}`,
                x: col * (frameWidth + padding),
                y: row * (frameHeight + padding),
                width: frameWidth,
                height: frameHeight,
                pivotX: 0.5,
                pivotY: 0.5,
            });
        }
    }
    return frames;
};
const detectFrames = (imageData) => {
    // Simple edge detection - find bounding boxes of non-transparent regions
    const frames = [];
    const visited = new Set();
    const { data, width, height } = imageData;
    const isTransparent = (x, y) => {
        if (x < 0 || y < 0 || x >= width || y >= height)
            return true;
        const idx = (y * width + x) * 4;
        return data[idx + 3] < 10; // alpha threshold
    };
    const floodFillBounds = (startX, startY) => {
        let minX = startX, maxX = startX, minY = startY, maxY = startY;
        const stack = [[startX, startY]];
        while (stack.length > 0) {
            const [x, y] = stack.pop();
            const key = `${x},${y}`;
            if (visited.has(key) || isTransparent(x, y))
                continue;
            visited.add(key);
            minX = Math.min(minX, x);
            maxX = Math.max(maxX, x);
            minY = Math.min(minY, y);
            maxY = Math.max(maxY, y);
            stack.push([x + 1, y], [x - 1, y], [x, y + 1], [x, y - 1]);
        }
        return { minX, maxX, minY, maxY };
    };
    for (let y = 0; y < height; y++) {
        for (let x = 0; x < width; x++) {
            const key = `${x},${y}`;
            if (!visited.has(key) && !isTransparent(x, y)) {
                const bounds = floodFillBounds(x, y);
                const frameWidth = bounds.maxX - bounds.minX + 1;
                const frameHeight = bounds.maxY - bounds.minY + 1;
                if (frameWidth > 1 && frameHeight > 1) {
                    frames.push({
                        id: generateId('frame'),
                        name: `sprite_${frames.length}`,
                        x: bounds.minX,
                        y: bounds.minY,
                        width: frameWidth,
                        height: frameHeight,
                        pivotX: 0.5,
                        pivotY: 1.0, // bottom center for most sprites
                    });
                }
            }
        }
    }
    return frames;
};
const createSpriteAnimation = (name, frameIds, frameDelay = 100) => {
    return {
        id: generateId('anim'),
        name,
        frameIds,
        frameDurations: frameIds.map(() => frameDelay),
        loop: true,
        pingPong: false,
    };
};
/* ────────────────────────────────────────────────────────────────
   EFFECTS PROCESSING
──────────────────────────────────────────────────────────────── */
const applyEffect = (imageData, effect) => {
    const canvas = document.createElement('canvas');
    canvas.width = imageData.width;
    canvas.height = imageData.height;
    const ctx = canvas.getContext('2d');
    ctx.putImageData(imageData, 0, 0);
    const data = imageData.data.slice();
    const result = new ImageData(new Uint8ClampedArray(data), imageData.width, imageData.height);
    switch (effect.type) {
        case 'brightness': {
            const value = effect.params.value || 0;
            for (let i = 0; i < data.length; i += 4) {
                result.data[i] = Math.min(255, Math.max(0, data[i] + value));
                result.data[i + 1] = Math.min(255, Math.max(0, data[i + 1] + value));
                result.data[i + 2] = Math.min(255, Math.max(0, data[i + 2] + value));
            }
            break;
        }
        case 'contrast': {
            const factor = (259 * (effect.params.value + 255)) / (255 * (259 - effect.params.value));
            for (let i = 0; i < data.length; i += 4) {
                result.data[i] = Math.min(255, Math.max(0, factor * (data[i] - 128) + 128));
                result.data[i + 1] = Math.min(255, Math.max(0, factor * (data[i + 1] - 128) + 128));
                result.data[i + 2] = Math.min(255, Math.max(0, factor * (data[i + 2] - 128) + 128));
            }
            break;
        }
        case 'invert': {
            for (let i = 0; i < data.length; i += 4) {
                result.data[i] = 255 - data[i];
                result.data[i + 1] = 255 - data[i + 1];
                result.data[i + 2] = 255 - data[i + 2];
            }
            break;
        }
        case 'saturation': {
            const value = (effect.params.value + 100) / 100;
            for (let i = 0; i < data.length; i += 4) {
                const r = data[i], g = data[i + 1], b = data[i + 2];
                const gray = 0.2989 * r + 0.587 * g + 0.114 * b;
                result.data[i] = Math.min(255, Math.max(0, gray + (r - gray) * value));
                result.data[i + 1] = Math.min(255, Math.max(0, gray + (g - gray) * value));
                result.data[i + 2] = Math.min(255, Math.max(0, gray + (b - gray) * value));
            }
            break;
        }
        case 'hue-shift': {
            const degrees = effect.params.degrees || 0;
            const cos = Math.cos(degrees * Math.PI / 180);
            const sin = Math.sin(degrees * Math.PI / 180);
            for (let i = 0; i < data.length; i += 4) {
                const r = data[i], g = data[i + 1], b = data[i + 2];
                result.data[i] = Math.min(255, Math.max(0, (0.213 + cos * 0.787 - sin * 0.213) * r +
                    (0.715 - cos * 0.715 - sin * 0.715) * g +
                    (0.072 - cos * 0.072 + sin * 0.928) * b));
                result.data[i + 1] = Math.min(255, Math.max(0, (0.213 - cos * 0.213 + sin * 0.143) * r +
                    (0.715 + cos * 0.285 + sin * 0.140) * g +
                    (0.072 - cos * 0.072 - sin * 0.283) * b));
                result.data[i + 2] = Math.min(255, Math.max(0, (0.213 - cos * 0.213 - sin * 0.787) * r +
                    (0.715 - cos * 0.715 + sin * 0.715) * g +
                    (0.072 + cos * 0.928 + sin * 0.072) * b));
            }
            break;
        }
        case 'tint': {
            const color = effect.params.color || '#ffffff';
            const amount = effect.params.amount || 0.5;
            const tr = parseInt(color.slice(1, 3), 16);
            const tg = parseInt(color.slice(3, 5), 16);
            const tb = parseInt(color.slice(5, 7), 16);
            for (let i = 0; i < data.length; i += 4) {
                result.data[i] = Math.round(data[i] * (1 - amount) + tr * amount);
                result.data[i + 1] = Math.round(data[i + 1] * (1 - amount) + tg * amount);
                result.data[i + 2] = Math.round(data[i + 2] * (1 - amount) + tb * amount);
            }
            break;
        }
        case 'pixelate': {
            const size = effect.params.size || 4;
            const { width, height } = imageData;
            for (let y = 0; y < height; y += size) {
                for (let x = 0; x < width; x += size) {
                    let r = 0, g = 0, b = 0, a = 0, count = 0;
                    for (let dy = 0; dy < size && y + dy < height; dy++) {
                        for (let dx = 0; dx < size && x + dx < width; dx++) {
                            const idx = ((y + dy) * width + (x + dx)) * 4;
                            r += data[idx];
                            g += data[idx + 1];
                            b += data[idx + 2];
                            a += data[idx + 3];
                            count++;
                        }
                    }
                    r = Math.round(r / count);
                    g = Math.round(g / count);
                    b = Math.round(b / count);
                    a = Math.round(a / count);
                    for (let dy = 0; dy < size && y + dy < height; dy++) {
                        for (let dx = 0; dx < size && x + dx < width; dx++) {
                            const idx = ((y + dy) * width + (x + dx)) * 4;
                            result.data[idx] = r;
                            result.data[idx + 1] = g;
                            result.data[idx + 2] = b;
                            result.data[idx + 3] = a;
                        }
                    }
                }
            }
            break;
        }
        case 'outline': {
            const color = effect.params.color || '#000000';
            const thickness = effect.params.thickness || 1;
            const or = parseInt(color.slice(1, 3), 16);
            const og = parseInt(color.slice(3, 5), 16);
            const ob = parseInt(color.slice(5, 7), 16);
            const { width, height } = imageData;
            for (let y = 0; y < height; y++) {
                for (let x = 0; x < width; x++) {
                    const idx = (y * width + x) * 4;
                    if (data[idx + 3] < 128) {
                        // Check neighbors for opaque pixels
                        let hasOpaqueNeighbor = false;
                        for (let dy = -thickness; dy <= thickness && !hasOpaqueNeighbor; dy++) {
                            for (let dx = -thickness; dx <= thickness && !hasOpaqueNeighbor; dx++) {
                                if (dx === 0 && dy === 0)
                                    continue;
                                const nx = x + dx, ny = y + dy;
                                if (nx >= 0 && nx < width && ny >= 0 && ny < height) {
                                    const nidx = (ny * width + nx) * 4;
                                    if (data[nidx + 3] >= 128)
                                        hasOpaqueNeighbor = true;
                                }
                            }
                        }
                        if (hasOpaqueNeighbor) {
                            result.data[idx] = or;
                            result.data[idx + 1] = og;
                            result.data[idx + 2] = ob;
                            result.data[idx + 3] = 255;
                        }
                    }
                }
            }
            break;
        }
        // Add more effects as needed
    }
    return result;
};
const applyEffects = (imageData, effects) => {
    return effects
        .filter(e => e.enabled)
        .reduce((img, effect) => applyEffect(img, effect), imageData);
};
/* ────────────────────────────────────────────────────────────────
   EXPORT UTILITIES
──────────────────────────────────────────────────────────────── */
const exportToTiledJson = (tileMap, tileSets) => {
    const tilesetData = tileSets.map((tile, index) => ({
        firstgid: index + 1,
        columns: 1,
        image: tile.thumbnail,
        imageheight: tile.height,
        imagewidth: tile.width,
        margin: 0,
        name: tile.name,
        spacing: 0,
        tilecount: 1,
        tileheight: tile.height,
        tilewidth: tile.width,
    }));
    const layerData = tileMap.layers.map(layer => {
        var _a;
        const data = [];
        for (let y = 0; y < tileMap.height; y++) {
            for (let x = 0; x < tileMap.width; x++) {
                const tileId = (_a = layer.tiles[y]) === null || _a === void 0 ? void 0 : _a[x];
                if (tileId) {
                    const tileIndex = tileSets.findIndex(t => t.id === tileId);
                    data.push(tileIndex >= 0 ? tileIndex + 1 : 0);
                }
                else {
                    data.push(0);
                }
            }
        }
        return {
            data,
            height: tileMap.height,
            id: 1,
            name: layer.name,
            opacity: layer.opacity,
            type: 'tilelayer',
            visible: layer.visible,
            width: tileMap.width,
            x: 0,
            y: 0,
        };
    });
    return {
        type: 'map',
        version: '1.10',
        tiledversion: '1.10.2',
        width: tileMap.width,
        height: tileMap.height,
        tilewidth: tileMap.tileWidth,
        tileheight: tileMap.tileHeight,
        layers: layerData,
        tilesets: tilesetData,
    };
};
const downloadJson = (data, filename) => {
    const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = filename;
    a.click();
    URL.revokeObjectURL(url);
};
const downloadPng = (canvas, filename) => {
    const url = canvas.toDataURL('image/png');
    const a = document.createElement('a');
    a.href = url;
    a.download = filename;
    a.click();
};
/* ────────────────────────────────────────────────────────────────
   PROJECT UTILITIES
──────────────────────────────────────────────────────────────── */
const createEmptyProject = (name) => ({
    id: generateId('project'),
    name,
    tileSets: [],
    tileMaps: [],
    spriteSheets: [],
    stateMachines: [],
    effectPresets: [],
    settings: { ...gameAssetTypes.DEFAULT_PROJECT_SETTINGS },
});
const serializeProject = (project) => {
    // Convert ImageData to base64 for serialization
    const serializable = {
        ...project,
        tileSets: project.tileSets.map(tile => ({
            ...tile,
            image: null, // Skip ImageData, use thumbnail
        })),
        spriteSheets: project.spriteSheets.map(sheet => ({
            ...sheet,
            image: null,
            imageData: null,
        })),
    };
    return JSON.stringify(serializable, null, 2);
};
const deserializeProject = async (json) => {
    const data = JSON.parse(json);
    // Restore ImageData from thumbnails
    for (const tile of data.tileSets) {
        if (tile.thumbnail) {
            tile.image = await dataUrlToImageData(tile.thumbnail);
        }
    }
    for (const sheet of data.spriteSheets) {
        if (sheet.dataUrl) {
            sheet.image = await loadImage(sheet.dataUrl);
            sheet.imageData = await dataUrlToImageData(sheet.dataUrl);
        }
    }
    return data;
};

exports.applyEffect = applyEffect;
exports.applyEffects = applyEffects;
exports.autoSliceSpriteSheet = autoSliceSpriteSheet;
exports.createEmptyProject = createEmptyProject;
exports.createEmptyTileLayer = createEmptyTileLayer;
exports.createEmptyTileMap = createEmptyTileMap;
exports.createSpriteAnimation = createSpriteAnimation;
exports.createTileFromImage = createTileFromImage;
exports.dataUrlToImageData = dataUrlToImageData;
exports.deserializeProject = deserializeProject;
exports.detectFrames = detectFrames;
exports.downloadJson = downloadJson;
exports.downloadPng = downloadPng;
exports.exportToTiledJson = exportToTiledJson;
exports.generateId = generateId;
exports.imageDataToDataUrl = imageDataToDataUrl;
exports.loadImage = loadImage;
exports.serializeProject = serializeProject;
//# sourceMappingURL=gameAssetUtils.js.map
