class SerializeBlogAttributes < ActiveRecord::Migration
  class BareSetting < ActiveRecord::Base
    include BareMigration
    belongs_to :blog, :class_name => "SerializeBlogAttributes::BareBlog"
  end

  class BareBlog < ActiveRecord::Base
    include BareMigration
    serialize :settings

    class SettingSpec
      attr_accessor :key, :type, :default

      def initialize(key, type, default)
        self.key     = key
        self.type    = type
        self.default = default
      end

      def normalize_value(line)
        if !line || line.value.nil?
          return default
        end
        value = line.value
        case type
        when :boolean
          ! value.match(/^(?:f(?:alse)?|0|)$/)
        when :integer
          value.to_i
        when :string
          value
        else
          YAML::load(value)
        end
      end

      def stringify_value(value)
        case type
        when :boolean
          value.to_s
        when :integer
          value.to_s
        when :string
          value
        else
          value.to_yaml
        end
      end
    end

    def self.fields(key = nil)
      @@fields ||= { }
      key ? @@fields[key.to_sym] : @@fields
    end

    def self.setting(key, type, default)
      fields[key.to_sym] = SettingSpec.new(key.to_sym, type, default)
    end

    # Description
    setting :blog_name,                  :string, 'My Shiny Weblog!'
    setting :blog_subtitle,              :string, ''
    setting :geourl_location,            :string, ''

    # Spam
    setting :sp_global,                  :boolean, false
    setting :sp_article_auto_close,      :integer, 0
    setting :sp_allow_non_ajax_comments, :boolean, true
    setting :sp_url_limit,               :integer, 0

    # Podcasting
    setting :itunes_explicit,            :boolean, false
    setting :itunes_author,              :string, ''
    setting :itunes_subtitle,            :string, ''
    setting :itunes_summary,             :string, ''
    setting :itunes_owner,               :string, ''
    setting :itunes_email,               :string, ''
    setting :itunes_name,                :string, ''
    setting :itunes_copyright,           :string, ''

    # Mostly Behaviour
    setting :text_filter,                :string, ''
    setting :comment_text_filter,        :string, ''
    setting :limit_article_display,      :integer, 10
    setting :limit_rss_display,          :integer, 10
    setting :default_allow_pings,        :boolean, false
    setting :default_allow_comments,     :boolean, true
    setting :link_to_author,             :boolean, false
    setting :show_extended_on_rss,       :boolean, true
    setting :theme,                      :string, 'azure'
    setting :use_gravatar,               :boolean, false
    setting :ping_urls,                  :string, "http://rpc.technorati.com/rpc/ping\nhttp://ping.blo.gs/\nhttp://rpc.weblogs.com/RPC2"
    setting :send_outbound_pings,        :boolean, true
    setting :email_from,                 :string, 'scott@sigkill.org'

    # Jabber config
    setting :jabber_address,             :string, ''
    setting :jabber_password,            :string, ''
  end


  def self.up
    add_column :blogs, :settings, :text
    unless $schema_generator
      begin
        BareSetting.transaction do
          BareBlog.find(:all).each do |blog|
            blog.settings = { }
            BareSetting.with_scope(:find => { :conditions => "blog_id = #{blog.id}"}) do
              BareBlog.fields.each do |key, spec|
                next unless setting = BareSetting.find_by_name(key.to_s, :limit => 1)
                blog.settings[key.to_s] =
                  spec.normalize_value(setting)
                BareSetting.delete(setting.id)
              end
            end
            blog.save
          end
        end
      rescue Exception => e
        remove_column :blogs, :settings rescue nil
        raise e
      end
    end
    drop_table :settings
  end

  def self.down
    begin
      create_settings
      unless $schema_generator
        BareSetting.transaction do
          BareBlog.find(:all).each do |blog|
            blog.settings ||= { }
            BareSetting.with_scope(:create => { :blog_id => blog.id }) do
              BareBlog.fields.each do |key, spec|
                next unless blog.settings.has_key?(key.to_s)
                BareSetting.create!(:name => key.to_s,
                                    :value => spec.stringify_value(blog.settings[key.to_s]))
              end
            end
          end
        end
      end
      remove_column :blogs, :settings rescue nil
    rescue Exception => e
      drop_table :settings rescue nil
      raise e
    end
  end

  def self.create_settings
    create_table :settings do |t|
      t.column :name,     :string, :limit => 255
      t.column :value,    :string, :limit => 255
      t.column :position, :integer
      t.column :blog_id,  :integer
    end
  end
end
