import grails.util.GrailsUtil

/**
* Provides a data service to create a large volume of test data for load testing.
*/
class  CreateBulkDataService {

    boolean transactional = false

    def authService
    def taskService
    def dateUtilService
    def appConfigService
    def searchableService
    def assignedGroupService
    def assignedPersonService
    def inventoryItemService

    def sessionFactory
    def grailsApplication
    def propertyInstanceMap = org.codehaus.groovy.grails.plugins.DomainClassGrailsPlugin.PROPERTY_INSTANCE_MAP

    def startTime
    def lastBatchStarted

/*******************************************
Start of Group methods.
Generally use these methods to create data.
*******************************************/

    /**
    * Make a run of data creation.
    */
    def createAll() {
        def result = [:]

        def fail = { Map m ->
            result.error = [ code: m.code, args: m.args ]
            return result
        }

        if(GrailsUtil.environment != "development")
            return fail(code: 'default.not.development.environment.failure')

        log.info "Stop mirroring lucene index."
        searchableService.stopMirroring()

        log.info "Creating BULK data..."

        // Person and Utils
        log.info "Creating persons..."
        createBulkTestPersons()
//         createBulkTestSites()
//         createBulkTestDepartments()
//         createBulkTestSuppliers()
//         createBulkTestManufacturers()

        // Assets
//         createBulkTestLifePlan()
//         createBulkTestTaskProcedure()
//         createBulkTestMaintenanceActions()
//         createBulkTestSections()
//         createBulkTestAssets()
//         createBulkTestAssetExtenedAttributes()
//         createBulkTestAssetSubItems()
//         createBulkTestAssetSubItemExtenedAttributes()

        // Inventory
        log.info "Creating inventory..."
//         createBulkTestInventoryStores()  /// @todo: Perhaps a 'createQuickStartData' method?
        createBulkTestInventoryLocations()
//         createBulkTestInventoryGroups() /// @todo: Perhaps a 'createQuickStartData' method?
        createBulkTestInventoryItems()

        // Tasks
        log.info "Creating tasks..."
        createBulkTestTasks()
//         createBulkTestEntries()
//         createBulkTestAssignedGroups()
//         createBulkTestAssignedPersons()
//         createBulkTestTaskRecurringSchedules()

        log.info "Creating BULK data...complete."

        log.info "Start mirroring Lucene index."
        searchableService.startMirroring()
        log.info "Rebuilding Lucene index, bulkIndex."
        searchableService.reindex()
        log.info "Rebuilding Lucene index, complete."

        return result

    } // create()

    /**
    * Make a run of inventory data creation.
    */
    def createBulkInventoryTestData() {
        def result = [:]

        def fail = { Map m ->
            result.error = [ code: m.code, args: m.args ]
            return result
        }

        if(GrailsUtil.environment != "development")
            return fail(code: 'default.not.development.environment.failure')

        log.info "Stop mirroring Lucene index."
        searchableService.stopMirroring()

        log.info "Creating BULK data..."

        // Inventory
        log.info "Creating inventory..."
//         createBulkTestInventoryStores()  /// @todo: Perhaps a 'createQuickStartData' method?
        createBulkTestInventoryLocations()
//         createBulkTestInventoryGroups() /// @todo: Perhaps a 'createQuickStartData' method?
        createBulkTestInventoryItems()

        log.info "Creating BULK data...complete."

        log.info "Start mirroring Lucene index."
        searchableService.startMirroring()
        log.info "Rebuilding Lucene index, bulkIndex."
        searchableService.reindex()
        log.info "Rebuilding Lucene index, complete."

        return result

    } // createBulkInventoryTestData()

/******************
Start of Person
*******************/

    def createBulkTestPersons() {
        //Person
        def passClearText = "pass"
        def passwordEncoded = authService.encodePassword(passClearText)
        def personInstance

        def start = Person.count() + 1
        def end = start + 100

        def range = start..end

        def loginName = "BtLoginName"
        String btLoginName
        def firstName = "BtFirstName"
        String btFirstName
        def lastName = "BtLastName"

        def authority2 = Authority.get(2)
        def authority3 = Authority.get(3)
        def personGroup1 = PersonGroup.get(1)
        def personGroup2 = PersonGroup.get(2)
        def personGroup3 = PersonGroup.get(3)
        def personGroup4 = PersonGroup.get(4)
        def personGroup5 = PersonGroup.get(5)

        range.each() {

            btLoginName = loginName + it
            btFirstName = firstName + it

            personInstance = new Person(loginName: btLoginName,
                                        firstName: btFirstName,
                                        lastName: lastName,
                                        pass: passClearText,
                                        password: passwordEncoded)
            saveAndTest(personInstance)
            personInstance.addToAuthorities(authority2)
            personInstance.addToAuthorities(authority3)
            personInstance.addToPersonGroups(personGroup1)
            personInstance.addToPersonGroups(personGroup2)
            personInstance.addToPersonGroups(personGroup3)
            personInstance.addToPersonGroups(personGroup4)
            personInstance.addToPersonGroups(personGroup5)

        }

    } // createBulkTestPersons()

/*********************
START OF TASK
*********************/

