require 'fileutils'
require 'tmpdir'

require 'rabbit/rabbit'

module Rabbit
  module Source
    module Base
      def self.append_features(klass)
        super
        klass.send(:include, GetText)
      end

      attr_reader :base, :tmp_base
      attr_accessor :encoding, :force_modified

      def initialize(encoding, logger)
        @encoding = encoding
        @logger = logger
        @source = nil
        @force_modified = false
        init_base
      end

      def source=(new_source)
        source_type = self.class.name.split("::").last.downcase
        raise ImmutableSourceTypeError.new(source_type)
      end

      def reset
      end

      def read
        if need_read?
          @source = _read
          case @encoding
          when nil
            enc = guess_encoding(@source)
          when Encoding
            enc = @encoding
          else
            enc = Encoding.find(@encoding)
          end

          case enc
          when Encoding::UTF_8, Encoding::ASCII_8BIT
            @source.force_encoding(enc)
          else
            @source = @source.encode(Encoding::UTF_8, enc)
          end
        end
        @source
      end

      def modified?
        @force_modified or need_read?
      end

      def need_read?
        @source.nil?
      end

      def full_path(path)
        if @base_uri.nil? or @base_uri.relative?
          ::File.join(@base, path)
        else
          uri = @base_uri.dup
          uri.path = @base_uri.path + "/" unless /\/$/ =~ @base_uri.path
          (uri + path).to_s
        end
      end

      def open_full_path(path, mode="rb")
        open(full_path(path), mode) do |f|
          yield f
        end
      end

      def old?(current, get_latest_method_name)
        current.nil? or
          (current and __send__(get_latest_method_name) > current)
      end

      def tmp_dir_name
        dir = ::File.join(@tmp_base, TMP_DIR_NAME)
        FileUtils.mkdir_p(dir) unless ::File.exist?(dir)
        dir
      end

      def base=(new_value)
        if new_value.nil?
          init_base
        else
          set_base(new_value)
        end
      end

      def extension
        nil
      end

      private
      def init_base
        set_base(".")
      end

      def set_base(new_value)
        if ::File::ALT_SEPARATOR
          new_value = new_value.gsub(::File::ALT_SEPARATOR, ::File::SEPARATOR)
        end
        @base = new_value
        @base_uri = parse_uri(@base)
        if @base_uri.nil? or @base_uri.scheme.nil?
          @tmp_base = @base
        else
          @tmp_base = "."
        end
        unless ::File.writable?(@tmp_base)
          @tmp_base = Dir.tmpdir
        end
      end

      def parse_uri(str)
        begin
          ::URI.parse(str)
        rescue ::URI::InvalidURIError
          nil
        end
      end

      def guess_encoding(str)
        return Encoding::UTF_8 if utf8_encoding?(str)

        require 'nkf'
        NKF.guess(str)
      end

      def utf8_encoding?(string)
        string.dup.force_encoding(Encoding::UTF_8).valid_encoding?
      end

      def extract_extension(path)
        components = ::File.basename(path).split(/\./)
        return nil if components.size < 2
        components.last
      end
    end

    module LimitAccessInterval
      MINIMUM_ACCESS_TIME = 60

      def initialize(*args, &block)
        update_last_access_time
        super
      end

      def old?(current, get_latest_method_name)
        result = (can_access? and super)
        update_last_access_time if result
        result
      end

      private
      def update_last_access_time
        @last_access_time = Time.now
      end

      def can_access?
        Time.now - @last_access_time > MINIMUM_ACCESS_TIME
      end
    end
  end
end
