module Octopress
  class Page

    DEFAULT_OPTIONS = {
      'post_ext' => 'markdown',
      'page_ext' => 'html',
      'post_layout' => 'post',
      'page_layout' => 'page',
      'titlecase' => true
    }

    attr_accessor :site

    def initialize(site, options)
      @site = site
      @config = DEFAULT_OPTIONS.merge(site.config)
      @options = options
      set_default_options

      @front_matter = %w{title date}

      # Ensure title
      #
      @options['title'] ||= ''

      # Ensure a quoted title to avoid YAML parsing issues.
      #
      @options['title'] = "#{@options['title']}"

      @content = options['content'] || content
    end

    def site
      @site
    end

    def write
      if File.exist?(path) && !@options['force']
        abort "File #{relative_path(path)} already exists. Use --force to overwrite."
      end

      dir = File.dirname(path)

      FileUtils.mkdir_p(dir)
      File.open(path, 'w') { |f| f.write(@content) }
      if STDOUT.tty?
        puts "#{@options['write_message']} #{relative_path(path)}"
        
        # If path begins with an underscore the page is probably being added to a collection
        #
        if @options['type'] == 'page'
          print_collection_tip($1) if dir =~ /#{site.source}\/_([^\/]+)/
        end
      else
        puts path
      end
    end

    # Print instructions for setting up a new collection
    #
    def print_collection_tip(collection)
      # If Jekyll is not already configurated for this collection, print instructions
      #
      if !@config['collections'] || !@config['collections'][collection]
        msg = "\nTIP: To create a new '#{collection}' collection, add this to your Jekyll configuration\n"
        msg += "----------------\n"
        msg += "collections:\n  #{collection}:\n    output: true"
        msg += "\n----------------"
        puts msg
      end
    end

    def relative_path(path)
      local = Dir.pwd + '/'
      path.sub(local, '')
    end

    def path
      return @path if @path
      file = @options['path']
      raise "You must specify a path." unless file

      # If path ends with a slash, make it an index
      #
      file += "index" if file =~ /\/$/

      # if path has no extension, add the default extension
      #
      file += ".#{extension}" unless file =~ /\.\w+$/

      @path = File.join(site.source, file)
    end

    def extension
      @options['extension'].sub(/^\./, '')
    end

    def set_default_options
      @options['type']      ||= 'page'
      @options['write_message'] ||= 'New page:'
      @options['layout']      = @config['page_layout']
      if @options['date']
        @options['date']        = convert_date @options['date']
      end
      @options['extension'] ||= @config['page_ext']
      @options['template']  ||= @config['page_template']
    end

    def convert_date(date)
      date ||= 'now'
      if date == 'now'
        date = Time.now.iso8601
      else
        begin
          date = Time.parse(date.to_s, Time.now).iso8601
        rescue => error
          puts 'Could not parse date. Try formatting it like YYYY-MM-DD HH:MM'
          abort error.message
        end
      end
      date
    end

    # Load the user provided or default template for a new post or page.
    #
    def content

      # Handle case where user passes the full path
      #
      file = @options['template'] || default_template

      if file
        file.sub!(/^_templates\//, '')
        file = File.join(site.source, '_templates', file) if file
        if File.exist? file
          parse_template File.open(file).read.encode('UTF-8')
        elsif @options['template']
          abort "No #{@options['type']} template found at #{file}"
        else
          parse_template default_front_matter
        end
      else
        parse_template default_front_matter
      end
    end

    def default_template
      'page'
    end

    # Render Liquid vars in YAML front-matter.
    def parse_template(input)

      if @config['titlecase']
        @options['title'].titlecase!
      end

      vars = @options.dup

      # Allow templates to use slug
      #
      vars['slug'] = title_slug

      # Allow templates to use date fragments
      #
      date = Time.parse(vars['date'] || Time.now.iso8601)
      vars['date'] = date.iso8601
      vars['year'] = date.year
      vars['month'] = date.strftime('%m')
      vars['day'] = date.strftime('%d')
      vars['ymd'] = date.strftime('%Y-%m-%d')

      # If possible only parse the YAML front matter.
      # If YAML front-matter dashes aren't present parse the whole 
      # template and add dashes.
      #

      parsed = if input =~ /\A-{3}\s+(.+?)\s+-{3}(.+)?/m
        input = $1
        content = $2
        input << default_front_matter(input)
      else
        content = ''
      end

      template = Liquid::Template.parse(input)
      "---\n#{template.render(vars).strip}\n---\n#{content}"
    end

    # Ensures front-matter is set with optional arguments
    #
    def default_front_matter(template='')
      @front_matter.dup.map do |k|
        if @options[k] && !(template =~ /#{k}:/)
          "\n#{k}: #{@options[k]}"
        end
      end.join('')
    end

    def date_slug
      @options['date'].split('T')[0]
    end

    # Returns a slug extracted from a path
    #
    def path_slug(path)
      File.basename(path, '.*').scan(/((\d+-){3})?(\S+)/).flatten[2]
    end
    
    # Returns a string which is url compatible.
    #
    def title_slug
      value = (@options['slug'] || @options['title']).downcase
      value.gsub!(/[^\x00-\x7F]/u, '')
      value.gsub!(/(&amp;|&)+/, 'and')
      value.gsub!(/[']+/, '')
      value.gsub!(/\W+/, ' ')
      value.strip!
      value.gsub!(' ', '-')
      value
    end
  end
end
