// output image class: one image to one layer
// 	* path:
// 	* image layer
// 	* viewer()
// 	* select()

// * init image class: has three layers
// 	* path :
// 	* init image group layer
// 	* init image layer
// 	* background layer

// * mask class:
// 	* path
// 	* mask group
// 	* white mask
// 	* balck layer
// 	* select()
// 	* viewe()
const general = require('./utility/general')
const Enum = require('./enum')
const psapi = require('./psapi')
const layer_util = require('./utility/layer')
const thumbnail = require('./thumbnail')
const ViewerObjState = {
    Delete: 'delete',
    Unlink: 'unlink',
}

class ViewerImage {
    constructor() {
        this.img_html = null //
        this.thumbnail_container = null
        this.is_highlighted = false
        this.can_highlight = true
        this.is_active = false // active is a temporary highlight , the yellow/orang highlight
        this.state = ViewerObjState['Unlink']

        // true will delete the layers from the layer stacks when the session ends,
        // false mean use this.state to determine whither you delete the layer or not
        this.autoDelete = false
        this.viewerManager = null // store link to the viewer manager of this document
        this.viewerObjectType
        this.objectId
    }
    info() {
        console.log('state: ', this.state)
    }
    visible(visibleOn) {}
    select() {}
    isActive() {
        return this.is_active
    }
    active(isActive) {
        if (isActive) {
            //unlink it if it's active
            // this.state = ViewerObjState['Unlink']

            this.img_html.classList.add('viewerImgActive')
        } else {
            if (this.getHighlight() === false) {
                // if it's not active and it's not highlighted
                // this.state = ViewerObjState['Delete']
            } else {
                // this.state = ViewerObjState['Unlink'] //it's not active but it's highlighted then keep it
            }
            this.img_html.classList.remove('viewerImgActive')
        }
        this.is_active = isActive
    }
    setAutoDelete(auto_delete) {
        this.autoDelete = auto_delete
    }

    isSameLayer(layer_id) {}
    setHighlight(is_highlighted) {
        if (this.can_highlight) {
            this.is_highlighted = is_highlighted
            if (this.is_highlighted) {
                // this.state = ViewerObjState['Unlink']
                this.img_html.classList.add('viewerImgSelected')
            } else {
                this.img_html.classList.remove('viewerImgSelected')
                // this.state = ViewerObjState["Delete"]
            }
        }
    }
    getHighlight() {
        return this.is_highlighted
    }
    toggleHighlight() {
        const toggle_value = !this.getHighlight()
        this.setHighlight(toggle_value)
        // this.is_highlighted = !this.is_highlighted
        // this.img_html.classList.toggle("viewerImgSelected")
    }
    setImgHtml() {}

    async delete() {
        try {
            if (this.img_html) {
                this.img_html.remove() //delete the img html element
            }
            if (this.thumbnail_container) {
                this.thumbnail_container.remove()
            }

            //1) it's output layer // can_highlight && this.getHighlight()
            //2) it init or mask relate layers // this.autoDelete
            //3) it output layer that been used as init layer // !can_highlight && !this.autoDelete

            // do 1) and 2) here . test for 3) in InitImage

            //1)
            if (this.can_highlight && (this.getHighlight() || this.is_active)) {
                //keep if can be highlighted and either is highlighted or active
                this.state = ViewerObjState['Unlink']
            } else {
                //
                this.state = ViewerObjState['Delete']
            }

            if (this.autoDelete) {
                //remove if it's first automated layer
                this.state = ViewerObjState['Delete']
            }

            // else {
            //   //discard only if it's
            //   if (this.autoDelete){
            //     this.state = ViewerObjState['Delete']
            //   }
            // }
        } catch (e) {
            console.warn(e)
        }
    }

    unlink() {
        //keep the layer but unlink it from the ui
        try {
            this.img_html.remove() //delete the img html element
        } catch (e) {
            console.warn(e)
        }
    }

