/**
* Provides a service class for the InventoryItem domain class.
*/
class InventoryItemService {

    boolean transactional = false

    /**
    * Prepare a sorted list of possible alternateItems.
    */
    def getPossibleAlternateItems(inventoryItemInstance) {
        def criteria = inventoryItemInstance.createCriteria()
        def possibleAlternateItems = criteria {
            and {
                eq('isActive', true)
                notEqual('id', inventoryItemInstance.id)
            }
        }.sort { p1, p2 -> p1.name.compareToIgnoreCase(p2.name) }
    }

    /**
    * Prepare the data for the show view.
    * The result can be used to easily construct the model for the show view.
    * @param params The incoming params as normally passed to the show view
    * primarily including the id of the inventoryItem.
    * @returns A map containing result.error, if any error, otherwise result.inventoryItemInstance.
    */
    def show(params) {
        def result = [:]

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

        result.inventoryItemInstance = InventoryItem.get( params.id )

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

        def p = [:]

        if(params.paginate == "purchases") {
            params.showTab = "showPurchasingTab"
            p.max = Math.min(params.max?.toInteger() ?: 10, 100)
            p.offset = params.offset?.toInteger() ?: 0
            p.sort = params.sort ?: null
            p.order = params.order ?: null
        }
        else {
            p.max = 10
            p.offset = 0
        }

        result.inventoryItemPurchasesTotal = InventoryItemPurchase.countByInventoryItem(result.inventoryItemInstance)

        result.inventoryItemPurchases = InventoryItemPurchase.withCriteria {
                eq("inventoryItem", result.inventoryItemInstance)
                maxResults(p.max)
                firstResult(p.offset)
                // Sorting:
                // Default is to sort by order number then id.
                // When a sortable column is clicked then we sort by that.
                // If the sortable column clicked is order number then we add id as the second sort.
                if(p.sort && p.order) {
                    order(p.sort, p.order)
                    if(p.sort == "purchaseOrderNumber") order('id', 'asc')
                }
                else {
                    order('purchaseOrderNumber', 'desc')
                    order('id', 'asc')
                }
            }

        result.showTab = [:]
        switch (params.showTab) {
            case "showDetailTab":
                result.showTab.detail =  new String("true")
                break
            case "showMovementTab":
                result.showTab.movement =  new String("true")
                break
            case "showPurchasingTab":
                result.showTab.purchasing =  new String("true")
                break
            default:
                result.showTab.inventory = new String("true")
        }

        p.max = result.inventoryMovementListMax = 10
        p.offset = 0
        p.order = "desc"
        p.sort = "id"
        result.inventoryMovementList = InventoryMovement.findAllByInventoryItem(result.inventoryItemInstance, p)
        result.inventoryMovementListTotal = InventoryMovement.countByInventoryItem(result.inventoryItemInstance)


        // Success.
        return result

    } // end show()

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

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

            result.inventoryItemInstance = InventoryItem.get(params.id)

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

            if(result.inventoryItemInstance.inventoryMovements)
                return fail(code:"inventoryMovement.still.associated")

            result.inventoryItemInstance.removeReverseAlternateItems()

            try {
                result.inventoryItemInstance.delete(flush:true)
                return result //Success.
            }
            catch(org.springframework.dao.DataIntegrityViolationException e) {
                return fail(code:"default.delete.failure")
            }

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

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

        result.inventoryItemInstance = InventoryItem.get(params.id)

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

