--[[----------------------------------------------------------------------------

ADOBE CONFIDENTIAL
------------------
Copyright 2015 Adobe Systems Incorporated
All Rights Reserved.

NOTICE: All information contained herein is, and remain the property of
Adobe Systems Incorporated and its suppliers, if any. The intellectual and
technical concepts contained herein are proprietary to Adobe Systems
Incorporated and its suppliers and may be covered by U.S. and Foreign
Patents, patents in process, and are protected by trade secret or copyright
law.  Dissemination of this information or reproduction of this material is
strictly forbidden unless prior written permission is obtained from
Adobe Systems Incorporated.

----------------------------------------------------------------------------]]--

-- WF.core.utils
local KSStringUtils = require 'KSStringUtils'
local KSTableUtils = require 'KSTableUtils'
local WFArguments = require 'WFArguments'

-- WF.core.rxlua
local KSData = require 'KSData'

-- WF.core.reactive
local KSReactive = require 'KSReactive'

local OzCatalogUploadStatus = require 'OzCatalogUploadStatus'

-- wichitafoundation.core.logging
local WFLogger = require 'WFLogger'
local WFFileUtils = require 'WFFileUtils'

local Rx = require 'Rx'
local WFRxRunUntilDone = require 'WFRxRunUntilDone'

local KSDate = require 'KSDate'
--==============================================================================
local WFCatUtils = require 'WFCatUtils'
local WFTableMerge = require 'WFTableMerge'
local WFArguments_check = assert( WFArguments.check )

local LrmAssetMetadataModel = {}

--------------------------------------------------------------------------------

local Model = {}

--------------------------------------------------------------------------------
local logger = WFLogger '_metadata'
 :setLevel 'debug'

