const path = require('path')
const fs = require('fs')
const sharp = require('sharp')
const mkdirp = require('mkdirp')
const crypto = require('crypto')
const slugify = require('slugify')

const configStore = require('./../config.js')

/**
 *  Media
 *
 *  change size, optimize and copy media to assets
 *
 *  @author Björn Hase <me@herr-hase.wtf>
 *  @license http://opensource.org/licenses/MIT The MIT License
 *  @link https://gitea.node001.net/HerrHase/siteomat-webpack-plugin.git
 *
 */

class Media {

    constructor(path = NULL) {
        this._path = path
        this._DIR_ASSETS = '/assets/'
    }

    /**
     *  resolve media 
     *
     *  @param  {string} src
     *  @param  {object} sizes
     *  @param  {Object} [options={}]
     *  @return {string}
     *
     */
    resolve(src, sizes = {}, options = {}) {

        let extension = path.extname(src)
        let sourcePath
        
        const filename = slugify(path.basename(src, extension))

        // check for images in path
        if (this._path && fs.existsSync(configStore.get('source') + this._path + '/' + src)) {
            sourcePath = configStore.get('source') + this._path + '/' + src
        } else {
            sourcePath = configStore.get('source') + '/' + src
        }
        
        // getting sharp
        const process = sharp(sourcePath)

        if (extension === '.gif') {
            process
                .gif({
                    reoptimise: true
                })
        } else {

            // change extension
            extension = '.webp'
            process
                .webp({
                    lossless: true
                })
        }

        // destination
        const destinationPath = this._getDestinationPath(sourcePath)  

        // create files to write
        const filesToWrite = this._getFilesToWrite(filename, extension, destinationPath, sizes)

        // results contains only path as strings
        const results = {}

        // create path if not exists
        if (!fs.existsSync(configStore.get('destination') + destinationPath)) {
            mkdirp.sync(configStore.get('destination') + destinationPath)
        }

        filesToWrite.forEach((file) => {
            if (!fs.existsSync(configStore.get('destination') + file.path)) {
                this._writeFile(file, process, options)
            }

            results[file.name] = file.path
        })

        return this._reduce(results)
    }

    /**
     *  if only full is in results, reduce object to string 
     *
     *  @param  {Object} results
     *  @return {mixed}
     *
     */
    _reduce(results) {
        if (Object.getOwnPropertyNames(results).length === 1) {
            results = results['full']
        }

        return results
    }

    /**
     *  getting files to write 
     * 
     *  @param  {string} src
     *  @param  {string} extension
     *  @param  {Object} sizes
     *  @return {string}
     */
    _getFilesToWrite(filename, extension, destinationPath, sizes) {
        
        const results = []

        // add orginal
        results.push(this._getFile(filename, destinationPath, extension))

        // check for sizes
        if (typeof sizes === 'object' && !Array.isArray(sizes)) {
            results.push(this._getFile(filename, destinationPath, extension, sizes))
        } else if (Array.isArray(sizes)) {
            sizes.forEach((size) => {
                results.push(this._getFile(filename, destinationPath, extension, size))
            })
        }

        return results
    }
 
    /**
     *  write files to destination
     * 
     *  @param  {string} file
     *  @param  {object} process
     *  @param  {Object} options
     */
    _writeFile(file, process, options) {

        // resize without options and with options
        if (file.sizes && Object.getOwnPropertyNames(options).length === 0 && Object.getOwnPropertyNames(file.sizes).length > 0) {
            process.resize(file.sizes)
        } else if (file.sizes && Object.getOwnPropertyNames(options).length > 0 && Object.getOwnPropertyNames(file.sizes).length > 0) {
            process.resize(file.sizes, options)
        }

        process.toFile(configStore.get('destination') + file.path)
    }

    /**
     *  generate destination path from hash of file 
     *
     *  @param {string}
     *  @return {string}
     */
    _getDestinationPath(sourcePath) {
        const hash = crypto.createHash('sha1')
        const file = fs.readFileSync(sourcePath)

        // getting hash from file 
        hash.update(file)

        return this._DIR_ASSETS + hash.digest('hex').match(new RegExp('.{1,8}', 'g')).join('/')
    }

    /**
     *  create file as object, adding path, name with sizes
     * 
     *  @param  {string} filename
     *  @param  {object} destinationPath
     *  @param  {Object} extension
     *  @param  {sizes}  sizes
     *  @return {object}
     */
    _getFile(filename, destinationPath, extension, sizes = undefined) {

        let file = {
            name: ''
        }

        let prefix = ''

        // check for sizes
        if (sizes && sizes.width) {
            file.name += sizes.width
        }

        if (sizes && sizes.height) {
            if (sizes.width) {
                prefix = 'x'
            }

            file.name += prefix + sizes.height
        }

        // create path before name is set to orginal as fallback
        file.path = destinationPath + '/' + filename + file.name + extension

        if (!file.name) {
            file.name = 'full'
        }

        if (sizes) {
            file.sizes = sizes
        }

        return file
    }
}

module.exports = Media
