module StaticMatic
  class Base
    # Directories generated for a new site setup
    @@base_dirs = %w{
      site/
      site/stylesheets
      site/images
      site/javascripts
      src/
      src/pages/
      src/layouts
      src/stylesheets
    }
  
    # Templates for setup and their location
    @@templates = {
      'application.haml' => 'layouts',
      'application.sass' => 'stylesheets',
      'index.haml' => 'pages'
    }
    
    def initialize(base_dir)
      @base_dir = base_dir
      @src_dir = "#{@base_dir}/src"
      @site_dir = "#{@base_dir}/site"
      @templates_dir = File.dirname(__FILE__) + '/templates'
      @layout = "application"
      @scope = Object.new
      @scope.instance_variable_set("@base_dir", @base_dir)
    end
    
    
    def base_dir
      @base_dir
    end
  
    def run(command)
      valid_commands = ['build', 'setup', 'preview']
      
      if valid_commands.include?(command)
        send(command)
      else
        puts "#{command} is not a valid StaticMatic command"
      end
    end
    
    def build
      build_css
      build_html
    end
  
    def setup
      if !File.exists?(@base_dir)
        Dir.mkdir(@base_dir)
      end
    
      @@base_dirs.each do |directory|
        directory = "#{@base_dir}/#{directory}"
        if !File.exists?(directory)
          Dir.mkdir(directory)
          puts "created #{directory}"
        end
      end
  
      @@templates.each do |template, destination|
        copy_file("#{@templates_dir}/#{template}", "#{@src_dir}/#{destination}")
      end
  
      puts "Done"
    end
    
    def preview
      puts "StaticMatic Preview Server Starting..."
      StaticMatic::Server.start(@base_dir)
    end
  
    def copy_file(from, to)
      FileUtils.cp(from, to)
    end
  
    def save_page(filename, content)
      filename = "#{filename}"
      generate_site_file(filename, 'html', content)
    end
  
    def save_stylesheet(filename, content)
      filename = "stylesheets/#{filename}"
      generate_site_file(filename, 'css', content)
    end
  
    def generate_site_file(filename, extension, content)
      path = File.join(@site_dir,"#{filename}.#{extension}")
      File.open(path, 'w+') do |f|
        f << content
      end
      
      puts "created #{path}"
    end
    
    def source_for_layout
      layout_template = "#{@src_dir}/layouts/#{@layout}.haml"
      if File.exists?(layout_template)
        File.read(layout_template)
      else
        raise StaticMatic::Error.new("", layout_template, "Layout not found")
      end
    end
    
    # Generate html from source file:
    # generate_html("index.haml")
    def generate_html(source_file)
      full_file_path = "#{@src_dir}/pages/#{source_file}.haml"
      begin
        page = Haml::Engine.new(File.read(full_file_path), :locals => {:base_dir => base_dir})
        html = page.render(@scope)
        
        # TODO: DRY this up
        if @scope.instance_variable_get("@layout")
          @layout = @scope.instance_variable_get("@layout")
        end
        
        html
      rescue Haml::Error => haml_error
        raise StaticMatic::Error.new(haml_error.haml_line, full_file_path, haml_error.message)
      end
    end
    
    def generate_html_with_layout(source)
      begin
        generated_content = generate_html(source)
        Haml::Engine.new(source_for_layout, :locals => {:base_dir => base_dir}).render(@scope) { generated_content }
      rescue StaticMatic::Error => staticmatic_error
        # Catch any errors from the actual template - otherwise the error will be assumed to be from the
        # layout
        raise staticmatic_error
      rescue Haml::Error => haml_error
        raise StaticMatic::Error.new(haml_error.haml_line, "Layout: #{@layout}", haml_error.message)
      end
    end

    def generate_css(source)
      full_file_path = "#{@src_dir}/stylesheets/#{source}.sass"
      begin
        stylesheet = Sass::Engine.new(File.read(full_file_path))
        stylesheet.to_css
      rescue Sass::Error => sass_error
        raise StaticMatic::Error.new(sass_error.sass_line, full_file_path, sass_error.message)
      end
    end

  
    def template_exists?(name)
      File.exists?("#{@src_dir}/pages/#{name}.haml") || File.exists?("#{@src_dir}/stylesheets/#{name}.sass")
     end
    
    # Build HTML from the source files
    def build_html
      Dir["#{@src_dir}/pages/**/*.haml"].each do |path|
        template = source_template_from_path(path)
        save_page(template, generate_html_with_layout(template))
        
        # reset the layout for the next page
        @scope.instance_variable_set("@layout", "application")
      end
    end
  
    # Build CSS from the source files
    def build_css
      Dir["#{@src_dir}/stylesheets/**/*.sass"].each do |path|
        template = source_template_from_path(path)
        save_stylesheet(template, generate_css(template))
      end
    end
    
    # Returns a raw template name from a source file path:
    # source_template_from_path("/path/to/site/src/stylesheets/application.sass")  ->  "application"
    def source_template_from_path(path)
      File.basename(path).chomp(File.extname(path))
    end
  
    class << self
      def base_dirs
        @@base_dirs
      end
    end
  end
end