    createThumbnailNew(img, _) {
        this.img_html = img
        this.thumbnail_container = thumbnail.Thumbnail.wrapImgInContainer(
            img,
            'viewer-image-container'
        )
        thumbnail.Thumbnail.addSPButtonToContainer(
            this.thumbnail_container,
            'svg_sp_btn',
            'Use this as an initial image',

            this.useOutputImageAsInitImage,
            img
        )
    }
    useOutputImageAsInitImage = async () => {
        //set init image event listener, use when settion is active
        // const layer = await app.activeDocument.activeLayers[0]
        try {
            // console.log('this.img_html:', this.img_html)
            // await executeAsModal(() => {
            //     this.visible(true)
            // })
            // await this.select(true) //select() does take arguments
            // this.active(true)
            await executeAsModal(async () => {
                await this.click(Enum.clickTypeEnum['Click'])
            })

            const layer = layer_util.Layer.doesLayerExist(this.layer)
                ? this.layer
                : await app.activeDocument.activeLayers[0]

            // const layer = this.layer
            const image_info = await psapi.silentSetInitImage(
                layer,
                random_session_id
            )
            const image_name = image_info['name']
            const path = `./server/python_server/init_images/${image_name}`
            g_viewer_manager.addInitImageLayers(layer, path, false)
            await g_viewer_manager.loadInitImageViewerObject(path)
        } catch (e) {
            console.warn(e)
        }
    }
    createThumbnail(img, b_button_visible = true) {
        this.img_html = img
        // Create new container element
        this.thumbnail_container = document.createElement('div')

        this.thumbnail_container.className = 'viewer-image-container'

        const elem = document.getElementById('svg_sp_btn')

        // Create a copy of it
        const clone = elem.cloneNode(true)
        const button = clone
        button.style.display = null
        button.setAttribute(
            'title',
            'use this image to generate more variance like it'
        )

        // Create button element
        // const button = document.createElement('sp-button');
        button.className = 'viewer-image-button'
        // button.innerHTML = "Button";
        if (!b_button_visible) {
            button.style.display = 'none'
        }
        button.addEventListener('click', async () => {
            await useOutputImageAsInitImage()
        })

        // Append elements to container
        this.thumbnail_container.appendChild(this.img_html)
        this.thumbnail_container.appendChild(button)

        // this.img_html = container
    }
}

class OutputImage extends ViewerImage {
    constructor(layer, path, viewer_manager) {
        super()
        this.layer = layer
        this.path = path
        this.img_html = null
        this.viewerManager = viewer_manager
        this.viewerObjectType = Enum.ViewerObjectTypeEnum['OutputImage']
        this.objectId = path // the path is unique, so we will use it as an id
    }
    async click(click_type) {
        console.log('click_type: ', click_type)
        // if (this.isActive() && click_type === Enum.clickTypeEnum['Click']) {
        //     //convert consecutive clicks to AltClick
        //     click_type = Enum.clickTypeEnum['SecondClick']
        //     console.log('converted click_type: ', click_type)
        // }

        if (click_type === Enum.clickTypeEnum['Click']) {
            //select layer
            //turn the layer visible
            //set the layer to active
            this.visible(true)
            await this.select(true) //select() does take arguments
            this.active(true)
        } else if (click_type === Enum.clickTypeEnum['ShiftClick']) {
            this.visible(true)
            await this.select(true) //select() does take arguments
            this.setHighlight(true)
            this.active(true)
            // if (this.viewerManager.last_selected_viewer_obj) {
            //     //if the last selected layer is valid then converted last selected layer into highlight layer
            //     this.viewerManager.last_selected_viewer_obj.setHighlight(true)
            // }
        } else if (click_type === Enum.clickTypeEnum['AltClick']) {
            // this.viewerManager.last_selected_viewer_obj = null
            this.setHighlight(false)
            this.visible(false)
            this.active(false)

            await psapi.unselectActiveLayersExe() //Note:can we move to ViewerManager.click()
        } else if (click_type === Enum.clickTypeEnum['SecondClick']) {
            //select layer
            //turn the layer visible
            //set the layer to active
            this.visible(false)
            await this.select(false) //select() does take arguments
            this.active(false)
        }
        this.viewerManager.replaceLastSelection(click_type, this) //pass the click_type and this object
    }
    visible(visibleOn) {
        //turn the visibility for the layer
        try {
            super.visible(visibleOn)
            if (layer_util.Layer.doesLayerExist(this.layer)) {
                this.layer.visible = visibleOn
            }
        } catch (e) {
            console.warn(e)
        }
    }
    async select() {
        //select the layer
        super.select()
        if (layer_util.Layer.doesLayerExist(this.layer)) {
            await psapi.selectLayersExe([this.layer])
            //   console.log(`${this.layer.id} got selected`);
        }
    }

