File size: 27,745 Bytes
34097e9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
const psapi = require('../psapi')
// const sdapi = require('../sdapi_py_re')
const layer_util = require('../utility/layer')
const general = require('./general')
const Jimp = require('../jimp/browser/lib/jimp.min')

const { executeAsModal } = require('photoshop').core
const batchPlay = require('photoshop').action.batchPlay
const formats = require('uxp').storage.formats
const storage = require('uxp').storage
const fs = storage.localFileSystem

async function isBlackAndWhiteImage(base64EncodedImage) {
    try {
        // Load your base64 encoded image
        const image = await Jimp.read(Buffer.from(base64EncodedImage, 'base64'))
        console.log(
            'isBlackAndWhiteImage(): image.bitmap.width, image.bitmap.height: ',
            image.bitmap.width,
            image.bitmap.height
        )
        // Check if your image only has one channel
        return (
            image.bitmap.width === image.bitmap.height &&
            image.bitmap.width === 1
        )
    } catch (e) {
        console.warn(e)
    }
}

async function convertBlackAndWhiteImageToRGBChannels(base64EncodedImage) {
    // Load your base64 encoded image
    const image = await Jimp.read(Buffer.from(base64EncodedImage, 'base64'))

    // Convert your black and white image to RGB channels
    image.color([
        { apply: 'red', params: [255] },
        { apply: 'green', params: [255] },
        { apply: 'blue', params: [255] },
    ])

    // Get your base64 encoded black and white image with RGB channels
    const base64EncodedImageWithRGBChannels = await image.getBase64Async(
        Jimp.MIME_JPEG
    )

    return base64EncodedImageWithRGBChannels
}
async function convertBlackAndWhiteImageToRGBChannels2(base64EncodedImage) {
    try {
        // Load your base64 encoded image
        const image = await Jimp.read(Buffer.from(base64EncodedImage, 'base64'))

        // Convert your black and white image to RGB channels
        image.color([{ apply: 'mix', params: ['#ffffff', 100] }])

        // Get your base64 encoded black and white image with RGB channels
        const base64EncodedImageWithRGBChannels = await image.getBase64Async(
            Jimp.MIME_JPEG
        )

        return base64EncodedImageWithRGBChannels
    } catch (e) {
        console.warn(e)
    }
}
async function convertBlackAndWhiteImageToRGBChannels3(base64EncodedImage) {
    try {
        // Load your base64 encoded image
        const image = await Jimp.read(Buffer.from(base64EncodedImage, 'base64'))

        // Convert your black and white image to RGB channels
        // image.color([{ apply: 'mix', params: ['#ffffff', 100] }])

        // Get your base64 encoded black and white image with RGB channels
        const base64EncodedImageWithRGBChannels = await image.getBase64Async(
            Jimp.MIME_PNG
        )

        return base64EncodedImageWithRGBChannels
    } catch (e) {
        console.warn(e)
    }
}

