require 'volt/server/html_parser/view_parser'
require 'volt/tasks/task'
require 'volt/server/template_handlers/preprocessors'


# Initialize with the path to a component and returns all the front-end
# setup code (for controllers, models, views, and routes)
module Volt
  class BasicHandler
    def call(file_contents)
      file_contents
    end
  end

  class ComponentTemplates
    extend ComponentTemplates::Preprocessors

    # client is if we are generating for the client or backend
    def initialize(component_path, component_name, client = true)
      @component_path = component_path
      @component_name = component_name
      @client         = client
    end

    def initializer_code
      if @client
        generate_initializers_code
      else
        ''
      end
    end

    def component_code
      code = ''

      code << generate_routes_code + generate_view_code
      if @client
        # On the backend, we just need the views
        code << generate_controller_code + generate_model_code +
                generate_tasks_code
      end

      code
    end

    def app_reference
      if @client
        'Volt.current_app'
      else
        'volt_app'
      end
    end

    def generate_view_code
      code = ''
      views_path = "#{@component_path}/views/"

      exts = Preprocessors.extensions

      # Load all templates in the folder
      Dir["#{views_path}*/*.{#{exts.join(',')}}"].sort.each do |view_path|
        if @client
          require_path = view_path.split('/')[-4..-1].join('/').gsub(/[.][^.]*$/, '')

          # On the client side, we can just require the file and let sprockets
          # handle things.
          code << "\nrequire '#{require_path}'\n"
        else
          # On the sever side, we eval the compiled code
          path_parts = view_path.scan(/([^\/]+)\/([^\/]+)\/[^\/]+\/([^\/]+)[.](html|email)$/)
          component_name, controller_name, view, _ = path_parts[0]

          # file extension
          format = File.extname(view_path).downcase.delete('.').to_sym

          # Get the path for the template, supports templates in folders
          template_path = view_path[views_path.size..-1].gsub(/[.](#{exts.join('|')})$/, '')
          template_path = "#{@component_name}/#{template_path}"

          html = File.read(view_path)

          if handler = ComponentTemplates.handler_for_extension(format)
            html = handler.call(html)

            code << ViewParser.new(html, template_path).code(app_reference)
          end
        end
      end

      code
    end

    def generate_controller_code
      code             = ''
      controllers_path = "#{@component_path}/controllers/"
      views_path = "#{@component_path}/views/"

      # Controllers are optional, specifying a view folder is enough to auto
      # generate the controller.

      implicit_controllers = Dir["#{views_path}*"].sort.map do |path|
        # remove the /views/ folder and add _controller.rb
        path.split('/').tap {|v| v[-2] = 'controllers' }.join('/') + '_controller.rb'
      end
      explicit_controllers = Dir["#{controllers_path}*_controller.rb"].sort

      controllers = (implicit_controllers + explicit_controllers).uniq

      controllers.each do |path|
        if File.exists?(path)
          code << "\nrequire '#{localize_path(path)}'\n"
        else
          # parts = path.scan(/([^\/]+)\/controllers\/([^\/]+)_controller[.]rb$/)
          # component, controller = parts[0]

          # # Generate a blank controller.  (We need to actually generate one so
          # # the Template can be attached to it for template inheritance)
          # code << "\nmodule #{component.camelize}\n  class #{controller.camelize} < Volt::ModelController\n  end\nend\n"
        end
      end

      code
    end

    def generate_model_code
      code        = ''
      models_path = "#{@component_path}/models/"

      Dir["#{models_path}*.rb"].sort.each do |model_path|
        # code << File.read(model_path) + "\n\n"

        # model_name = model_path.match(/([^\/]+)[.]rb$/)[1]
        if File.exists?(model_path)
          code << "require '#{localize_path(model_path)}'\n"
        end
      end

      code
    end

    def generate_routes_code
      code        = ''
      routes_path = "#{@component_path}/config/routes.rb"

      if File.exist?(routes_path)
        code << "#{app_reference}.add_routes do\n"
        code << "\n" + File.read(routes_path) + "\n"
        code << "end\n\n"
      end

      code
    end

    def generate_tasks_code
      Task.known_handlers.map do |handler|
        # Split into modules and class
        klass_parts = handler.name.split('::')

        # Start with the inner class
        parts = ["class #{klass_parts.pop} < Volt::Task; end"]

        # Work backwards on the modules
        klass_parts.reverse_each do |kpart|
          parts.unshift("module #{kpart}")
          parts.push('end')
        end

        # Combine the parts
        parts.join("\n")
      end.join "\n" # combine all into one string
    end

    def generate_initializers_code
      paths = Dir["#{@component_path}/config/initializers/*.rb"]
      paths += Dir["#{@component_path}/config/initializers/client/*.rb"]

      code = "\n" + paths.map { |path| "require '#{localize_path(path)}'" }.join("\n")

      code + "\n\n"
    end

    private

  # Takes a full path and returns the localized version so opal supprots it
    def localize_path(path)
      cpath_size = @component_path.size
      return @component_name + path[cpath_size..-1]
    end


  end
end