    isSameLayer(layer_id) {
        super.isSameLayer(layer_id)
        const is_same = this.layer.id == layer_id
        return is_same
    }
    isSameObject(object) {
        if (
            layer_util.Layer.doesLayerExist(this.layer) &&
            layer_util.Layer.doesLayerExist(object.layer)
        ) {
            return this.layer.id === object.layer.id
        }
        return false
    }

    setImgHtml(img_html) {
        super.setImgHtml()
        this.img_html = img_html
    }
    async delete() {
        try {
            await super.delete()
            // this.img_html.remove()//delete the img html element
            if (this.state === ViewerObjState['Delete']) {
                await psapi.cleanLayers([this.layer])
            }
        } catch (e) {
            console.warn(e)
        }
    }
    info() {
        super.info()
    }
    // unlink(){
    //   //keep the layer but unlink it from the ui
    //   try{

    //     super.unlink()
    //     this.img_html.remove()//delete the img html element

    //   }catch(e){
    //     console.warn(e)
    //   }
    // }
}

class InitImage extends ViewerImage {
    constructor(init_group, init_snapshot, solid_layer, path, viewer_manager) {
        super()
        this.init_group = init_group
        this.init_snapshot = init_snapshot
        this.solid_layer = solid_layer

        this.path = path
        this.can_highlight = false
        this.viewerManager = viewer_manager
        this.viewerObjectType = Enum.ViewerObjectTypeEnum['InitImage']
        this.objectId = path // the path is unique, so we will use it as an id
        // if (this.autoDelete === false){
        //   this.state = ViewerObjState['Unlink']
        // }
    }
    async click(click_type) {
        if (click_type === Enum.clickTypeEnum['Click']) {
            //select layer
            //turn the layer visible
            //set the layer to active
            this.visible(true)
            await this.select(true) //select() does take arguments
            this.active(true)
            click_type = Enum.clickTypeEnum['Click'] // convert all click to Click
            this.viewerManager.replaceLastSelection(click_type, this) //pass the click_type and this object
        } else if (click_type === Enum.clickTypeEnum['ShiftClick']) {
            this.visible(true)
            await this.select(true) //select() does take arguments
            this.active(true)
            // if (this.viewerManager.last_selected_viewer_obj) {
            //     //if the last selected layer is valid then converted last selected layer into highlight layer
            //     this.viewerManager.last_selected_viewer_obj.setHighlight(true)
            // }
            click_type = Enum.clickTypeEnum['Click'] // convert all click to Click
            this.viewerManager.replaceLastSelection(click_type, this) //pass the click_type and this object
        }
        // else if (click_type === Enum.clickTypeEnum['AltClick']) {
        //     // this.viewerManager.last_selected_viewer_obj = null
        //     this.setHighlight(false)
        //     this.visible(false)
        //     this.active(false)

        //     await psapi.unselectActiveLayersExe() //Note:can we move to ViewerManager.click()
        // }
        // this.viewerManager.replaceLastSelection(click_type, this) //pass the click_type and this object
    }
    visible(visibleOn) {
        try {
            super.visible(visibleOn)

            let visibleValues = []
            if (visibleOn) {
                visibleValues = [true, true, true]
            } else {
                visibleValues = [false, false, false]
            }

            if (layer_util.Layer.doesLayerExist(this.init_group)) {
                this.init_group.visible = visibleValues[0]
            }
            if (layer_util.Layer.doesLayerExist(this.init_snapshot)) {
                this.init_snapshot.visible = visibleValues[1]
            }

            if (layer_util.Layer.doesLayerExist(this.solid_layer)) {
                this.solid_layer.visible = visibleValues[2]
            }

            if (!this.autoDelete) {
                //means it's not the first init image

                if (layer_util.Layer.doesLayerExist(this.solid_layer)) {
                    this.solid_layer.visible = false //turn it off sense the init group is above the output group, and the white solid will hide the init image reference located in the output group
                }
            }
        } catch (e) {
            console.warn(e)
        }
    }