async function snapShotLayer() {
    //snapshot layer with no mask
    let command = [
        // Select All Layers current layer
        {
            _obj: 'selectAllLayers',
            _target: [
                { _enum: 'ordinal', _ref: 'layer', _value: 'targetEnum' },
            ],
        },
        // Duplicate current layer
        // {"ID":[459,460,461,462,463,464,465,466,467,468,469,470,471,472,473,474,475,476,477,478,479,480,481,482,483,484,485,486,487,488,489,490,491,492,493,494,495,496,497,498,499,500,501,502,503,504,505,506,507,508,509,510,511,512,513],"_obj":"duplicate","_target":[{"_enum":"ordinal","_ref":"layer","_value":"targetEnum"}],"version":5},
        {
            // ID: ids,
            _obj: 'duplicate',
            _target: [
                { _enum: 'ordinal', _ref: 'layer', _value: 'targetEnum' },
            ],
            // version: 5
        },

        // Merge Layers
        { _obj: 'mergeLayersNew' },
        // Make
        {
            _obj: 'make',
            at: { _enum: 'channel', _ref: 'channel', _value: 'mask' },
            new: { _class: 'channel' },
            using: { _enum: 'userMaskEnabled', _value: 'revealSelection' },
        },
        // Set Selection
        {
            _obj: 'set',
            _target: [{ _property: 'selection', _ref: 'channel' }],
            to: { _enum: 'ordinal', _ref: 'channel', _value: 'targetEnum' },
        },
        //make a group
        {
            _obj: 'make',
            _target: [
                {
                    _ref: 'layerSection',
                },
            ],
            from: {
                _ref: 'layer',
                _enum: 'ordinal',
                _value: 'targetEnum',
            },
            layerSectionStart: 512,
            layerSectionEnd: 513,
            name: 'Group 2',
            _options: {
                dialogOptions: 'dontDisplay',
            },
        },
        {
            _obj: 'mergeLayersNew',
            _options: {
                dialogOptions: 'dontDisplay',
            },
        },
    ]

    const result = await batchPlay(command, {
        synchronousExecution: true,
        modalBehavior: 'execute',
    })

    return result
}

async function snapShotLayerExe() {
    await executeAsModal(async () => {
        //create a fill layer above the background layer, so that it's present in the snapshot
        try {
            const selectionInfo = await psapi.getSelectionInfoExe()

            // const backgroundLayer = await app.activeDocument.backgroundLayer

            await psapi.createSolidLayer(255, 255, 255)
            const solid_layer = await app.activeDocument.activeLayers[0]
            // await psapi.unSelectMarqueeExe()//unselect the

            // await solid_layer.moveAbove(backgroundLayer)
            // await snapShotLayer() //create a layer with only the opaque pixels
            // await psapi.reSelectMarqueeExe(selectionInfo)
            // await solid_layer.delete()
        } catch (e) {
            console.warn(e)
        }
    })
    await executeAsModal(async () => {
        //create a fill layer above the background layer, so that it's present in the snapshot
        try {
            const solid_layer = await app.activeDocument.activeLayers[0]
            const backgroundLayer = await app.activeDocument.backgroundLayer
            await solid_layer.moveAbove(backgroundLayer)
            await psapi.unselectActiveLayersExe()
            await snapShotLayer() //create a layer with only the opaque pixels
            // await psapi.reSelectMarqueeExe(selectionInfo)
            // await psapi.unSelectMarqueeExe()//unselect the
            await solid_layer.delete()
        } catch (e) {
            console.warn(e)
        }
    })
}

class IO {
    // constructor() {}
    static async exportWebp(layer, export_width, export_height) {
        await executeAsModal(async () => {
            //we assume we have a valid layer rectangular image/layer, no transparency
            const doc_entry = await getCurrentDocFolder() //get the main document folder before we switch doc
            const layer_info = await layer_util.Layer.getLayerInfo(layer)
            //*) create a new document
            const new_doc = await IOHelper.createDocumentExe(
                export_width,
                export_height
            )
            const new_layer = await layer_util.Layer.duplicateToDoc(
                layer,
                new_doc
            )
            //*) resize the layer to the same dimension as the document

            await layer_util.Layer.scaleTo(
                new_layer,
                new_doc.width,
                new_doc.height
            ) //
            await layer_util.Layer.moveTo(new_layer, 0, 0) //move to the top left corner
            //
            await IOHelper.saveAsWebpExe(doc_entry) //save current document as .webp file, save it into doc_entry folder
            await new_doc.closeWithoutSaving()
        })
    }
    static async exportPng() {}
    static async exportDoc() {}
    static async exportLayer() {}

