import au.com.bytecode.opencsv.CSVWriter
import au.com.bytecode.opencsv.CSVReader

/**
 * Provides some csv import/export methods.
 * Requires the opencsv jar to be available which is included in the grails-export plugin.
 */
class CsvService {

    boolean transactional = false

    /**
    * Import an asset tree creating items as required.
    * @param request The http request to run getFile against.
    * Get file should return a csv format file containing the asset tree as per template.
    */
    def importAssetTree(request) {
        Asset.withTransaction { status ->
            def result = [:]

            def megaByteMultiplier = 1000 * 1000
            def fileMaxSize = 10 * megaByteMultiplier //Mb

            def multiPartFile = request.getFile('file')

            InputStreamReader sr = new InputStreamReader(multiPartFile.inputStream)
            CSVReader reader = new CSVReader(sr)

            def fail = { Map m ->
                status.setRollbackOnly()
                reader.close()
                result.error = [ code: m.code, args: m.args ]
                return result
            }

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

            if (multiPartFile.getSize() > fileMaxSize)
                return fail(code: "asset.tree.import.file.over.max.size", args: [fileMaxSize/megaByteMultiplier])

            def line = reader.readNext()
            def lineNumber = 1

            def header = ["Site", "Section", "Asset", "Sub Asset", "Functional Assembly", "Sub Assembly Group"]

            if(line != header)
                return fail(code: "asset.tree.import.no.header")

            log.info "Import checks passed, start processing asset file."

            // Prepare the first body line.
            line = reader.readNext()
            lineNumber ++

            def siteInstance
            def sectionInstance
            def assetInstance

            while(line) {
                def lineSize = line.size()
    //             log.info lineNumber+ "(" + lineSize + ")" + " : " + line

                if(line[0]) {
                        if( !Site.findByName(line[0]) )
                            siteInstance = new Site(name: line[0])
                            if(!siteInstance.save())
                                return fail(code: "asset.tree.import.failure", args: [lineNumber])
                }
                else continue

                if(line[1]) {
                        if( !Section.findByName(line[1]) )
                           sectionInstance =  new Section(name: line[1],
                                                                                site: siteInstance)
                            if(!sectionInstance.save())
                                return fail(code: "asset.tree.import.failure", args: [lineNumber])
                }
                else continue

                if(line[2]) {
                        if( !Asset.findByName(line[2]) )
                            assetInstance = new Asset(name: line[2],
                                                                        section: sectionInstance)
                            if(!sectionInstance.save())
                                return fail(code: "asset.tree.import.failure", args: [lineNumber])
                }
                else continue

                line = reader.readNext()
                lineNumber ++
            } //while(line)

            // Success.
            reader.close()
            return result

        } //end withTransaction
    } // end importAssetTree()

    /**
    * Build an asset tree template csv file.
    * This template can then be populated for import.
    * @returns The template as a String in csv format.
    */
    def buildAssetTreeTemplate() {

        StringWriter sw = new StringWriter()
        CSVWriter writer = new CSVWriter(sw)

        //Header
        def header = ["Site", "Section", "Asset", "Sub Asset", "Functional Assembly", "Sub Assembly Group"]
        def blankLine = []
        def noteLine = ["Note: the header is required, start by replacing this line."]

        writer.writeNext(header as String[])
        writer.writeNext(blankLine as String[])
        writer.writeNext(noteLine as String[])

        writer.close()
        return sw.toString()
    }

    /**
    * Build complete asset trees for export.
    * @param assetList The list of assets to build and export trees for.
    * @returns The tree as a String in csv format.
    */
    def buildAssetTree(List assetList) {

        StringWriter sw = new StringWriter()
        CSVWriter writer = new CSVWriter(sw)

        //Header
        def header = ["Site", "Section", "Asset", "Sub Asset", "Functional Assembly", "Sub Assembly Group"]
        writer.writeNext(header as String[])

        //Rows
        def row

        def writeAssetSubItem4 = { assetSubItem ->
            row.add(assetSubItem)
            writer.writeNext(row as String[])
        }

        def writeAssetSubItem3 = { assetSubItem ->
            row.add(assetSubItem)

            if(assetSubItem.subItems.size() > 0) {
                assetSubItem.subItems.sort { p1, p2 -> p1.name.compareToIgnoreCase(p2.name) }.each() { assetSubItem4 ->
                    writeAssetSubItem4(assetSubItem4)
                    row.remove(row.last())
                }
            }
            else {
                writer.writeNext(row as String[])
            }

        }

        def writeAssetSubItem2 = { assetSubItem ->
            row.add(assetSubItem)

            if(assetSubItem.subItems.size() > 0) {
                assetSubItem.subItems.sort { p1, p2 -> p1.name.compareToIgnoreCase(p2.name) }.each() { assetSubItem3 ->
                    writeAssetSubItem3(assetSubItem3)
                    row.remove(row.last())
                }
            }
            else {
                writer.writeNext(row as String[])
            }

        }

        def writeAssetSubItem1 = { assetSubItem ->
            row.add(assetSubItem)

            if(assetSubItem.subItems.size() > 0) {
                assetSubItem.subItems.sort { p1, p2 -> p1.name.compareToIgnoreCase(p2.name) }.each() { assetSubItem2 ->
                    writeAssetSubItem2(assetSubItem2)
                    row.remove(row.last())
                }
            }
            else {
                writer.writeNext(row as String[])
            }

        }

        assetList.sort { p1, p2 -> p1.name.compareToIgnoreCase(p2.name) }.each() { asset ->
            row = []
            writer.writeNext(row as String[]) //blank row between assets.
            row.add(asset.section.site)
            row.add(asset.section)
            row.add(asset.name)

            if(asset.assetSubItems.size() > 0) {
                asset.assetSubItems.each() { assetSubItem1 ->
                    writeAssetSubItem1(assetSubItem1)
                    row.remove(row.last())
                }
            }
            else {
                writer.writeNext(row as String[])
            }

        }

        writer.close()
        return sw.toString()
    } // end buildAssetTree

} // end class