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

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.

This files contains utilities for the OzDocStoreMigratorTest.

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

local KSPathUtils = require "KSPathUtils"
local WFFileUtils = require "WFFileUtils"

	-- WF.core.table
local WFTableArchiving = require 'WFTableArchiving'

local tableToString = assert( WFTableArchiving.tableToString )

local OzDocStoreMigratorUtils = {}

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

local valueFnEnv = {} -- disallow all function calls

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

 function OzDocStoreMigratorUtils.contentFromFlattenedValue( value )

	-- Lightweight sandboxing. The one potentially malicious piece of code
	-- somebody could inject (and they'd have to do it directly into the
	-- SQLite database) would be to put an infinite loop here.
	-- Some more potentially valuable approaches we could take are
	-- described in http://lua-users.org/wiki/SandBoxes.

	local valueFn = loadstring( 'return ' .. value )
	assert( type( valueFn ) == 'function' )
	setfenv( valueFn, valueFnEnv ) -- disallow all function calls

	return valueFn()

end

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

function OzDocStoreMigratorUtils.getWritableDocStorePath( tempDir, name )
	local dbPath = KSPathUtils.child( tempDir, name .. '.ozdocstore' )

	return dbPath
end

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

function OzDocStoreMigratorUtils.getTestDataDocStorePath( name, externalPath )
	local dbPath
	
	if externalPath then
		dbPath = externalPath
	else
		dbPath = KSPathUtils.getStandardFilePath( 'testdata' )
		dbPath = KSPathUtils.child( dbPath, 'old-oz-docstore-migrator' )
	end
		
	dbPath = KSPathUtils.child( dbPath, name .. '.ozdocstore' )
	
	return dbPath
end

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


function OzDocStoreMigratorUtils.createTempDir ( name )
	local tempDir = KSPathUtils.getStandardFilePath 'temp'
	if not name then
		name = 'OzDocStoreMigration'
	end
	tempDir = KSPathUtils.child( tempDir, name )

	pcall( WFFileUtils.mkdir, tempDir ) -- ignore failure (could already exists)

	return tempDir

end

function OzDocStoreMigratorUtils.copyDocStoreToWritableLocation( source, target )

	if not WFFileUtils.exists( source ) then
		error( "test docStore does not exist", 4 )
	end

	-- Make a copy of the source DB.

	WFFileUtils.copy( source, target )

	-- And copy its WAL buddies if they're around, too!

	local function copyWalBuddyFileIfExists( extension )

		local sourceBuddyPath = source .. extension
		local destBuddyPath = target .. extension

		if WFFileUtils.exists( sourceBuddyPath ) then
			WFFileUtils.copy( sourceBuddyPath, destBuddyPath )
		end

	end

	copyWalBuddyFileIfExists( '-wal' )
	copyWalBuddyFileIfExists( '-shm' )

end

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

function OzDocStoreMigratorUtils.setupTestDataDocStoreForMigration( tempDir, name, externalPath )
	local testDataDocStorePath = OzDocStoreMigratorUtils.getTestDataDocStorePath( name, externalPath )
	local docStorePath = OzDocStoreMigratorUtils.getWritableDocStorePath( tempDir, name )
	local writableDocStorePath = OzDocStoreMigratorUtils.getWritableDocStorePath( tempDir, name .. '-writable' )

	if WFFileUtils.exists( docStorePath ) then
		WFFileUtils.deleteDirectory( docStorePath )
	end
	if WFFileUtils.exists( writableDocStorePath ) then
		WFFileUtils.deleteDirectory( writableDocStorePath )
	end

	if name ~= 'dummy' then
		OzDocStoreMigratorUtils.copyDocStoreToWritableLocation( testDataDocStorePath, docStorePath )
	end
	return docStorePath, writableDocStorePath