    static async base64PngToPngFile(
        base64_png,
        folder,
        image_name = 'temp_base64Png.png'
    ) {
        const arrayBuffer = _base64ToArrayBuffer(base64_png)

        // const folder = await storage.localFileSystem.getTemporaryFolder()

        const file = await folder.createFile(image_name, { overwrite: true })

        await file.write(arrayBuffer, { format: storage.formats.binary })
        return file
    }
    static async openImageFileAsDocument(file_entry) {
        const new_doc = await app.open(file_entry)
        return new_doc
    }
    static async base64PngToBase64Webp(base64_png) {
        let base64_webp
        try {
            await executeAsModal(async () => {
                try {
                    const main_doc_entry = await getCurrentDocFolder()
                    //save the base64_png to .png file
                    const temp_folder = await fs.getTemporaryFolder()
                    const png_file = await this.base64PngToPngFile(
                        base64_png,
                        temp_folder
                    )

                    //load the .png file as a layer in new document
                    const new_doc = await this.openImageFileAsDocument(png_file)
                    //save document as .webp
                    const [_, webp_file] = await IOHelper.saveAsWebpExe(
                        main_doc_entry
                    ) //save current document as .webp file, save it into doc_entry folder
                    await new_doc.closeWithoutSaving()
                    //load/read the .webp file as an arraybuffer
                    const ArrayBufferWebp = await webp_file.read({
                        format: formats.binary,
                    })

                    //convert the arraybuffer to base64Webp string

                    base64_webp = _arrayBufferToBase64(ArrayBufferWebp)
                } catch (e) {
                    console.warn(e)
                }
            })
            return base64_webp
        } catch (e) {
            console.warn(e)
        }
    }
    static async base64WebpFromFile(file_entry) {
        //file_entry most be .webp
        let webp_base64
        try {
            await executeAsModal(async () => {
                const arrayBuffer = await file_entry.read({
                    format: formats.binary,
                })
                console.log('webp arrayBuffer:', arrayBuffer)

                const base64_image = _arrayBufferToBase64(arrayBuffer) //convert the buffer to base64
                console.log('base64_image:', base64_image)
                webp_base64 = base64_image
            })
            return [webp_base64, webp_arrayBuffer]
        } catch (e) {
            console.warn(e)
        }
    }

    static async base64ToLayer(
        base64_png,
        image_name = 'base64_to_layer.png',
        to_x = 0,
        to_y = 0,
        width = 512,
        height = 512,
        format = 'png'
    ) {
        let layer
        if (format === 'png') {
            layer = await IOBase64ToLayer.base64PngToLayer(
                base64_png,
                image_name
            )

            psapi.setVisibleExe(layer, true)
            await layer_util.Layer.scaleTo(layer, width, height) //
            await layer_util.Layer.moveTo(layer, to_x, to_y) //move to the top left corner
            psapi.setVisibleExe(layer, true)
        }
        return layer
    }

