class Baza::Driver::Mysql < Baza::MysqlBaseDriver
  AutoAutoloader.autoload_sub_classes(self, __FILE__)

  attr_reader :conn

  def self.from_object(args)
    raise "Mysql does not support auth extraction" if args[:object].class.name == "Mysql"
  end

  def initialize(db)
    super

    @opts = @db.opts

    require "monitor"
    @mutex = Monitor.new

    if db.opts[:conn]
      @conn = db.opts[:conn]
    else
      if @opts[:encoding]
        @encoding = @opts[:encoding]
      else
        @encoding = "utf8"
      end

      if @db.opts.key?(:port)
        @port = @db.opts[:port].to_i
      else
        @port = 3306
      end

      reconnect
    end
  end

  # Cleans the wref-map holding the tables.
  def clean
    tables.clean if tables
  end

  # Respawns the connection to the MySQL-database.
  def reconnect
    @mutex.synchronize do
      require "mysql" unless ::Object.const_defined?(:Mysql)
      @conn = ::Mysql.real_connect(@db.opts[:host], @db.opts[:user], @db.opts[:pass], @db.opts[:db], @port)
      query("SET NAMES '#{esc(@encoding)}'") if @encoding
    end
  end

  # Executes a query and returns the result.
  def query(str)
    str = str.to_s
    str = str.force_encoding("UTF-8") if @encoding == "utf8" && str.respond_to?(:force_encoding)
    tries = 0

    begin
      tries += 1
      @mutex.synchronize do
        return Baza::Driver::Mysql::Result.new(self, @conn.query(str))
      end
    rescue => e
      if tries <= 3
        if e.message == "MySQL server has gone away" || e.message == "closed MySQL connection" || e.message == "Can't connect to local MySQL server through socket"
          sleep 0.5
          reconnect
          retry
        elsif e.message.include?("No operations allowed after connection closed") || e.message == "This connection is still waiting for a result, try again once you have the result" || e.message == "Lock wait timeout exceeded; try restarting transaction"
          reconnect
          retry
        end
      end

      raise e
    end
  end

  # Executes an unbuffered query and returns the result that can be used to access the data.
  def query_ubuf(str)
    @mutex.synchronize do
      @conn.query_with_result = false
      return Baza::Driver::Mysql::UnbufferedResult.new(@conn, @opts, @conn.query(str))
    end
  end

  # Escapes a string to be safe to use in a query.
  def escape_alternative(string)
    @conn.escape_string(string.to_s)
  end

  # Closes the connection threadsafe.
  def close
    @mutex.synchronize { @conn.close }
  end

  # Destroyes the connection.
  def destroy
    @conn = nil
    @db = nil
    @mutex = nil
    @subtype = nil
    @encoding = nil
    @query_args = nil
    @port = nil
  end

  def supports_multiple_databases?
    true
  end
end
