begin
  require 'v8' unless defined?(V8)
rescue LoadError => e
  warn "[WARNING] Please install gem 'therubyracer' to use Less."
  raise e
end

require 'pathname'

module Less
  module JavaScript
    class V8Context

      def self.instance
        return new
      end

      def initialize(globals = nil)
        lock do
          @v8_context = V8::Context.new
          globals.each { |key, val| @v8_context[key] = val } if globals
        end
      end

      def unwrap
        @v8_context
      end

      def exec(&block)
        lock(&block)
      end

      def eval(source, options = nil) # passing options not supported
        source = source.encode('UTF-8') if source.respond_to?(:encode)

        lock do
          @v8_context.eval("(#{source})")
        end
      end

      def call(properties, *args)
        args.last.is_a?(::Hash) ? args.pop : nil # extract_options!

        lock do
          @v8_context.eval(properties).call(*args)
        end
      end

      def method_missing(symbol, *args)
        if @v8_context.respond_to?(symbol)
          @v8_context.send(symbol, *args)
        else
          super
        end
      end

      private

        def lock(&block)
          do_lock(&block)
        rescue V8::JSError => e
          if e.in_javascript?
            js_value = e.value.respond_to?(:'[]')
            name = js_value && e.value["name"]
            constructor = js_value && e.value['constructor']
            if name == "SyntaxError" ||
                ( constructor && constructor.name == "LessError" )
              raise Less::ParseError.new(e, js_value ? e.value : nil)
            end
          # NOTE: less/parser.js :
          #
          #   error = new(LessError)({
          #      index: i,
          #      type: 'Parse',
          #      message: "missing closing `}`",
          #      filename: env.filename
          #   }, env);
          #
          # comes back as value: RuntimeError !
          elsif e.value.to_s =~ /missing opening `\(`/
            raise Less::ParseError.new(e.value.to_s)
          end
          raise Less::Error.new(e)
        end

        def do_lock
          result, exception = nil, nil
          V8::C::Locker() do
            begin
              result = yield
            rescue Exception => e
              exception = e
            end
          end

          if exception
            raise exception
          else
            result
          end
        end

    end
  end
end