    static async getSelectionFromCanvasAsBase64Silent(
        selectionInfo,
        b_resize = false,
        resize_width = 0,
        resize_height = 0
    ) {
        //it will save the document then crop it so that only the selection area are left.
        //return arrayBuffer or base64Png?
        try {
            let file
            const folder = await fs.getTemporaryFolder()
            await executeAsModal(
                async () => {
                    const canvas_image_name = 'canvas_image.png'
                    file = await folder.createFile(canvas_image_name, {
                        overwrite: true,
                    })

                    const currentDocument = app.activeDocument
                    await currentDocument.saveAs.png(file, null, true)
                    //save file end

                    //read the saved image.png
                },

                { commandName: 'readPng' }
            )

            const arrayBuffer = await file.read({
                format: formats.binary,
            })

            // const selectionInfo = g_generation_session.selectionInfo
            // const selectionInfo = await psapi.getSelectionInfoExe()
            const cropped_base64_url = await IOHelper.cropPng(
                arrayBuffer,
                selectionInfo,
                true,
                resize_width,
                resize_height
            )
            const cropped_base64 = general.base64UrlToBase64(cropped_base64_url)

            // html_manip.setInitImageSrc(cropped_base64_url)
            return cropped_base64
        } catch (e) {
            console.warn(e)
        }
    }
    static async getSelectionFromCanvasAsBase64NonSilent(
        layer,
        image_name,
        width,
        height
    ) {
        try {
            const image_buffer = await psapi.newExportPng(
                layer,
                image_name,
                width,
                height
            )

            const base64_image = _arrayBufferToBase64(image_buffer) //convert the buffer to base64
            //send the base64 to the server to save the file in the desired directory
            // await sdapi.requestSavePng(base64_image, image_name)
            // await saveFileInSubFolder(base64_image, document_name, image_name)
            // debugger
            const { requestSavePng } = require('../sdapi_py_re')
            await requestSavePng(base64_image, image_name)
            return base64_image
        } catch (e) {
            console.warn(e)
        }
    }
    static async getSelectionFromCanvasAsBase64Interface(
        width,
        height,
        layer,
        selectionInfo,
        resize = true,
        use_silent_mode = true,
        image_name = 'temp.png'
    ) {
        let base64_image
        if (use_silent_mode) {
            base64_image = await this.getSelectionFromCanvasAsBase64Silent(
                selectionInfo,
                resize,
                width,
                height
            )
        } else {
            base64_image = await this.getSelectionFromCanvasAsBase64NonSilent(
                layer,
                image_name,
                width,
                height
            )
        }
        return base64_image
    }
    static async getSelectionFromCanvasAsBase64Interface_New(
        width,
        height,
        selectionInfo,
        resize = true,
        image_name = 'temp.png'
    ) {
        //use this version, it has less parameters
        const use_silent_mode = html_manip.getUseSilentMode()
        let layer = null
        if (!use_silent_mode) {
            await psapi.snapshot_layerExe()
            const snapshotLayer = await app.activeDocument.activeLayers[0]
            layer = snapshotLayer
        }
        let base64_image
        if (use_silent_mode) {
            base64_image = await this.getSelectionFromCanvasAsBase64Silent(
                selectionInfo,
                resize,
                width,
                height
            )
        } else {
            base64_image = await this.getSelectionFromCanvasAsBase64NonSilent(
                layer,
                image_name,
                width,
                height
            )
        }
        await layer_util.deleteLayers([layer]) //delete the snapshot layer if it exists
        return base64_image
    }

    static async urlToLayer(image_url, image_file_name = 'image_from_url.png') {
        try {
            await psapi.unselectActiveLayersExe()
            const temp_entry = await fs.getTemporaryFolder()
            await downloadItExe(image_url, temp_entry, image_file_name)
        } catch (e) {
            console.warn('urlToLayer()', image_url, image_file_name, e)
        }
    }
}

class IOHelper {
    static async saveAsWebp(doc_entry) {
        //doc_entry must be in dataFolder or tempFolder
        //save document as webp
        const document_id = app.activeDocument.id

        // doc_entry = await getCurrentDocFolder()
        const file_entry = await doc_entry.createFile('temp.webp', {
            overwrite: true,
        })

        const token = await fs.createSessionToken(file_entry)
        const result = await batchPlay(
            [
                {
                    _obj: 'save',
                    as: {
                        _obj: 'WebPFormat',
                        compression: {
                            _enum: 'WebPCompression',
                            _value: 'compressionLossless',
                        },
                        includeXMPData: false,
                        includeEXIFData: false,
                        includePsExtras: false,
                    },
                    in: {
                        _path: token,
                        _kind: 'local',
                    },
                    documentID: 59,
                    copy: true,
                    lowerCase: true,
                    saveStage: {
                        _enum: 'saveStageType',
                        _value: 'saveBegin',
                    },
                    _options: {
                        dialogOptions: 'dontDisplay',
                    },
                },
            ],
            {
                synchronousExecution: true,
                modalBehavior: 'execute',
            }
        )

        return [result, file_entry]
    }