function LrmAssetMetadataModel.makeModel( catalog )

    assert( catalog )
    logger:debugf('LrmAssetMetadataModel.makeModel')

    local resultB = KSReactive.variableB( nil )

    resultB:set('Created the new subModel')

    return {
        catalog = catalog,
        valueB = resultB,
        models = Model,
        events = {

            changeKeywords = function( self, assetId, list, delete)
                local newTable = {}

                if delete then
                    for i,v in pairs(list) do
                        newTable[v] = { _delete = true }
                    end
                else
                    for i,v in pairs(list) do
                        newTable[v] = true
                    end
                end

                local overlay = {
                    xmp = {
                        dc = {
                            subject = newTable
                        }
                    },
                    userUpdated = KSDate.timeInIso8601Format( nil, false )
                }

                catalog:docStore():applyOverlayToDocumentP {
                    docType = 'asset',
                    docId = assetId,
                    overlay = overlay,
                }
                :runUntilDone()
            end,

            updateProxyPath = function( self, docId, proxyFilePath)
                if (proxyFilePath and WFFileUtils.exists( proxyFilePath )) then
                    local overlay = {
                        _localOnly = {
                            proxy = {
                                filePath = proxyFilePath
                            }
                        }
                    }

                    catalog:docStore():applyOverlayToDocumentP {
                        docType = 'asset',
                        docId = docId,
                        overlay = overlay,
                    }
                    :runUntilDone()

                end

            end,

            UpdateInfoDetails = function(self, docId, copyright, caption, title)
                local overlay = {
                    xmp = {
                        dc = {
                            rights = copyright,
                            title = title,
                            description = caption
                        }
                    },
                    userUpdated = KSDate.timeInIso8601Format( nil, false )
                }

                catalog:docStore():applyOverlayToDocumentP {
                    docType = 'asset',
                    docId = docId,
                    overlay = overlay,
                }
                :runUntilDone()
            end,

            updateShareDetails  = function(self,albumId,allowDownloads, showMetadata,showLocation,flagsArray)
                catalog:indexStore():queryP( [[SELECT spaceId FROM space_album WHERE albumId == ?]], albumId )
                :mapDataP( function ( status, rows )
                    local spaceId = rows and rows[ 1 ] and rows[ 1 ][ 1 ]

                    if not flagsArray then
                        flagsArray = { _delete = true }
                    end

                    if spaceId then

                        local innerResultP = catalog:docStore():applyOverlayToDocumentP {
                            docType = 'space',
                            docIds = spaceId,
                            overlay = {
                                download = allowDownloads,
                                metadata= showMetadata,
                                location=showLocation,
                                filters = {
                                    flags = flagsArray
                                }
                            },
                        }

                        return innerResultP
                        :mapDataP( function ()
                            return 'data', true
                        end )
                    else
                        return 'error', 'AlbumNotShared'
                    end

                end )
                :runUntilDone()

            end,

            updatePresentationFilters  = function(self,albumId,isPresentationFilters)
                catalog:indexStore():queryP( [[SELECT spaceId FROM space_album WHERE albumId == ?]], albumId )
                :mapDataP( function ( status, rows )
                    local spaceId = rows and rows[ 1 ] and rows[ 1 ][ 1 ]



                    if spaceId then

                        local innerResultP = catalog:docStore():applyOverlayToDocumentP {
                            docType = 'space',
                            docIds = spaceId,
                            overlay = {
                                presentationFilters = isPresentationFilters
                            },
                        }

                        return innerResultP
                        :mapDataP( function ()
                            return 'data', true
                        end )
                    else
                        return 'error', 'AlbumNotShared'
                    end

                end )
                :runUntilDone()

            end,

            updateAlbumAuthorDetails = function( self, albumId, authorname)

                local overlay = {
                    story = {
                        author = authorname
                    }
                }

                catalog:docStore():applyOverlayToDocumentP {
                    docType = 'album',
                    docId = albumId,
                    overlay = overlay,
                }
                :runUntilDone()
            end,

            updateAlbumAppearanceDetails = function( self, albumId, appearance)
                local overlay = {
                    _id = albumId,
                    presentation = {
                        explore = {
                            version = 1,
                            themeId = appearance,
                        }
                    }
                }


                catalog:docStore():applyOverlay(overlay)
                :call(WFRxRunUntilDone)
            end,

            updateAlbumThemeDetails = function( self, albumId, theme)

                local overlay = {
                    _id = albumId,
                    presentation = {
                        explore = {
                            version = 1,
                            styleId = theme,
                        }
                    }
                }

                catalog:docStore():applyOverlay(overlay)
                :call(WFRxRunUntilDone)
            end,

            updateAlbumFilterPickedDetails = function( self, albumId, isPicked)

                local overlay = {
                    _id = albumId,
                    presentation = {
                        filters = {
                            pickedOnly = isPicked,
                        }
                    }
                }

                catalog:docStore():applyOverlay(overlay)
                :call(WFRxRunUntilDone)
            end,

            updateAlbumFilterRatingDetails = function( self, albumId, rating)

                local overlay = {
                    _id = albumId,
                    presentation = {
                        filters = {
                            ratingAtLeast = rating,
                        }
                    }
                }

                catalog:docStore():applyOverlay(overlay)
                :call(WFRxRunUntilDone)
            end,

            updateShowTitleDetails = function( self, albumId, shouldShowTitle)

                local overlay = {
                    _id = albumId,
                    presentation = {
                        options = {
                            showTitle = shouldShowTitle,
                        }
                    }
                }

                catalog:docStore():applyOverlay(overlay)
                :call(WFRxRunUntilDone)
            end,


            updateShowAuthorDetails = function( self, albumId, shouldShowAuthor)

                local overlay = {
                    _id = albumId,
                    presentation = {
                        options = {
                            showAuthor = shouldShowAuthor,
                        }
                    }
                }

                catalog:docStore():applyOverlay(overlay)
                :call(WFRxRunUntilDone)
            end,

			saveRenditions = function(self, assetId, newRevId, thumbnail, preview)

				local previewMD5 = preview:digest( 'MD5' )
				local thumbnailMD5 = thumbnail:digest( 'MD5' )

				local WFOzRenditionServerUtils = require 'WFOzRenditionServerUtils'
				thumbnailMD5 = WFOzRenditionServerUtils.getFullMD5HashedKey( thumbnailMD5, assetId )
				previewMD5 = WFOzRenditionServerUtils.getFullMD5HashedKey( previewMD5, assetId )

				logger:debugf('saveRenditions, reached the saveRenditions')

				return self.catalog:getDataCache()
					:take ( 1 )
					:flatMap( function( dataCache )

						logger:debugf('saveRenditions, Got the dataCache, %s', (dataCache ~= nil))
						return Rx.Observable.combineLatest (dataCache:put( previewMD5, preview ), dataCache:put( thumbnailMD5, thumbnail ))

					end )

					:flatMap( function()

						local epochNow = self.catalog:getCacheEpochB():valueNow()

						local previewDoc = {
							md5 = previewMD5,
							revId = newRevId,
							epoch = epochNow,
							size = KSData.length( preview ),
						}

						local thumbnail2xDoc = {
							md5 = thumbnailMD5,
							revId = newRevId,
							epoch = epochNow,
							size = KSData.length( thumbnail ),
						}

						local localOnly = {}

						localOnly[ 'thumbnail2x' ] = thumbnail2xDoc
						localOnly[ 'preview' ] = previewDoc

						local overlay = {
							_id = assetId,
							type = 'asset',
							_localOnly = localOnly,
						}

						logger:debugf('saveRenditions, overlay : %s', overlay)

						return self.catalog:docStore():applyOverlay( overlay )

							:map( function(res)

								logger:debugf('saveRenditions, done saving the overlay : %s', res)

								return res

							end)
					end)
					:call ( WFRxRunUntilDone )
			end,

			savePreviewRendition = function(self, assetId, preview)

				local previewMD5 = preview:digest( 'MD5' )

				local WFOzRenditionServerUtils = require 'WFOzRenditionServerUtils'
				previewMD5 = WFOzRenditionServerUtils.getFullMD5HashedKey( previewMD5, assetId )

				return self.catalog:getDataCache()
					:take(1)
					:flatMap( function(dataCache)
					return dataCache:put(previewMD5, preview)
					end )

					:flatMap( function()

						local epochNow = self.catalog:getCacheEpochB():valueNow()

						local previewDoc = {
							md5 = previewMD5,
							epoch = epochNow,
							size = KSData.length( preview ),
						}

						local localOnly = {}

						localOnly[ 'preview' ] = previewDoc

						local overlay = {
							_id = assetId,
							_localOnly = localOnly,
						}

						logger:debugf('savePreviewRendition, overlay : %s', overlay)

						return self.catalog:docStore():applyOverlay( overlay )

							   :tap( function(res)
									logger:debugf('savePreviewRendition, done saving the overlay : %s', res)
								end)
					end)
					:call ( WFRxRunUntilDone )
			end,

            updateShareAppearanceAndThemeDetails = function( self, albumId, appearance,style)



                if not appearance then
                    appearance = { _delete = true }
                end

                if not style then
                    style = { _delete = true }
                end

                logger:debugf('appearance is %s and style is %s' , appearance , style)

                local overlay = {

                    presentation = {
                        explore = {
                            version = 1,
                            themeId = appearance,
                            styleId = style,
                        }

                    }
                }


                catalog:docStore():applyOverlayToDocumentP {
                    docType = 'album',
                    docId = albumId,
                    overlay = overlay,
                }
                :runUntilDone()
            end,

            deletePeopleData = function(self, docId, regionId , personId)
                local mwgrs = "mwg-rs"
                local adobefi = "adobe-fi"
                local rejected = "Rejected"
                local overlay = {

                    xmp = {
                        [ mwgrs ] = {
                            Regions = {
                                RegionList = {
                                    [ regionId ] = {
                                        Extensions = {
                                            [ adobefi ] = {
                                                People = {
                                                    [personId] = {
                                                        [rejected]  =  true,
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                }


                catalog:docStore():applyOverlayToDocumentP {
                    docType = 'asset',
                    docId = docId,
                    overlay = overlay,
                }
                :runUntilDone()
            end,

            updatePreset = function(self, docId, assetType, newName, relPath, sha256, localPath, digest)
                local size = WFFileUtils.fileAttributes(localPath).fileSize

                local baseDocValue = WFCatUtils.createBaseOverlayWithUserUpdatedField( docId )
                local overlay = {
                    name = newName,
                    importSource = {
                        sha256 = sha256,
                        fileSize = size,
                    },
                    _localOnly = {
                        files = {
                            presetProfile = {
                                path = relPath,
                                sha256 = sha256,
                                uploadStatus = { _delete = true },
                            }
                        }
                    }
                }

				if digest then
					overlay.digest = digest
				end

                WFTableMerge.applyOverlay( overlay, baseDocValue )

                catalog:docStore():applyOverlay ( overlay )
                :call ( WFRxRunUntilDone )

            end,

            createPreset = function(self, docId, type, subType, name, relativefilePath, sha256, presetPath, digest)

                local captureDate = KSDate.timeInIso8601Format( nil, false )

                local PRESET_CONTENT_TYPE = "application/rdf+xml"
                local size = WFFileUtils.fileAttributes(presetPath).fileSize

                local baseDocValue = WFCatUtils.createBaseOverlayWithUserCreatedFields( docId )

                local overlay = {
                    type = type,
                    subtype = subType,
                    name = name,
                    if_none_match = sha256,
                    captureDate = captureDate,
                    -- group = nil, --groupName, -- currently not exposed. TODO agk: needs to work on it.
                    importSource = {
                        fileSize = size,
                        sha256 = sha256,
                        contentType = PRESET_CONTENT_TYPE
                    },
                    _localOnly = {
                        files = {
                            presetProfile = {
                                sha256 = sha256,
                                path = relativefilePath,
                            }
                        }
                    }
                }

				if digest then
					overlay.digest = digest
				end

                WFTableMerge.applyOverlay( overlay, baseDocValue )

                catalog:docStore():applyOverlay ( overlay )
                :call ( WFRxRunUntilDone )

            end,

			createCameraProfile = function(self, docId, type, subType, name, fileName, model, cameraProfileDigest, resolvedDigest, relativefilePath, sha256, profilePath)

				local function deleteLocalProfile()

					WFFileUtils.deleteFile( OzCatalogUploadStatus.checkAndCorrectLocalPath( profilePath ) )

				end

				local function saveProfile()

					local PROFILE_CONTENT_TYPE = "application/octet-stream"
					local size = WFFileUtils.fileAttributes(profilePath).fileSize

					local baseDocValue = WFCatUtils.createBaseOverlayWithUserCreatedFields( docId )

					local overlay = {
						type = type,
						subtype = subType,
						name = name,
						fileName = fileName,
						model = model,
						version = 1,
						cameraProfileDigest = cameraProfileDigest,
						resolvedDigest = resolvedDigest,
						fileSize = size,
						importSource = {
							fileSize = size,
							sha256 = sha256,
							contentType = PROFILE_CONTENT_TYPE
						},
						_localOnly = {
							files = {
								original = {
									master = {
										sha256 = sha256,
										path = profilePath,
										relativePath = relativefilePath,
									}
								},
								dcpProfile = {
									sha256 = sha256
								}
							}
						}
					}

					WFTableMerge.applyOverlay( overlay, baseDocValue )

					catalog:docStore():applyOverlay ( overlay )
					:call ( WFRxRunUntilDone )

				end

				local catalog = assert( self.catalog )
				local sql = {
					sql = [[ SELECT docId FROM CustomCameraProfiles WHERE digest = ? AND modelName = ? AND filename = ? ]],
					singleColumnQuery = true,
					cameraProfileDigest, KSStringUtils.upper(model), fileName,
				}

				catalog:indexStore()
				:queryOnce( sql )
				:map(function(docIds)
					if #docIds == 0 then
						saveProfile()
					else
						-- Deleting the profile which is recently created by ACR and is no more needed as have an entry in our db
						deleteLocalProfile()
					end
				end)
				:call ( WFRxRunUntilDone )
			end,

            deletePreset = function(self, docId)
                local overlay = {
                    _id =  docId,
                    _deleted = true
                }
                catalog:docStore():applyOverlay ( overlay )
                :call ( WFRxRunUntilDone )
            end,

            changeFavoriteStatus = function(self, fingerprint, isProfile, isFavorite, isDefaultFavorite )

                logger:debugf('reached changeFavStatus')

                local section = isProfile and "profiles" or "presets"
                local kDeleteToken = { _delete = true }
                local KSTableUtils = require 'KSTableUtils'
                local derefOpt = assert( KSTableUtils.derefOpt )

                local FAV_SLURP_MARKER_DOC_ID = "favoritesSlurpDone"

                local function isSlurpDone( localSlurpDocValue )
                    return localSlurpDocValue and localSlurpDocValue[ FAV_SLURP_MARKER_DOC_ID ] == true
                end

                catalog:docStore():localDocValue( FAV_SLURP_MARKER_DOC_ID )
                :flatMapLatest( function( docValue )

                    if (not isSlurpDone(docValue)) then
                        -- If the slurp was done once, then return
                        logger:debugf('not slurp done')
                        return Rx.Observable.empty()
                    end

                    return catalog._wfCatalogStream
                    :flatMap( function( wfCatalog )
                        local catalogId = wfCatalog:ozCatalogSpec():catalogId()
                        logger:debugf('we got the catalogId:%s', catalogId)
                        return catalog:docStore():docValue( catalogId )
                        :flatMap( function( catalogDocValue )

                            logger:debugf('catalog.profiles:%s', catalogDocValue.profiles)

                            local favList = derefOpt( catalogDocValue, section, "favorites" ) or {}
                            local favToken = ( isFavorite == isDefaultFavorite )
                                    and kDeleteToken -- The style is favorite and is favorite by default or vice versa
                                    or isFavorite -- Favorite status doesn't match with default status

                            favList[ fingerprint ] = favToken

                            local docOverlay = {
                                _id = catalogId,

                                [ section ] = {
                                    favorites = favList,
                                }
                            }

                            logger:debugf('docOverlay:%s', docOverlay)

                            return catalog:docStore():applyOverlay( docOverlay )

                        end )
                    end)

                end)
                :call ( WFRxRunUntilDone )

            end,

            downloadDcps = function(self, name)
                catalog:getBinaryDownloader():startDownloadingDcps(name)
            end,

            stopDownloadingDcps = function(self)
                catalog:getBinaryDownloader():stopDownloadingDcps()
            end,

        }
    }

end

--------------------------------------------------------------------------------

function Model:presetProfileFavoriteStream( docId, keyPaths, separator)
    local catalog = assert( self.catalog )
    local docStore = assert( catalog:docStore() )
    local FAV_SLURP_MARKER_DOC_ID = "favoritesSlurpDone"
    local WFRxScheduler = require 'WFRxScheduler'

    local function distinctTableCompare( _, tbl1, _, tbl2 )
        return KSTableUtils.deepEquals( tbl1, tbl2 )
    end

    local sep = separator or "/"
    return {
        value = docStore:ongoingLocalDocValue( FAV_SLURP_MARKER_DOC_ID )
        :filter( function ( localSlurpDocValue )
            logger:debugf('filtering the results:%s', localSlurpDocValue)
            return localSlurpDocValue and localSlurpDocValue[ FAV_SLURP_MARKER_DOC_ID ] == true
        end)
        :take ( 1 )
        :flatMapLatest(function( _ )

            logger:debugf('after filter map:%s', _)
            return docStore:ongoingDocValue( docId )

            :map( function( docValue )

                local trimmedDocs = {}

                local doc = {
                    state = true
                }

                for _, value in ipairs( keyPaths ) do

                    local infoPath = KSStringUtils.split( value, sep )
                    local v = KSTableUtils.derefOpt( docValue, unpack( infoPath ) )
                    KSTableUtils.setNested( doc, v, unpack( infoPath ) )

                end

                trimmedDocs[ docValue._id ] = doc

                return trimmedDocs

            end)

        end)
        :calm( 5, WFRxScheduler.sharedScheduler() )
        :distinctUntilChanged( nil, distinctTableCompare )
    }
end

--------------------------------------------------------------------------------

function Model:doesSharedSpaceExist(albumId)
    local catalog = assert( self.catalog )
    local sql = {
        sql =  [[SELECT spaceId FROM space_album WHERE albumId == ?]], albumId
    }

    logger:debugf("SQL IS %s" , sql)
    local queryResult = catalog:indexStore()
    :query( sql )

    return {
        value = queryResult
    }


end

--------------------------------------------------------------------------------

function Model:isCustomProfile( digest, cameraModelName )
	WFArguments_check( "table", "+string", '+string' )

	local catalog = assert( self.catalog )

    local sql = {
        sql =  [[SELECT 1 FROM CustomCameraProfiles
					WHERE digest = ? AND modelName = ? LIMIT 1]],
		digest,
		KSStringUtils.upper( cameraModelName ),
		quiesceUntilUpToDate = true,
    }

    local result = catalog:indexStore()
    	:queryOnce( sql )
		:map( function( rows )

			return rows and #rows > 0
		end )

    return {
        value = result
    }


end

--------------------------------------------------------------------------------

return LrmAssetMetadataModel
