#!/usr/bin/env ruby
## vim: set ft=ruby et sw=2 ts=2:
# encoding: ascii-8bit

require 'tins/go'
include Tins::GO
require 'term/ansicolor'
include Term::ANSIColor

def usage
  puts <<EOT
Usage: #{File.basename($0)} list|create|truncate|insert|replace|search [OPTION] [TABLES]

  Commands are
    - list:       display all tables in the backup

    - create:     display all create table statements in the backup. If TABLES
                  are given, display only those statements.
      -d          if this OPTION is given, first drop the table (if it exists)
                  before creation.

    - truncate:   all tables or the given TABLES, if they exist.

    - insert:     extract insert statements from the backup. If TABLES are given,
                  extract only those statements for these TABLES.
      -t          if this OPTION is given, truncate the table, before starting
                  to insert.

    - replace:    extract insert statements from the backup, and convert them
                  into replace statements. If TABLES are given, extract only
                  those statements for these TABLES.

    - search:     search the insert statements from the backup matching the
                  pattern given with the -p option and output them in a context
                  of the size (as number of characters) given via the -C option.
      -p PATTERN the pattern to match.
      -C NUMBER  the NUMBER of characters for the context.
EOT
  exit 1
end

def bell(n = 1, s = 1)
  n.times do
    STDERR.print "\a\b"
    sleep s
  end
end

cmd = ARGV.shift or usage
opts = go('dtDiC:p:')

STDOUT.sync = true
STDIN.set_encoding 'ascii-8bit'
case cmd
when 'list'
  STDIN.grep(/^CREATE TABLE `([^`]+)` \(/) { puts $1 }
when 'create'
  STDIN.grep(/^CREATE TABLE `([^`]+)` \(/) do |stmt|
    table = $1
    next unless ARGV.empty? or ARGV.member?(table)
    if opts[?d]
      puts "DROP TABLE IF EXISTS `#{table}`;"
      warn "Dropped table #{table}."
    end
    line = stmt
    puts line
    until line =~ /;$/
      line = STDIN.readline
      puts line
    end
    warn "Created table #{table}."
  end
when 'truncate'
  puts "SET FOREIGN_KEY_CHECKS = 0;"
  STDIN.grep(/^CREATE TABLE `([^`]+)` \(/) do |stmt|
    table = $1
    next unless ARGV.empty? or ARGV.member?(table)
    puts "TRUNCATE TABLE `#{table}`;"
    warn "Truncated table #{table}."
  end
when 'insert'
  truncated = {}
  puts "SET FOREIGN_KEY_CHECKS = 0;"
  STDIN.grep(/^INSERT (?:IGNORE )?INTO `(#{ARGV.empty? ? '[^`]+' : ARGV * '|'})`/) do |stmt|
    table = $1
    stmt.sub!(/(^INSERT) (INTO)/, '\1 IGNORE \2') if opts[?i]
    if opts[?t] and not truncated.key?(table)
      puts "TRUNCATE TABLE `#{table}`;"
      truncated[table] = true
      warn "Truncated table #{table}."
    end
    puts stmt
    warn "Inserted into table #{table}."
  end
when 'replace'
  STDIN.grep(/^INSERT INTO `(#{ARGV.empty? ? '[^`]+' : ARGV * '|'})`/) do |stmt|
    table = $1
    puts stmt.sub(/^INSERT INTO `(?:[^`]+)`/, "REPLACE INTO `#{table}`")
    warn "Replaced into table #{table}."
  end
when 'search'
  pattern = opts[?p] or usage
  c = (opts[?C] || 20).to_i
  STDIN.each_line do |l|
    if l =~ /^INSERT INTO `(#{ARGV.empty? ? '[^`]+' : ARGV * '|'})`.*?(#{pattern})/
      table   = $1
      match   = $2
      context = l[ [ $~.begin(2) - c, 0 ].max..($~.end(2) + c) ]
      context.sub!(match, bold(match))
      table.encode!('UTF-8', undef: :replace, invalid: :replace)
      context.encode!('UTF-8', undef: :replace, invalid: :replace)
      puts "#{table}: …#{context}…"
    end
  end
else
  usage
end
bell 3