    static async saveAsWebpExe(doc_entry) {
        let result
        let file_entry
        await executeAsModal(async () => {
            ;[result, file_entry] = await this.saveAsWebp(doc_entry)
        })
        return [result, file_entry]
    }
    static async createDocumentExe(width, height) {
        let new_doc
        try {
            await executeAsModal(async () => {
                new_doc = await app.documents.add({
                    width: width,
                    height: height,
                    resolution: await app.activeDocument.resolution,
                    mode: 'RGBColorMode',
                    fill: 'transparent',
                })
            })
        } catch (e) {
            console.warn(e)
        }
        return new_doc
    }
    static async cropPng(
        arrayBuffer,
        selectionInfo,
        b_resize = false,
        resize_width = 0,
        resize_height = 0
    ) {
        //crop png from array buffer
        //have the option to resize the after cropping

        const crop_x = selectionInfo.left
        const crop_y = selectionInfo.top
        const crop_w = selectionInfo.width
        const crop_h = selectionInfo.height
        const base64_url_result = await Jimp.read(arrayBuffer)
            .then(async (img) => {
                let cropped_img = await img.crop(crop_x, crop_y, crop_w, crop_h)

                let resized_img
                if (b_resize) {
                    resized_img = await cropped_img.resize(
                        resize_width,
                        resize_height
                    )
                } else {
                    resized_img = cropped_img
                }

                const base64_url = await resized_img.getBase64Async(
                    Jimp.MIME_PNG
                )

                // console.log('jimp: base64_url: ', base64_url)
                // document.getElementById("image").setAttribute("src", data);

                return base64_url
            })
            .catch((error) => {
                console.error(error)
            })
        return base64_url_result
    }
}

class IOBase64ToLayer {
    static {}
    static async base64PngToLayer(base64_png, image_name) {
        //unselect all layers so that the imported layer get place at the top of the document
        await psapi.unselectActiveLayersExe()

        const imported_layer = await base64ToFile(base64_png, image_name) //silent import into the document

        return imported_layer
    }
}
class IOFolder {
    static {}
    static async createSettingsFolder() {
        //create a folder named "Settings" in the DataFolder
        let settings_entry
        await executeAsModal(async () => {
            settings_entry = await this.createFolderSafe('Settings')
        })
        return settings_entry
    }
    static async findOrCreateFolderExe(folder_name) {
        //create a folder named "Settings" in the DataFolder
        let folder_entry
        await executeAsModal(async () => {
            folder_entry = await this.createFolderSafe(folder_name)
        })
        return folder_entry
    }

    static async doesFolderExist(folder_name) {
        //check if folder exist. return true if it does. false if it doesn't.
        const data_folder = await fs.getDataFolder()
        let b_exist = false
        let folder
        try {
            folder = await data_folder.getEntry(folder_name)
            b_exist = true
        } catch (e) {
            // console.warn(e)
            b_exist = false
        }
        return b_exist
    }

    static async createFolderSafe(folder_name) {
        //will always return a folder. it will create the folder if it doesn't exist.
        try {
            // const uuid = await getUniqueDocumentId()
            const data_folder = await fs.getDataFolder()

            let folder_entry
            try {
                folder_entry = await data_folder.getEntry(folder_name)
            } catch (e) {
                console.warn(e)
                //create document folder
                folder_entry = await data_folder.createFolder(folder_name)
            }

            return folder_entry
        } catch (e) {
            console.warn(e)
        }
    }

    static async getDocumentFolderNativePath() {
        try {
            const uuid = await getUniqueDocumentId()

            let doc_folder = await this.getDocFolder(uuid)
            const path = general.fixNativePath(doc_folder.nativePath)
            return path
        } catch (e) {
            console.warn(e)
        }
        return ''
    }

    static async getDocFolder(doc_uuid) {
        //will create folder if does not exist. always return a folder entry
        const doc_entry = await getDocFolder(doc_uuid)
        return doc_entry
    }
    static async getSettingsFolder() {
        //will create folder if does not exist. always return a folder entry
        const settings_entry = await this.createSettingsFolder()
        return settings_entry
    }
    static async getPresetFolder() {
        //will create folder if does not exist. always return a folder entry
        const preset_entry = await this.findOrCreateFolderExe('Preset')
        return preset_entry
    }
    static async getCustomPresetFolder(
        custom_preset_folder_name = 'custom_preset'
    ) {
        //will create folder if does not exist. always return a folder entry
        const preset_entry = await this.findOrCreateFolderExe(
            custom_preset_folder_name
        )
        return preset_entry
    }
    static async createFolderIfDoesNotExist(folder_name) {
        try {
            await executeAsModal(async () => {
                try {
                    const folder = await fs.getDataFolder()
                    const sub_folder = await folder.createFolder(folder_name)
                } catch (e) {
                    console.warn(e)
                }
            })
        } catch (e) {
            console.warn(e)
        }
    }
}