    async select() {
        super.select()

        const selectLayers = []
        if (layer_util.Layer.doesLayerExist(this.init_group)) {
            selectLayers.push(this.init_group)
        }

        await psapi.selectLayersExe(selectLayers)
        //   console.log(`${this.layer.id} got selected`);
    }

    isSameLayer(layer_id) {
        super.isSameLayer(layer_id)
        let is_same = false
        if (layer_util.Layer.doesLayerExist(this.init_group)) {
            is_same = this.init_group.id == layer_id
        }
        return is_same
    }
    setImgHtml(img_html) {
        super.setImgHtml()
        this.img_html = img_html
    }
    async delete() {
        try {
            await super.delete()

            // this.img_html.remove()//delete the img html element

            if (!this.autoDelete && !this.can_highlight) {
                //don't delete since it's output layer that is been used as init image
                this.state = ViewerObjState['Unlink']
            }

            if (this.state === ViewerObjState['Delete']) {
                await psapi.cleanLayers([
                    this.init_group,
                    this.init_snapshot,
                    this.solid_layer,
                ])
            }
        } catch (e) {
            console.warn(e)
        }
    }
}

class InitMaskImage extends ViewerImage {
    constructor(mask_group, white_mark, solid_black, path, viewer_manager) {
        super()
        this.mask_group = mask_group
        this.white_mark = white_mark
        this.solid_black = solid_black

        this.path = path
        this.can_highlight = false
        this.viewerManager = viewer_manager
        this.viewerObjectType = Enum.ViewerObjectTypeEnum['MaskImage']
        this.objectId = path // the path is unique, so we will use it as an id
    }
    async click(click_type) {
        if (click_type === Enum.clickTypeEnum['Click']) {
            //select layer
            //turn the layer visible
            //set the layer to active
            this.visible(true)
            await this.select(true) //select() does take arguments
            this.active(true)
            click_type = Enum.clickTypeEnum['Click'] // convert all click to Click
            this.viewerManager.replaceLastSelection(click_type, this) //pass the click_type and this object
        } else if (click_type === Enum.clickTypeEnum['ShiftClick']) {
            this.visible(true)
            await this.select(true) //select() does take arguments
            this.active(true)
            // if (this.viewerManager.last_selected_viewer_obj) {
            //     //if the last selected layer is valid then converted last selected layer into highlight layer
            //     this.viewerManager.last_selected_viewer_obj.setHighlight(true)
            // }
            click_type = Enum.clickTypeEnum['Click'] // convert all click to Click
            this.viewerManager.replaceLastSelection(click_type, this) //pass the click_type and this object
        }
    }
    visible(visibleOn) {
        try {
            super.visible(visibleOn)

            let visibleValues = []
            if (visibleOn) {
                visibleValues = [true, true, false]
            } else {
                visibleValues = [false, false, false]
            }

            if (layer_util.Layer.doesLayerExist(this.mask_group)) {
                this.mask_group.visible = visibleValues[0]
            }
            if (layer_util.Layer.doesLayerExist(this.white_mark)) {
                this.white_mark.visible = visibleValues[1]
            }
            if (layer_util.Layer.doesLayerExist(this.solid_black)) {
                this.solid_black.visible = visibleValues[2]
            }
        } catch (e) {
            console.warn(e)
        }
    }

    async select() {
        super.select()

        const selectLayers = []

        if (layer_util.Layer.doesLayerExist(this.white_mark)) {
            selectLayers.push(this.white_mark)
        }

        await psapi.selectLayersExe(selectLayers)
        //   console.log(`${this.layer.id} got selected`);
    }

