
import org.apache.commons.lang.WordUtils

class AssetService {

    boolean transactional = false

    def sessionFactory

    def assetSubItemService

    /**
    * Determines and returns a possible list of asset sub items.
    * @returns A list of the possible assetSubItems.
    */
    def possibleAssetSubItems() {
        def criteria = AssetSubItem.createCriteria()
        def possibleAssetSubItems = criteria.list() {
            and {
                eq('isActive', true)
                isNull("parentItem")
                }
        }
    }

    /**
    * Determines and returns a list of assemblies for an asset.
    * This is purely a 'load from database' type method since a new hibernateSession is used.
    * @params Asset to get the subItems for.
    * @returns A list of the assemblies.
    */
    def getAssemblies(asset) {
        def assemblies = []
        if(!(asset instanceof Asset))
            return assemblies
        // Database efficiency:
        // The asset is configured to batch fetch assetSubItems which
        // in turn are configured to batch fetch subItems.
        Asset.withNewSession {
            Asset.get(asset.id).assetSubItems.each {
                assemblies.addAll(it.subItems)
            }
        }
        assemblies.sort { p1, p2 -> p1.name.compareToIgnoreCase(p2.name) }
        return assemblies
    }

    def delete(params) {
        Asset.withTransaction { status ->
            def result = [:]

            def fail = { Map m ->
                status.setRollbackOnly()
                if(result.assetInstance && m.field)
                    result.assetInstance.errors.rejectValue(m.field, m.code)
                result.error = [ code: m.code, args: ["Asset", params.id] ]
                return result
            }

            result.assetInstance = Asset.get(params.id)

            if(!result.assetInstance)
                return fail(code:"default.not.found")

            if(result.assetInstance.maintenanceActions)
                return fail(code:"maintenanceActions.still.associated")

            // Remove orphan assetSubItems.
            def assetSubItems = new ArrayList(result.assetInstance.assetSubItems) // avoid ConcurrentModificationException.
            def r
            for(assetSubItem in assetSubItems) {
                result.assetInstance.removeFromAssetSubItems(assetSubItem)
                if(!assetSubItem.assets && !assetSubItem.parentItem) {
                    r = assetSubItemService.delete(id: assetSubItem.id)
                    if(r.error) {
                        log.debug r.error
                        fail(code:"asset.subItems.delete.failure")
                        break
                    }
                }
            }

            if(result.error)
                return result

            // Success.
            // We have handled all the foreign keys so the delete should go forward.
            // Can't flush here due to cascading from Section and Site.
            // And without flush there is no point it trying to catch the dao.DataIntegrityViolationException
            // since that will only happen after leaving the transaction.
            result.assetInstance.delete()
            return result

        } // end withTransaction
    } // end delete()

    def create(params) {
        def result = [:]
        def fail = { Map m ->
            result.error = [ code: m.code, args: ["Asset", params.id] ]
            return result
        }

        result.assetInstance = new Asset()
        result.assetInstance.properties = params

        // success
        return result
    }

    def copy(params) {
        def result = [:]
        def fail = { Map m ->
            result.error = [ code: m.code, args: ["Asset", params.id] ]
            return result
        }

        result.assetToCopy = Asset.get(params.assetToCopy?.id)

        if(!result.assetToCopy)
            return fail(code: "asset.copy.asset.required")

        result.assetInstance = new Asset(name: result.assetToCopy.name,
                                                            description: result.assetToCopy.description,
                                                            comment: result.assetToCopy.comment,
                                                            section: result.assetToCopy.section)

        result.assetInstance.properties = params

        // success
        return result
    }

    def save(params) {
        def result = [:]
        def fail = { Map m ->
            if(result.assetInstance && m.field) 
                result.assetInstance.errors.rejectValue(m.field, m.code)
            result.error = [ code: m.code, args: ["Asset", params.id] ]
            return result
        }

        result.assetInstance = new Asset(params)

        use(WordUtils) {
            result.assetInstance.name = result.assetInstance.name.capitalize()
            result.assetInstance.description = result.assetInstance.description.capitalize()
        }

        if(result.assetInstance.hasErrors() || !result.assetInstance.save(flush: true))
            return fail(code:"default.create.failure")

        // success
        return result
    }

