// Extract Internal Layer IDs from Photoshop Document
// This script extracts the actual internal IDs that Adobe's API uses
// to identify text layers in a PSD file.
#target photoshop

// Function to extract text layers with their internal IDs
function extractTextLayersWithInternalIDs() {
    if (!app.documents.length) {
        return {
            error: "No document open"
        };
    }
    
    var doc = app.activeDocument;
    var result = {
        documentName: doc.name,
        psdPath: doc.fullName.fsName,
        extractedAt: new Date().toString(),
        dimensions: {
            width: doc.width.as('px'),
            height: doc.height.as('px')
        },
        textLayerCount: 0,
        textLayers: []
    };
    
    // Utility function to traverse layers and extract info
    function traverseLayers(layers, path) {
        path = path || "";
        for (var i = 0; i < layers.length; i++) {
            var layer = layers[i];
            
            if (layer.typename === "LayerSet") {
                // This is a layer group, traverse its children
                var groupPath = path ? path + "/" + layer.name : layer.name;
                traverseLayers(layer.layers, groupPath);
            } else if (layer.kind === LayerKind.TEXT) {
                // This is a text layer, extract its information
                extractTextLayerInfo(layer, path);
            }
        }
    }
    
    function extractTextLayerInfo(layer, path) {
        try {
            var layerPath = path ? path + "/" + layer.name : layer.name;
            
            // Get the layer's ID directly from the layer object
            var directID = layer.id;
            
            // Get the layer's internal ID using ActionManager
            var internalID = getInternalLayerID(layer);
            
            // Extract text content
            var text = layer.textItem.contents;
            
            // Extract basic style information
            var styleInfo = {
                font: layer.textItem.font,
                size: layer.textItem.size.value,
                color: null, // Will be populated if available
                alignment: getTextAlignment(layer)
            };
            
            // Try to extract text color
            try {
                if (layer.textItem.color.rgb) {
                    styleInfo.color = [
                        layer.textItem.color.rgb.red,
                        layer.textItem.color.rgb.green,
                        layer.textItem.color.rgb.blue
                    ];
                }
            } catch (e) {
                // Color not defined or error accessing it
            }
            
            // Extract detailed formatting info if available
            var richTextInfo = extractRichTextInfo(layer);
            if (richTextInfo && richTextInfo.styles && richTextInfo.styles.length > 0) {
                styleInfo.styles = richTextInfo.styles;
                var hasRichTextFormatting = richTextInfo.styles.length > 1;
            } else {
                styleInfo.styles = [];
                var hasRichTextFormatting = false;
            }
            
            // Create layer info object with both direct and internal IDs
            var layerInfo = {
                id: directID, // The ID we normally use
                internalID: internalID, // The ID Adobe API might use
                name: layer.name,
                path: layerPath,
                text: text,
                visible: layer.visible,
                styleInfo: styleInfo,
                hasRichTextFormatting: hasRichTextFormatting
            };
            
            // Add to results
            result.textLayers.push(layerInfo);
            result.textLayerCount++;
            
        } catch (e) {
            // Log error but continue processing other layers
            $.writeln("Error processing layer '" + layer.name + "': " + e.message);
        }
    }
    
    // Function to get internal layer ID using ActionManager
    function getInternalLayerID(layer) {
        try {
            // Select the layer
            var idslct = charIDToTypeID("slct");
            var desc = new ActionDescriptor();
            var ref = new ActionReference();
            ref.putIdentifier(charIDToTypeID("Lyr "), layer.id);
            desc.putReference(charIDToTypeID("null"), ref);
            executeAction(idslct, desc, DialogModes.NO);
            
            // Get the layer's item index
            var ref = new ActionReference();
            ref.putProperty(charIDToTypeID("Prpr"), charIDToTypeID("ItmI"));
            ref.putEnumerated(charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));
            var itemIndexDesc = executeActionGet(ref);
            var itemIndex = itemIndexDesc.getInteger(charIDToTypeID("ItmI"));
            
            // Get the layer's ID
            var ref = new ActionReference();
            ref.putProperty(charIDToTypeID("Prpr"), stringIDToTypeID("layerID"));
            ref.putEnumerated(charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));
            var layerDesc = executeActionGet(ref);
            
            // Check if the layer has a layerID property
            if (layerDesc.hasKey(stringIDToTypeID("layerID"))) {
                return layerDesc.getInteger(stringIDToTypeID("layerID"));
            } else {
                // Fallback to another method of ID extraction
                return layer.id; // Use direct ID as fallback
            }
        } catch (e) {
            $.writeln("Error getting internal ID for layer '" + layer.name + "': " + e.message);
            return layer.id; // Use direct ID if we can't get the internal ID
        }
    }
    
    // Function to get text alignment
    function getTextAlignment(layer) {
        try {
            var align = layer.textItem.justification;
            if (align === Justification.LEFT) {
                return "left";
            } else if (align === Justification.CENTER) {
                return "center";
            } else if (align === Justification.RIGHT) {
                return "right";
            } else {
                return "left"; // Default
            }
        } catch (e) {
            return "left"; // Default if error
        }
    }
    
    // Function to extract rich text information
    function extractRichTextInfo(layer) {
        try {
            var styles = [];
            
            // Get a reference to the layer
            var ref = new ActionReference();
            ref.putIdentifier(charIDToTypeID("Lyr "), layer.id);
            var desc = executeActionGet(ref);
            
            // Check if textKey exists
            if (desc.hasKey(stringIDToTypeID("textKey"))) {
                var textKey = desc.getObjectValue(stringIDToTypeID("textKey"));
                
                // Check if textStyleRange exists
                if (textKey.hasKey(stringIDToTypeID("textStyleRange"))) {
                    var styleRanges = textKey.getList(stringIDToTypeID("textStyleRange"));
                    
                    // Iterate through each style range
                    for (var i = 0; i < styleRanges.count; i++) {
                        var range = styleRanges.getObjectValue(i);
                        var from = range.getInteger(stringIDToTypeID("from"));
                        var to = range.getInteger(stringIDToTypeID("to"));
                        var styleRef = range.getObjectValue(stringIDToTypeID("textStyle"));
                        
                        var rangeText = layer.textItem.contents.substring(from, to);
                        var fontName = layer.textItem.font;
                        var fontSize = layer.textItem.size.value;
                        var fontStyle = "Regular";
                        var fontColor = null;
                        
                        // Extract font name
                        try {
                            if (styleRef.hasKey(stringIDToTypeID("fontName"))) {
                                fontName = styleRef.getString(stringIDToTypeID("fontName"));
                            }
                        } catch (e) {}
                        
                        // Extract font style
                        try {
                            if (styleRef.hasKey(stringIDToTypeID("fontStyleName"))) {
                                fontStyle = styleRef.getString(stringIDToTypeID("fontStyleName"));
                            }
                        } catch (e) {}
                        
                        // Extract font size
                        try {
                            if (styleRef.hasKey(stringIDToTypeID("size"))) {
                                fontSize = styleRef.getUnitDoubleValue(stringIDToTypeID("size"));
                            }
                        } catch (e) {}
                        
                        // Extract color
                        try {
                            if (styleRef.hasKey(stringIDToTypeID("color"))) {
                                var colorObj = styleRef.getObjectValue(stringIDToTypeID("color"));
                                var colorValues = colorObj.getObjectValue(stringIDToTypeID("color"));
                                
                                // Check color model
                                if (colorValues.hasKey(stringIDToTypeID("red"))) {
                                    fontColor = [
                                        Math.round(colorValues.getDouble(stringIDToTypeID("red"))),
                                        Math.round(colorValues.getDouble(stringIDToTypeID("green"))),
                                        Math.round(colorValues.getDouble(stringIDToTypeID("blue")))
                                    ];
                                }
                            }
                        } catch (e) {}
                        
                        // Add style info to the array
                        styles.push({
                            start: from,
                            end: to,
                            text: rangeText,
                            font: fontName,
                            style: fontStyle,
                            size: fontSize,
                            color: fontColor
                        });
                    }
                }
            }
            
            return { styles: styles };
        } catch (e) {
            $.writeln("Error extracting rich text info: " + e.message);
            return { styles: [] };
        }
    }
    
    // Start the layer traversal
    traverseLayers(doc.layers);
    
    return result;
}

// Main function
function main() {
    try {
        var result = extractTextLayersWithInternalIDs();
        
        // Convert result to formatted JSON
        var jsonString = JSON.stringify(result, null, 2);
        
        // Save the result to a file
        var docPath = app.activeDocument.path;
        var fileName = app.activeDocument.name.replace(/\.[^\.]+$/, '') + "_internal_ids.json";
        var outputFile = new File(docPath + "/" + fileName);
        
        if (outputFile.open("w")) {
            outputFile.write(jsonString);
            outputFile.close();
            alert("Layer ID information saved to:\n" + outputFile.fsName);
        } else {
            alert("Error saving output file");
        }
        
        // Also return the JSON string
        return jsonString;
    } catch (e) {
        alert("Error: " + e.message);
        return JSON.stringify({ error: e.message });
    }
}

// Run the script
main();