    isSameLayer(layer_id) {
        super.isSameLayer(layer_id)
        let is_same = false
        if (layer_util.Layer.doesLayerExist(this.mask_group)) {
            is_same = this.mask_group.id == layer_id
        }
        return is_same
    }
    setImgHtml(img_html) {
        super.setImgHtml()
        this.img_html = img_html
    }
    async delete() {
        try {
            await super.delete()
            // this.img_html.remove()//delete the img html element
            if (this.state === ViewerObjState['Delete']) {
                await psapi.cleanLayers([
                    this.mask_group,
                    this.white_mark,
                    this.solid_black,
                ])
            }
        } catch (e) {
            console.warn(e)
        }
    }
    createThumbnailNew(img, _) {
        this.img_html = img
        this.thumbnail_container = thumbnail.Thumbnail.wrapImgInContainer(
            img,
            'viewer-image-container'
        )
        thumbnail.Thumbnail.addSPButtonToContainer(
            this.thumbnail_container,
            'svg_sp_btn',
            'update the mask',
            setMaskViewer,
            img
        )
    }
}

class initImageLayers {
    //store info about the init image related layers
    constructor(group, snapshot, solid_background, autoDelete) {
        this.group = group
        this.snapshot = snapshot
        this.solid_background = solid_background
        this.autoDelete = autoDelete
    }
}
class maskLayers {
    //store info about the init image related layers
    constructor(group, white_mark, solid_background, autoDelete) {
        this.group = group
        this.white_mark = white_mark
        this.solid_background = solid_background
        this.autoDelete = autoDelete
    }
}
class ViewerManager {
    //viewer manager will reset after the end of the session
    //it will store
    constructor() {
        this.outputImages = []
        this.initImages = []
        this.initMaskImage
        this.pathToViewerImage = {} // quick way to check if an link image path on disk to ViewerImage object.
        this.initImageLayersJson = {} //{path: initImageLayers}

        this.selectedOutputImages = {} //store the selected output images {path: outputImage}

        this.mask_layer
        this.maskLayersJson = {} //{path: MaskLayers}

        //Note:move initGroup, to GenerationSession
        this.initGroup
        this.init_solid_background
        this.maskGroup
        this.mask_solid_background

        //last_selected_obj
        this.last_selected_viewer_obj
        this.thumbnail_scaler = 1
        this.isSquareThumbnail = false
        this.init_image_container = document.getElementById(
            'divInitImageViewerContainer'
        )
    }

    replaceLastSelection(click_type, clicked_object) {
        if (
            this.last_selected_viewer_obj && // is valid last selected object
            this.last_selected_viewer_obj.objectId !== clicked_object.objectId // not the same object

            // clicked_object instanceof OutputImage &&
            // !clicked_object.isSameObject(this.last_selected_viewer_obj)
        ) {
            //if the current selection and the last selection are different

            this.last_selected_viewer_obj.visible(false)
            this.last_selected_viewer_obj.active(false)
        }
        if (click_type === Enum.clickTypeEnum['Click']) {
            this.last_selected_viewer_obj = clicked_object
        } else if (click_type === Enum.clickTypeEnum['ShiftClick']) {
            if (this.last_selected_viewer_obj) {
                //if the last selected layer is valid then converted last selected layer into highlight layer
                this.last_selected_viewer_obj.setHighlight(true)
            }
            this.last_selected_viewer_obj = clicked_object
        } else if (click_type === Enum.clickTypeEnum['AltClick']) {
            this.last_selected_viewer_obj = null
        } else if (click_type === Enum.clickTypeEnum['SecondClick']) {
            this.last_selected_viewer_obj = null
        }
    }
    initializeInitImage(group, snapshot, solid_background, path) {
        console.warn('this method is deprecated, use the session.js method ')
        this.initGroup = group
        this.init_solid_background = solid_background
        this.addInitImageLayers(snapshot, path, true)
    }
    initializeMask(group, white_mark, solid_background, path, base64) {
        this.maskGroup = group
        this.mask_solid_background = solid_background
        this.addMaskLayers(white_mark, path, true, base64)
    }
    addMaskLayers(white_mark, path, auto_delete, base64) {
        try {
            if (!this.maskLayersJson.hasOwnProperty(path)) {
                //it's a new mask, mostly for the first time storing the mask

                const mask_layers = new maskLayers(
                    this.maskGroup,
                    white_mark,
                    this.mask_solid_background,
                    auto_delete
                )
                this.maskLayersJson[path] = mask_layers
            } else {
                //for updating the mask

                //just update the html
                const new_path = `${path}?t=${new Date().getTime()}`
                console.log('new mask path: ', new_path)
                // this.maskLayersJson[path].img_html.src = new_path

                // this.pathToViewerImage[path].img_html.src = new_path
                this.pathToViewerImage[path].img_html.src = base64ToSrc(base64)
            }
        } catch (e) {
            console.warn(e)
        }
    }
    updateMaskLayer() {}
    addInitImageLayers(snapshot, path, auto_delete) {
        try {
            if (!this.initImageLayersJson.hasOwnProperty(path)) {
                //if this is a new init image
                //store it all of layers in a container object
                const init_image_layers = new initImageLayers(
                    this.initGroup,
                    snapshot,
                    this.init_solid_background,
                    auto_delete
                )
                this.initImageLayersJson[path] = init_image_layers
            }
        } catch (e) {
            console.warn(e)
        }
    }