    def saveCopy(params) {
        Asset.withTransaction { status ->
            def result = [:]

            def fail = { Map m ->
                status.setRollbackOnly()
                if(result.assetInstance && m.field)
                    result.assetInstance.errors.rejectValue(m.field, m.code)
                result.error = [ code: m.code, args: ["Asset", params.id] ]
                return result
            }

            result.assetToCopy = Asset.get(params.assetToCopy?.id)
            if(!result.assetToCopy)
                return fail(code:"default.not.found")

            if(!params.copyMethod)
                fail(code:"asset.copy.method.required")

            result.assetInstance =  new Asset(params)

            if(result.assetInstance.hasErrors() || !result.assetInstance.save())
                return fail(code:"default.create.failure")

            def assetSubItemInstance1

            // Copy subItems from level 2 and bellow.
            def copyAssetSubItem = { assetSubItemToCopy, parentItem ->
                def nextCount = AssetSubItem.count() + 1
                def baseName = assetSubItemToCopy.name.split('\\(id:')[0]
                def name = baseName +'(id:'+nextCount+')'
                def assetSubItemInstance = new AssetSubItem(name: name,
                                                                                            description: assetSubItemToCopy.description,
                                                                                            parentItem: parentItem)

                if(assetSubItemInstance.hasErrors() || !assetSubItemInstance.save())
                    return fail(code:"asset.copy.subItem.create.failure")

                def i = 0
                for(assetSubItem in assetSubItemToCopy.subItems) {
                    call(assetSubItem, assetSubItemInstance)
                    // Protect against endless recurrsion.
                    i++
                    if(i > 100)
                        fail(code:"asset.copy.subItem.too.many.failure")
                    // Stop if an error is flagged.
                    if(result.error)
                        break
                }
            } //copyAssetSubItem

            // Copy the 1st level of subItems.
            def copyAssetSubItem1 = { assetSubItemToCopy ->
                def nextCount = AssetSubItem.count() + 1
                def baseName = assetSubItemToCopy.name.split('\\(id:')[0]
                def name = baseName +'(id:'+nextCount+')'
                assetSubItemInstance1 = new AssetSubItem(name: name,
                                                                                        description: assetSubItemToCopy.description,
                                                                                        asset: result.assetInstance)

                if(assetSubItemInstance1.hasErrors() || !assetSubItemInstance1.save())
                    return fail(code:"asset.copy.subItem.create.failure")

                result.assetInstance.addToAssetSubItems(assetSubItemInstance1)

                def i = 0
                for(assetSubItem in assetSubItemToCopy.subItems) {
                    copyAssetSubItem(assetSubItem, assetSubItemInstance1)
                    // Protect against endless recurrsion.
                    i++
                    if(i > 100)
                        fail(code:"asset.copy.subItem.too.many.failure")
                    // Stop if an error is flagged.
                    if(result.error)
                        break
                }
            } //copyAssetSubItem1

            def linkAssetSubItem = { assetSubItemToLink ->
                result.assetInstance.addToAssetSubItems(assetSubItemToLink)
            }

            def i = 0
            for(assetSubItem in result.assetToCopy.assetSubItems) {

                if(params.copyMethod == "copy")
                    copyAssetSubItem1(assetSubItem)
                else
                    linkAssetSubItem(assetSubItem)
                // Protect against endless recurrsion.
                i++
                if(i > 100)
                    fail(code:"asset.copy.subItem.too.many.failure")
                // Stop if an error is flagged.
                if(result.error)
                    break
            }

            // Success or not.
            return result

        } // end withTransaction
    } // end saveCopySrvce