        // Success.
        return result
    }

    def update(params) {
        InventoryItem.withTransaction { status ->
            def result = [:]

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

            result.inventoryItemInstance = InventoryItem.get(params.id)

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

            // Optimistic locking check.
            if(params.version) {
                if(result.inventoryItemInstance.version > params.version.toLong())
                    return fail(field:"version", code:"default.optimistic.locking.failure")
            }

            def previousAlternateItems = new ArrayList(result.inventoryItemInstance.alternateItems)

            result.inventoryItemInstance.properties = params

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

            result.inventoryItemInstance.removeReverseAlternateItems(previousAlternateItems)
            result.inventoryItemInstance.addReverseAlternateItems()

            // Success.
            return result

        } //end withTransaction
    }  // end update()

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

        result.inventoryItemInstance = new InventoryItem()
        result.inventoryItemInstance.properties = params

        // success
        return result
    }

    def save(params) {
        InventoryItem.withTransaction { status ->
            def result = [:]

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

            result.inventoryItemInstance = new InventoryItem(params)

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

            // success
            return result

        } //end withTransaction
    }

    /**
    * Save an inventory item picture.
    * @param pictureSource A supported source to get the picture image from.
    * Supported sources:
    * HttpServletRequest e.g: 'request' var from controller to run getFile('file') against.
    * ServletContextResource e.g: grailsApplication.mainContext.getResource('images/logo.png')
    */
    def savePicture(params, pictureSource) {
        InventoryItem.withTransaction { status ->
            def result = [:]

            def kByteMultiplier = 1000

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

            result.inventoryItemInstance = InventoryItem.get(params.id)

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

            // Optimistic locking check.
            if(params.version) {
                if(result.inventoryItemInstance.version > params.version.toLong())
                    return fail(field:"version", code:"default.optimistic.locking.failure")
            }

            if(result.inventoryItemInstance.picture)
                return fail(field:"picture", code:"inventory.item.already.has.picture")

            // Declare some more variables, since we appear to have most of what we need.
            def picture = new Picture(inventoryItem: result.inventoryItemInstance)
            def imaging = new Imaging()
            def images = null
            def pictureFile
            def pictureFileName = ''
            def pictureInputStream

            // Check the supplied pictureSource and get the inputStream.
            if(pictureSource instanceof javax.servlet.http.HttpServletRequest) {
                def multiPartFile = pictureSource.getFile('file')
                pictureFileName = multiPartFile.originalFilename

                if(!multiPartFile || multiPartFile.isEmpty())
                    return fail(code: "default.file.not.supplied")

                if (multiPartFile.getSize() > Image.MAX_SIZE)
                    return fail(code: "default.file.over.max.size", args: [Image.MAX_SIZE/kByteMultiplier, "kB"])

                pictureInputStream = multiPartFile.inputStream
            }
            else if(pictureSource instanceof org.springframework.web.context.support.ServletContextResource) {
                pictureFile = pictureSource.getFile()
                pictureFileName = pictureFile.name

                if ( !pictureFile.isFile() || (pictureFile.length() == 0) )
                    return fail(code:"default.file.not.supplied")

                if (pictureFile.length() > Image.MAX_SIZE)
                    return fail(code:"default.file.over.max.size", args: [Image.MAX_SIZE/kByteMultiplier, "kB"])

                pictureInputStream = pictureSource.inputStream
            }
            else {
                    return fail(code:"inventory.item.picture.source.not.supported")
            }

            // Create the Images.
            try {
                images = imaging.createAll(result.inventoryItemInstance, picture, pictureInputStream)
                // Ensure the stream is closed.
                pictureInputStream.close()
            }
            catch(Exception ex) {
                log.error("picture save", ex)
                // Ensure the stream is closed.
                pictureInputStream.close()
                return fail(code:"inventory.item.picture.file.unrecognised", args: [pictureFileName])
            }

            // Add images to picture.
            images.each { image ->
                picture.addToImages(image)
            }

            // Save picture.
            if(picture.hasErrors() || !picture.save())
                return fail(code:"default.create.failure", args: ["Picture"])

            result.inventoryItemInstance.picture = picture

            // Save inventoryItem.
            if(result.inventoryItemInstance.hasErrors() || !result.inventoryItemInstance.save())
                return fail(code:"default.create.failure")

            // success
            return result

        } //end withTransaction
    }

} // end class