    hasViewerImage(path) {
        if (this.pathToViewerImage.hasOwnProperty(path)) {
            return true
        }
        return false
    }
    addOutputImage(layer, path) {
        const outputImage = new OutputImage(layer, path, this)

        this.outputImages.push(outputImage)
        this.pathToViewerImage[path] = outputImage //
        return outputImage
    }
    addInitImage(group, snapshot, solid_background, path, auto_delete) {
        const initImage = new InitImage(
            group,
            snapshot,
            solid_background,
            path,
            this
        )
        initImage.setAutoDelete(auto_delete)
        this.initImages.push(initImage)
        this.pathToViewerImage[path] = initImage
        return initImage
    }
    addMask(group, white_mark, solid_background, path) {
        const mask = new InitMaskImage(
            group,
            white_mark,
            solid_background,
            path,
            this
        )

        this.initMaskImage = mask
        this.pathToViewerImage[path] = mask
        return mask
    }

    scaleThumbnails(
        original_width,
        original_height,
        min_width,
        min_height,
        scaler
    ) {
        //calculate the new width and height

        const image_width = this.isSquareThumbnail
            ? 100
            : g_generation_session.last_settings.width
        const image_height = this.isSquareThumbnail
            ? 100
            : g_generation_session.last_settings.height

        const [new_width, new_height] = general.scaleToClosestKeepRatio(
            image_width,
            image_height,
            100,
            100
        )
        const [scaled_width, scaled_height] = [
            new_width * scaler,
            new_height * scaler,
        ]

        for (let outputImage of this.outputImages) {
            //get the image and it's container
            const img = outputImage.img_html
            const img_container = img.parentElement

            img_container.style.width = scaled_width
            img_container.style.height = scaled_height
            img.style.width = scaled_width
            img.style.height = scaled_height
            //scale them to the new dimensions
        }
    }
    onSessionEnd() {
        this.outputImages = []
        this.initImages = []
        this.initMaskImage = null

        this.pathToViewerImage = {} // quick way to check if an link image path on disk to ViewerImage object.
        this.initImageLayersJson = {} //{path: initImageLayers}

        this.selectedOutputImages = {} //store the selected output images {path: outputImage}

        this.mask_layer = null
        this.maskLayersJson = {} //{path: MaskLayers}

        //Note:move initGroup, to GenerationSession
        this.initGroup = null
        this.init_solid_background = null
        this.maskGroup = null
        this.mask_solid_background = null

        //last_selected_obj
        this.last_selected_viewer_obj = null
        // this.thumbnail_scaler = 1
        // this.isSquareThumbnail = false
    }

    async loadInitImageViewerObject(path) {
        if (!g_viewer_manager.hasViewerImage(path)) {
            const group = this.initImageLayersJson[path].group
            const snapshot = this.initImageLayersJson[path].snapshot
            const solid_background =
                this.initImageLayersJson[path].solid_background
            const auto_delete = this.initImageLayersJson[path].autoDelete
            const base64_image = g_generation_session.base64initImages[path]
            await loadInitImageViewerObject(
                group,
                snapshot,
                solid_background,
                path,
                auto_delete,
                base64_image
            )
        }
    }
    deleteAll() {}
    keepAll() {}
    keepSelected() {}
    deleteSelected() {}
    deleteInitImages() {}
    deleteMask() {}
}

module.exports = {
    OutputImage,
    InitImage,
    InitMaskImage,
    ViewerObjState,
    ViewerManager,
}
