module Cms

  class EngineAwarePathBuilder

    attr_reader :path_subject

    def initialize(model_class_or_content_type_or_model)
      normalize_subject_class(model_class_or_content_type_or_model)
    end

    def subject_class
      if path_subject.instance_of?(Class)
        path_subject
      else
        path_subject.class
      end
    end

    def build_preview(view)
      path = []
      path << engine_name
      path << path_subject
      path
    end

    def build(view)
      path = []
      path << engine(view)
      path << path_subject
      path

    end

    def main_app_model?
      engine_name == "main_app"
    end

    # Determine which 'Engine' this model is from based on the class
    def engine_name
      model_class = subject_class.model_name
      name = EngineAware.module_name(model_class)
      return "main_app" unless name

      begin
        engine = "#{name}::Engine".constantize
      rescue NameError
        # This means there is no Engine for this model, so its from the main Rails App.
        return "main_app"
      end
      engine.engine_name
    end


    def engine_class
      if main_app_model?
        Rails.application
      else
        guess_engine_class(subject_class)
      end
    end

    private

    # Will raise NameError if klass::Engine doesn't exist.
    def guess_engine_class(klass)
      name = EngineAware.module_name(klass)
      "#{name}::Engine".constantize
    end

    # Allows ContentType, Class, or model to be passed.
    def normalize_subject_class(model_class_or_content_type_or_model)
      if model_class_or_content_type_or_model.respond_to? :model_class # i.e. ContentType
        @path_subject = model_class_or_content_type_or_model.model_class
      else # Class or Model
        @path_subject = model_class_or_content_type_or_model
      end
    end

    # Loads the actual engine (which contains the RouteSet.)
    # See http://api.rubyonrails.org/classes/ActionDispatch/Routing/PolymorphicRoutes.html
    def engine(view)
      view.send(engine_name)
    end


  end

  module EngineAware

    # Finds the top level module for a given class.
    # Cms::Thing -> Cms
    # Thing -> nil
    # Whatever::Thing -> Whatever
    #
    # @param [Class] klass
    def self.module_name(klass)
      names = klass.name.split("::")
      return names.first if names.size > 1
      nil
    end
  end
end