class IOLog {
    static {}
    static async saveLogToFile(json, file_name) {
        try {
            const plugin_folder = await fs.getDataFolder()
            const file = await plugin_folder.createFile(file_name, {
                type: storage.types.file,
                overwrite: true,
            })

            const JSONInPrettyFormat = JSON.stringify(json, undefined, 4)
            await file.write(JSONInPrettyFormat, {
                format: storage.formats.utf8,
                append: true,
            })
        } catch (e) {
            console.warn(e)
        }
    }
}
class IOJson {
    static {}
    static async saveJsonToFile(json, folder_entry, file_name) {
        try {
            const file = await folder_entry.createFile(file_name, {
                type: storage.types.file,
                overwrite: true,
            })

            const JSONInPrettyFormat = JSON.stringify(json, undefined, 4)
            await file.write(JSONInPrettyFormat, {
                format: storage.formats.utf8,
                append: false,
            })
        } catch (e) {
            console.warn(e)
        }
    }

    static async saveJsonToFileExe(json, folder_entry, file_name) {
        await executeAsModal(async () => {
            await this.saveJsonToFile(json, folder_entry, file_name)
        })
    }
    static async loadJsonFromFile(folder_entry, file_name) {
        const json_file_name = file_name

        try {
            const json_entry = await folder_entry.getEntry(json_file_name)
            if (json_entry) {
                const json = JSON.parse(
                    await json_entry.read({
                        format: storage.formats.utf8,
                    })
                )
                return json
            }
        } catch (e) {
            console.warn(e)
        }
    }

    static async saveSettingsToFile(settings_json, settings_file_name) {
        await executeAsModal(async () => {
            // debugger
            const folder_entry = await IOFolder.getSettingsFolder('Settings')
            await this.saveJsonToFile(
                settings_json,
                folder_entry,
                settings_file_name
            )
        })
    }
    static async loadSettingsFromFile(settings_file_name) {
        const folder_entry = await IOFolder.getSettingsFolder('Settings')
        const settings_json = await this.loadJsonFromFile(
            folder_entry,
            settings_file_name
        )
        return settings_json
    }
    static async saveHordeSettingsToFile(settings_json) {
        const settings_file_name = 'horde_settings.json'
        await this.saveSettingsToFile(settings_json, settings_file_name)
    }
    static async loadHordeSettingsFromFile() {
        const settings_file_name = 'horde_settings.json'
        const settings_json = await this.loadSettingsFromFile(
            settings_file_name
        )
        return settings_json
    }

    static async getJsonEntries(doc_entry) {
        let entries = await doc_entry.getEntries()
        const json_entries = entries.filter(
            (e) => e.isFile && e.name.toLowerCase().includes('.json') // must be a file and has the of the type .json
        )
        console.log('json_entries: ', json_entries)
        // .forEach((e) => console.log(e.name))
        return json_entries
    }
    static async deleteFile(doc_entry, file_name) {
        try {
            const file_entry = await doc_entry.getEntry(file_name)
            file_entry.delete()
        } catch (e) {}
    }
}

module.exports = {
    IO,
    snapShotLayerExe,
    IOHelper,
    IOJson,
    IOFolder,
    IOLog,
    convertBlackAndWhiteImageToRGBChannels,
    convertBlackAndWhiteImageToRGBChannels2,
    convertBlackAndWhiteImageToRGBChannels3,
    isBlackAndWhiteImage,
}