    /**
    * Create recommended extended attributes for all assets.
    */
    def createRecommendedExtendedAttributes() {
        def result = [:]

        def hibernateSession = sessionFactory.currentSession

        def assets = Asset.list()
        def locationDescription = ExtendedAttributeType.findByName("Location Description")
        def ecr = ExtendedAttributeType.findByName("ecr")
        def assetNumber = ExtendedAttributeType.findByName("Asset Number")
        def assetCondition = ExtendedAttributeType.findByName("Asset Condition")
        def maintenancePercentComplete = ExtendedAttributeType.findByName("Maintenance % Completion")
        def registrationRequired = ExtendedAttributeType.findByName("Registration Required")
        def registrationExpiryDate =  ExtendedAttributeType.findByName("Registration Expiry Date")
        def regulatoryRequirement = ExtendedAttributeType.findByName("Regulatory Requirement")
        def riskLevel = ExtendedAttributeType.findByName("Risk Level")
        def safeWorkProcedure = ExtendedAttributeType.findByName("Safe Work Procedure")

        for(asset in assets) {

            def attributeTypes = asset.assetExtendedAttributes.collect {it.extendedAttributeType}

            //AssetExtendedAttribute
            def assetExtendedAttributeInstance

            if(!attributeTypes.contains(locationDescription)) {
                //AssetExtendedAttribute #1
                assetExtendedAttributeInstance = new AssetExtendedAttribute(value: "Not Specified",
                                                                                                                    asset: asset,
                                                                                                                    extendedAttributeType: locationDescription)
                assetExtendedAttributeInstance.save()
            }

            if(!attributeTypes.contains(ecr)) {
                //AssetExtendedAttribute #2
                assetExtendedAttributeInstance = new AssetExtendedAttribute(value: "Not Specified",
                                                                                                                    asset: asset,
                                                                                                                    extendedAttributeType: ecr)
                assetExtendedAttributeInstance.save()
            }

            if(!attributeTypes.contains(assetNumber)) {
                //AssetExtendedAttribute #3
                assetExtendedAttributeInstance = new AssetExtendedAttribute(value: "Not Specified",
                                                                                                                    asset: asset,
                                                                                                                    extendedAttributeType: assetNumber)
                assetExtendedAttributeInstance.save()
            }

            if(!attributeTypes.contains(assetCondition)) {
                //AssetExtendedAttribute #4
                assetExtendedAttributeInstance = new AssetExtendedAttribute(value: "Not Specified",
                                                                                                                    asset: asset,
                                                                                                                    extendedAttributeType: assetCondition)
                assetExtendedAttributeInstance.save()
            }

            if(!attributeTypes.contains(maintenancePercentComplete)) {
                //AssetExtendedAttribute #5
                assetExtendedAttributeInstance = new AssetExtendedAttribute(value: "TBA",
                                                                                                                    asset: asset,
                                                                                                                    extendedAttributeType: maintenancePercentComplete)
                assetExtendedAttributeInstance.save()
            }

            if(!attributeTypes.contains(registrationRequired)) {
                //AssetExtendedAttribute #6
                assetExtendedAttributeInstance = new AssetExtendedAttribute(value: "Not Specified",
                                                                                                                    asset: asset,
                                                                                                                    extendedAttributeType: registrationRequired)
                assetExtendedAttributeInstance.save()
            }

            if(!attributeTypes.contains(registrationExpiryDate)) {
                //AssetExtendedAttribute #7
                assetExtendedAttributeInstance = new AssetExtendedAttribute(value: "Not Specified",
                                                                                                                    asset: asset,
                                                                                                                    extendedAttributeType:registrationExpiryDate)
                assetExtendedAttributeInstance.save()
            }

            if(!attributeTypes.contains(regulatoryRequirement)) {
                //AssetExtendedAttribute #8
                assetExtendedAttributeInstance = new AssetExtendedAttribute(value: "Not Specified",
                                                                                                                    asset: asset,
                                                                                                                    extendedAttributeType: regulatoryRequirement)
                assetExtendedAttributeInstance.save()
            }

            if(!attributeTypes.contains(riskLevel)) {
                //AssetExtendedAttribute #9
                assetExtendedAttributeInstance = new AssetExtendedAttribute(value: "Not Specified",
                                                                                                                    asset: asset,
                                                                                                                    extendedAttributeType: riskLevel)
                assetExtendedAttributeInstance.save()
            }

            if(!attributeTypes.contains(safeWorkProcedure)) {
                //AssetExtendedAttribute #10
                assetExtendedAttributeInstance = new AssetExtendedAttribute(value: "Not Specified",
                                                                                                                    asset: asset,
                                                                                                                    extendedAttributeType: safeWorkProcedure)
                assetExtendedAttributeInstance.save()
            }

            hibernateSession.flush()

        } // for

        // Success.
        return result

    } // createRecommendedExtendedAttributes()

} // end class