# Frozen-string-literal: true
# Copyright: 2012 - 2018 - MIT License
# Encoding: utf-8

require "pathutil"
require "forwardable/extended"
require "jekyll/sanity"
require "sprockets"
require "jekyll"

require_all "patches/*"
require_relative "utils"
require_relative "drop"
require_relative "version"
require_relative "filters"
require_relative "manifest"
require_relative "writer"
require_relative "reader"
require_relative "config"
require_relative "logger"
require_relative "hook"
require_relative "tag"
require_relative "url"
require_all "compressors/*"

module Jekyll
  module Assets
    class Env < Sprockets::Environment
      extend Forwardable::Extended
      include Utils

      # --
      attr_reader :manifest
      attr_accessor :assets_to_write
      attr_reader :asset_config
      attr_reader :jekyll

      # --
      def initialize(jekyll = nil)
        @asset_config = Config.new(jekyll.config["assets"] ||= {})
        Hook.trigger :env, :before_init do |h|
          instance_eval(&h)
        end

        super()
        @jekyll = jekyll
        @assets_to_write = []
        @manifest = Manifest.new(self, in_dest_dir)
        @jekyll.sprockets = self
        @total_time = 0.000000
        @logger = Logger
        @cache = nil

        setup_sources!
        setup_resolver!
        ignore_caches!
        setup_drops!
        precompile!
        copy_raw!

        Hook.trigger :env, :after_init do |h|
          instance_eval(&h)
        end
      end

      # --
      def skip_gzip?
        !asset_config[:gzip]
      end

      # --
      def find_asset(v, *a)
        msg = "Searched for, and rendered #{v} in %<time>s"
        out = Logger.with_timed_logging msg do
          super(v, *a)
        end

        # We keep track.
        @total_time += out[:time]
        out[:result]
      end

      # --
      def find_asset!(v, *a)
        msg = "Searched for, and rendered #{v} in %<time>s"
        out = Logger.with_timed_logging msg do
          super(v, *a)
        end

        # We keep track.
        @total_time += out[:time]
        out[:result]
      end

      # --
      # @note this is configurable with :caching -> :type
      # Create a cache, or a null cache (if no caching) for caching.
      # @note this is configurable with :caching -> :enabled
      # @return [Sprockets::Cache]
      # --
      def cache
        @cache ||= begin
          type = asset_config[:caching][:type]
          enbl = asset_config[:caching][:enabled]
          path = in_cache_dir

          out = Sprockets::Cache::MemoryStore.new if enbl && type == "memory"
          out = Sprockets::Cache::FileStore.new(path) if enbl && type == "file"
          out = Sprockets::Cache::NullStore.new unless enbl
          out = Sprockets::Cache.new(out, Logger)
          clear_cache(out)
          out
        end
      end

      # --
      # @note this does not find the asset.
      # Takes all user assets and turns them into a drop.
      # @return [Hash]
      # --
      def to_liquid_payload
        each_file.each_with_object({}) do |k, h|
          skip, path = false, Pathutil.new(strip_paths(k))
          path.descend do |p|
            skip = p.start_with?("_")
            if skip
              break
            end
          end

          next if skip
          h.update({
            path.to_s => Drop.new(path, {
              jekyll: jekyll,
            }),
          })
        end
      end

      # --
      def write_all
        remove_old_assets unless asset_config[:digest]
        manifest.compile(*assets_to_write); @asset_to_write = []
        Hook.trigger(:env, :after_write) { |h| instance_eval(&h) }
        Logger.debug "took #{format(@total_time.round(2).to_s,
          '%.2f')}s"
      end

      # ---
      def remove_old_assets
        assets_to_write.each do |v|
          in_dest_dir(find_asset!(v).logical_path).rm_f
        end
      end

      # --
      private
      def clear_cache(cache)
        if @manifest.new_manifest? && cache && cache.respond_to?(:clear)
          cache.clear
        end
      rescue Errno::ENOENT
        nil
      end

      # --
      private
      def ignore_caches!
        jekyll.config["exclude"] ||= []
        jekyll.config["exclude"].push(asset_config[:caching][:path])
        jekyll.config["exclude"].uniq!
      end

      # --
      def copy_raw!
        raw_precompiles.each do |v|
          v[:dst].mkdir_p if v[:dst].extname.empty?
          v[:dst].parent.mkdir_p unless v[:dst].extname.empty?
          v[:src].cp(v[:dst])
        end
      end

      # --
      private
      def precompile!
        assets = asset_config[:precompile]
        assets.map do |v|
          v !~ %r!\*! ? @assets_to_write |= [v] : glob_paths(v).each do |sv|
            @assets_to_write |= [sv]
          end
        end

        nil
      end

      # --
      private
      def setup_resolver!
        create_resolver!
        depend_on "jekyll-env"
      end

      # --
      private
      def create_resolver!
        register_dependency_resolver "jekyll-env" do
          ENV["JEKYLL_ENV"]
        end
      end

      # --
      private
      def setup_sources!
        source_dir, cwd = Pathutil.new(jekyll.in_source_dir), Pathutil.cwd
        asset_config["sources"].each do |v|
          path = source_dir.join(v).expand_path
          next unless path.in_path?(cwd)
          unless paths.include?(path)
            append_path path
          end
        end

        paths
      end

      # --
      private
      def setup_drops!
        Jekyll::Hooks.register :site, :pre_render do |_, h|
          h["assets"] = to_liquid_payload
        end
      end

      require_all "plugins/*"
      require_all "plugins/html/defaults/*"
      require_all "plugins/html/*"
      require_relative "context"

      # --
      # @see https://github.com/rails/sprockets/pull/523
      # Registers a few MimeTypes since I don't think
      #   Sprockets will update before we release, so we
      #   need them to be available.
      # --
      Sprockets.register_mime_type "audio/mp4", extensions: %w(.m4a)
      Sprockets.register_mime_type "audio/ogg", extensions: %w(.ogg .oga)
      Sprockets.register_mime_type "audio/flac", extensions: %w(.flac)
      Sprockets.register_mime_type "audio/aac", extensions: %w(.aac)
    end
  end
end