require 'vpim/vcard'
require 'sqlite3'
require 'set'
require "base64"




class NilClass
  def empty?
    true
  end
  def each
  end
end

class Hash
  def bucket(k,v)
    store(k,fetch(k,[]) << v)
  end
end

class Contact
  attr_accessor :name,:emails,:phones,:addresses

  def vcard
    card = Vpim::Vcard::Maker.make2 do |maker|
      maker.add_name do |name|
        if @name =~ /^\s*(.*?)\s+(\S+)\s*$/
          name.given = $1
          name.family = $2
        else
          name.given = @name
        end
      end

      for addr in @addresses do
        begin
          maker.add_addr do |m|
            m.street = addr
          end
        rescue
          $stderr.print "Adding address failed: " + $! 
        end
      end

      for tel in @phones do
        maker.add_tel tel
      end


      for email in @emails do
        maker.add_email email
      end
    end
    card
  end
end

$db = SQLite3::Database.new( "contacts2.db")

names = {}

for id_name in $db.execute("SELECT _id,display_name FROM raw_contacts") do
  names[id_name[0]] = id_name[1]
end

def prop(mimetype,hash)
  for id_val in $db.execute("select raw_contact_id,data1 from data where mimetype_id=(SELECT _id FROM mimetypes where mimetype=?)",mimetype) do
    id,val = id_val
    hash.bucket id,val
  end
end

phones = {}

prop('vnd.android.cursor.item/phone_v2',phones)

emails = { }

prop('vnd.android.cursor.item/email_v2',emails)

addresses = { }

for m in ["vnd.android.cursor.item/note","vnd.com.google.cursor.item/contact_misc","vnd.android.cursor.item/postal-address_v2"] do
  prop(m,addresses)
end

same_ids = Set.new

phone_same = {}

for k,vs in phones do
  for v in vs do
    b = v.gsub(/\D/,"")[-8..-1]
    phone_same.bucket(b,k)
  end
end

for k,vs in phone_same do
  for a in vs do 
    for b in vs do
      same_ids << [a,b]
    end
  end
end

for ka,va in names
  for kb,vb in names
    if va == vb
      same_ids << [ka,kb]
    end
  end
end

def notice_same(same_ids,hash)
  for ka,vas in hash do
    for kb,vbs in hash do
      for va in vas do
        for vb in vbs do
          if va && va != "" and va == vb
#            warn ka.to_s+" mapped to "+kb.to_s+" because "+va.to_s+" == "+vb.to_s
            same_ids << [ka,kb]
          end
        end
      end
    end
  end
end

notice_same(same_ids,emails)

ids_to_set = { }

for k,v in names do
  ids_to_set[k] = Set.new [k]
end

for a,b in same_ids do
  s = Set.new(ids_to_set[a] | ids_to_set[b])
  for k in s do
    ids_to_set[k] = s
  end
end

m = ids_to_set.values.map { |s| s.to_a }

def mset_inside(ids,set)
  ids.map { |n| set[n] }.reduce([]) do |a,b| 
    a.to_a + b.to_a
  end
end

def mset(ids,set)
  mset_inside(ids,set).uniq.find_all {|s| not(s =~ /^;*$/) }
end

def merge(set,names,phones,emails,addresses)
  mn = set.map { |n| names[n] }.sort { |a,b| b.length <=> a.length }
  mps = mset(set,phones)
  

  ps = {}
  for p in mps do
    b = p.gsub(/\D/,"")[-8..-1]
    ps.bucket(b,p)
  end
  
  clean_phones = []
  for k,v in ps do
    va = v.sort { |a,b| b.length <=> a.length }
    clean_phones << va.first
  end

  final_phones = clean_phones.map { |p| p.sub(/^(44|852|49|972|86)/,"+\\1").sub(/^00/,"+"); }

  mes = mset(set,emails)
  mas = mset(set,addresses)

  vc = Contact.new
  vc.name = mn.first
  vc.emails = mes
  vc.addresses = mas
  vc.phones = final_phones
  puts vc.vcard
end



for set in m.uniq do
  merge(set,names,phones,emails,addresses)
end