    def createBulkTestTasks() {

        def taskResult
        def p = [:]

        def start = Task.count() + 1
        def end = start + 10000

        def range = start..end


        def taskGroup1 = TaskGroup.get(1)
        def taskPriority2 = TaskPriority.get(2)
        def taskType3 = TaskType.get(3)
        def leadPerson2 = Person.get(2)

        def description = "Bulk test data "
        String btDescription
        def comment1 = "Has been noted as problematic, try recalibrating."
        def today = dateUtilService.today

        startTime = System.currentTimeMillis()
        lastBatchStarted = startTime

        range.each() {

            if(it % 100 == 0) {
                logStatus("Creating task #" + it)
                cleanUpGorm()
            }

            btDescription = description + it

            //Task #1
            p = [taskGroup: taskGroup1,
                    taskPriority: taskPriority2,
                    taskType: taskType3,
                    leadPerson: leadPerson2,
                    description: btDescription,
                    comment: comment1,
                    targetStartDate: today]

            taskResult = taskService.save(p)
        }

    } // createBulkTestTasks()

    def createBulkTestEntries() {

        def entryResult
        def p = [:]

        def range = 1..10
        def task1 = Task.get(1)
        def entryType1 = EntryType.get(1)
        def comment1 = "This is a bulk test entry."
        def durationMinute1 = 20

        range.each() {

            p = [task: task1,
                    entryType: entryType1,
                    comment: comment1,
                    durationMinute: durationMinute1]

            entryResult = taskService.saveEntry(p)

        }

    } // createBulkTestEntries()


/**************************
START OF INVENTORY
**************************/

    def createBulkTestInventoryLocations() {

        def inventoryLocationResult
        def p = [:]

        def start = InventoryLocation.count() + 1
        def end = start + 50

        def range = start..end


        def inventoryStore1 = InventoryStore.read(1)

        def name = "Bulk test location "
        def btName = ''

        startTime = System.currentTimeMillis()
        lastBatchStarted = startTime

        range.each() {

            if(it % 25 == 0) {
                logStatus("Creating inventory location #" + it)
                cleanUpGorm()
            }

            btName = name + it

            p = [inventoryStore: inventoryStore1,
                    name: btName]

            inventoryLocationResult = new InventoryLocation(p).save()
        } // each()

    } // createBulkTestInventoryLocations()

    def createBulkTestInventoryItems() {

        def inventoryItemInstance
        def p = [:]

        def pictureResource = grailsApplication.mainContext.getResource('images/logo.png')

        def start = InventoryItem.count() + 1
        def end = start + 250

        def range = start..end

        def inventoryLocation
        def inventoryLocationIndex = 0
        def inventoryLocationList = InventoryLocation.findAll()
        def unitOfMeasure2 = UnitOfMeasure.read(2)
        def inventoryType1 = InventoryType.read(1)
        def inventoryGroup1 = InventoryGroup.read(1)

        def name = "Bulk test inventory item "
        def btName = ''

        startTime = System.currentTimeMillis()
        lastBatchStarted = startTime

        range.each() {

            if(it % 50 == 0) {
                logStatus("Creating inventory item #" + it)
                cleanUpGorm()
            }

            // Spread the inventoryItems across all available locations.
            if(inventoryLocationIndex < inventoryLocationList.size()) {
                inventoryLocation = inventoryLocationList[inventoryLocationIndex]
            }
            else {
                inventoryLocationIndex = 0
                inventoryLocation = inventoryLocationList[inventoryLocationIndex]
            }
            inventoryLocationIndex++

            // Change the name for each inventoryItem.
            btName = name + it

            p = [inventoryGroup: inventoryGroup1,
                    inventoryType: inventoryType1,
                    unitOfMeasure: unitOfMeasure2,
                    inventoryLocation: inventoryLocation,
                    name: btName,
                    description: "Bulk test data",
                    unitsInStock: 2,
                    reorderPoint: 0]

            inventoryItemInstance = new InventoryItem(p)
            saveAndTest(inventoryItemInstance)

            def pictureResult = inventoryItemService.savePicture(inventoryItemInstance, pictureResource)

            if(pictureResult.error)
                log.error pictureResult.error
        } // each()

    } // createBulkTestInventoryItems()

    /**
    * This cleans up the hibernate session and a grails map.
    * For more info see: http://naleid.com/blog/2009/10/01/batch-import-performance-with-grails-and-mysql/
    * The hibernate session flush is normal for hibernate.
    * The map is apparently used by grails for domain object validation errors.
    * A starting point for clean up is every 100 objects.
    */
    def cleanUpGorm() {
        def session = sessionFactory.currentSession
        session.flush()
        session.clear()
        propertyInstanceMap.get().clear()
    }

    def logStatus(String message) {
        def batchEnded = System.currentTimeMillis()
        def seconds = (batchEnded-lastBatchStarted)/1000
        def total = (batchEnded-startTime)/1000
        log.info "${message}, last: ${seconds}s, total: ${total}s"
        lastBatchStarted = batchEnded
    }


/****************************************
Call this function instead of .save()
*****************************************/
    private boolean saveAndTest(object) {
        if(!object.save()) {
//             BulkTestDataSuccessful = false
            log.error "'${object}' failed to save!"
            log.error object.errors
            return false
        }
        return true
    }

} // end class.