/*
 * Copyright 2018 resin.io
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict'

/**
 * @module Etcher.Components.SVGIcon
 */

const _ = require('lodash')
const react = require('react')
const propTypes = require('prop-types')
const path = require('path')
const fs = require('fs')
const analytics = require('../../modules/analytics')
const domParser = new window.DOMParser()

const DEFAULT_SIZE = '40px'

/**
 * @summary Try to parse SVG contents and return it data encoded
 *
 * @param {String} contents - SVG XML contents
 * @returns {String|null}
 *
 * @example
 * const encodedSVG = tryParseSVGContents('<svg><path></path></svg>')
 *
 * img.src = encodedSVG
 */
const tryParseSVGContents = (contents) => {
  const doc = domParser.parseFromString(contents, 'image/svg+xml')
  const parserError = doc.querySelector('parsererror')
  const svg = doc.querySelector('svg')

  if (!parserError && svg) {
    return `data:image/svg+xml,${encodeURIComponent(svg.outerHTML)}`
  }

  return null
}

/* eslint-disable jsdoc/require-example */

/**
 * @summary SVG element that takes both filepaths and file contents
 * @type {Object}
 * @public
 */
class SVGIcon extends react.Component {
  /**
   * @summary Render the SVG
   * @returns {react.Element}
   */
  render () {
    // __dirname behaves strangely inside a Webpack bundle,
    // so we need to provide different base directories
    // depending on whether __dirname is absolute or not,
    // which helps detecting a Webpack bundle.
    // We use global.__dirname inside a Webpack bundle since
    // that's the only way to get the "real" __dirname.
    const baseDirectory = path.isAbsolute(__dirname)
      ? path.join(__dirname, '..')
      // eslint-disable-next-line no-underscore-dangle
      : global.__dirname

    let svgData = ''

    _.find(this.props.contents, (content) => {
      const attempt = tryParseSVGContents(content)

      if (attempt) {
        svgData = attempt
        return true
      }

      return false
    })

    if (!svgData) {
      _.find(this.props.paths, (relativePath) => {
        // This means the path to the icon should be
        // relative to *this directory*.
        // TODO: There might be a way to compute the path
        // relatively to the `index.html`.
        const imagePath = path.join(baseDirectory, 'assets', relativePath)

        const contents = _.attempt(() => {
          return fs.readFileSync(imagePath, {
            encoding: 'utf8'
          })
        })

        if (_.isError(contents)) {
          analytics.logException(contents)
          return false
        }

        const parsed = _.attempt(tryParseSVGContents, contents)

        if (parsed) {
          svgData = parsed
          return true
        }

        return false
      })
    }

    const width = this.props.width || DEFAULT_SIZE
    const height = this.props.height || DEFAULT_SIZE

    return react.createElement('img', {
      className: 'svg-icon',
      style: {
        width,
        height
      },
      src: svgData,
      disabled: this.props.disabled
    })
  }

  /**
   * @summary Cause a re-render due to changed element properties
   * @param {Object} nextProps - the new properties
   */
  componentWillReceiveProps (nextProps) {
    // This will update the element if the properties change
    this.setState(nextProps)
  }
}

SVGIcon.propTypes = {

  /**
   * @summary Paths to SVG files to be tried in succession if any fails
   */
  paths: propTypes.array,

  /**
   * @summary List of embedded SVG contents to be tried in succession if any fails
   */
  contents: propTypes.array,

  /**
   * @summary SVG image width unit
   */
  width: propTypes.string,

  /**
   * @summary SVG image height unit
   */
  height: propTypes.string,

  /**
   * @summary Should the element visually appear grayed out and disabled?
   */
  disabled: propTypes.bool

}

module.exports = SVGIcon