end


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

 function OzDocStoreMigratorUtils.readFullV2Document( docStore, fullDocId )

	-- a document with revisions and labels
	local fullDoc = {}

	local docRows = docStore._db:gatherArrayOfArray(
		[[ SELECT localDocId, winningRevSequence, mostRecentRevSequence, deleted, hasConflicts, annotation
			FROM docs
			WHERE fullDocId = ?]],
		fullDocId )

	local docRow = docRows and docRows[ 1 ]
	
	if not docRow then
		return
	end

	fullDoc.doc = {
		localDocId = docRow[ 1 ],
		winningRevSequence = docRow[ 2 ],
		mostRecentRevSequence = docRow[ 3 ],
		deleted = docRow[ 4 ],
		hasConflicts = docRow[ 5 ],
		annotation = OzDocStoreMigratorUtils.contentFromFlattenedValue( docRow[ 6 ] )
	}

	local revRows = docStore._db:gatherArrayOfArray(
		[[ SELECT revId, content
			FROM revs
			WHERE localDocId = ?
			ORDER BY sequence ASC ]],
		fullDoc.doc.localDocId )

	fullDoc.revs = {}

	for i, revRow in ipairs( revRows ) do
		fullDoc.revs[ i ] = {
			revId = revRow[ 1 ],
			content = OzDocStoreMigratorUtils.contentFromFlattenedValue( revRow[ 2 ] )
		}
	end

	local labelRows = docStore._db:gatherArrayOfArray(
		[[ SELECT localDocId, revSequence, name
			FROM labels
			WHERE localDocId = ?
			ORDER BY revSequence ASC ]],
		fullDoc.doc.localDocId )

	local labelRow = labelRows and labelRows[ 1 ]

	if labelRow then
	
		fullDoc.label = {
			localDocId = labelRow[ 1 ],
			revSequence = labelRow[ 2 ],
			name = labelRow[ 3 ]
		}

	end
	
	return fullDoc 
end

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

function OzDocStoreMigratorUtils.writeFullV2Document( docStore, doc )
	
	docStore._exec( docStore._db, [[INSERT INTO docs( fullDocId ) VALUES( ? )]], doc.doc.fullDocId )

	doc.doc.localDocId = docStore._db:getLastInsertRowId()
	doc.label.localDocId = doc.doc.localDocId

	-- what to do about the parentRevSeq?

	local current

	for i, rev in ipairs( doc.revs ) do

		if not rev.revId then
			error("nil revId")
		end

		current = 0
		if i == #doc.revs then
			current = 1
		end

		docStore._exec( docStore._db,
			  [[INSERT INTO revs( localDocId, revId, current, deleted, content )
					VALUES( ?, ?, ?, ?, ? )]],
			  doc.doc.localDocId,
			  rev.revId,
			  current,
			  rev._deleted and 1 or 0,
			  tableToString( rev.content ) )
			  
		rev.sequence = docStore._db:getLastInsertRowId()
		doc.doc.mostRecentRevSequence = rev.sequence
		doc.doc.winningRevSequence = rev.sequence
		doc.label.revSeq = rev.sequence
	end

	docStore._exec( docStore._db,
		[[UPDATE docs
					SET winningRevSequence = ?,
						mostRecentRevSequence = ?,
						deleted = ?,
						hasConflicts = ?,
						annotation = ?
					WHERE localDocId = ?]],
			doc.doc.mostRecentRevSequence,
			doc.doc.winningRevSequence,
		  	(doc.doc.deleted == true or doc.doc.deleted == 1) and 1 or 0,
		  	doc.doc.hasConflicts == true and 1 or 0,
		  	tableToString( doc.doc.annotation ) ,
		  	doc.doc.localDocId )
	
	docStore._exec( docStore._db,
		  [[INSERT INTO labels( localDocId, revSequence, name )
				VALUES( ?, ?, ? )]],
		  doc.label.localDocId,
		  doc.label.revSequence,
		  doc.label.name )
				
end

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

function OzDocStoreMigratorUtils.docIdIterator( docStore )

	local docIds = docStore._db:gatherArray( [[ SELECT fullDocId FROM docs]] )
	local i = 0
	local n = #docIds

	return function()
		i = i + 1
		if i <= n then return docIds[i] end
	end
end

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

return OzDocStoreMigratorUtils
