
require 'rubygems'

require 'sinatra'
require 'json'
require 'pp'

# reloader
require 'sinatra/base'
require 'sinatra/reloader'  # gem install sinatra-reloader

require 'models/zipcode'

set :public, File.dirname(__FILE__) + '/public'

def trace_print(s)
  pp s
end

get '/' do
  redirect '/index.html'
end

get '/zipcode.json' do

  trace_print '/zipcode.json'
  trace_print params

  p_page = params[:page].to_i
  p_rows = params[:rows].to_i
  p_sort_index = params[:sidx]
  p_sort_order = params[:sord]
  p_search = params[:_search]
  p_filters = params[:filters]
  # p_s_field = params[:searchField]
  # p_s_str = params[:searchString]
  # p_s_oper = params[:searchOper]

  result_all = nil
  total_count = 0
  if p_filters
    q = {}
    result_all = Zipcode.all
    filters = JSON.parse(p_filters)
    filters['rules'].each{|r|
      if (r['data'] != '')
        fieldname = r['field']
        pat = Regexp.new(r['data'])
        result_all = result_all.where( fieldname.to_sym => pat)
        q[fieldname.to_sym] = pat
      end
    } if filters and filters['rules'] and filters['rules'].size > 0
  else
    result_all = Zipcode.all
  end

  total_count = result_all.count

  p_sort_index = 'first_name' if p_sort_index == nil
  p_sort_order = 'asc' if p_sort_order == nil
  p_sort_order = 'ascending' if p_sort_order == 'asc'
  p_sort_order = 'descending' if p_sort_order == 'desc'

  p_rows = 1 if p_rows < 1
  total_page = (1.0 * total_count / p_rows).ceil
  p_page = 1 if p_page < 1
  p_page = total_page if total_page < p_page
  p_page = 1 if p_page < 1

  paged = result_all.only(:field02, :field06, :field07, :field08).order_by([p_sort_index, p_sort_order]).paginate(:page => p_page, :per_page => p_rows)

  rows = paged.collect { |i|
    { :id => i._id,
      :cell => [i.field02, i.field06, i.field07, i.field08]
    }
  }

  body = {}
  content_type :json
  body[:page] = p_page
  body[:total] = total_page
  body[:records] = total_count
  body[:rows] = rows

  body.to_json
end

def map_reduce_area(conds)

  m =<<-EOT
function(){
  emit(this.field06, {
    point: 1
  });
}
  EOT

  r =<<-EOT
function(key, values){
  var sum = 0;
  values.forEach(function(value){
    sum += value.point;
  });
  return {
    point: sum
  };
}
  EOT

  Zipcode.collection.map_reduce(m, r,
    {:out => "x", :query => conds}).find().sort('value.point', :desc).to_a

end

def map_reduce_area2(conds)

  m =<<-EOT
function(){
  emit(this.field07, {
    point: 1
  });
}
  EOT

  r =<<-EOT
function(key, values){
  var sum = 0;
  values.forEach(function(value){
    sum += value.point;
  });
  return {
    point: sum
  };
}
  EOT

  Zipcode.collection.map_reduce(m, r,
    {:out => "x", :query => conds}).find().sort('value.point', :desc).to_a

end

def get_search_condition(query)
  cond = {}
  return cond if query == nil

  query.keys.each{|k|
    str = query[k]
    if (str != nil and str != '' and str != 'undefined')
      pat = Regexp.new(query[k])
      cond[k.to_sym] = pat
    end
  }
  cond
end

post '/list/area' do
  trace_print 'post /list/area'
  trace_print params

  # request.body.rewind  # 既に読まれているときのため
  # data = request.body.read
  # pp data

  body = {}
  items = nil
  success = false

  begin
    conds = get_search_condition(params)
    items = get_from_cache(conds.to_s)

    if items == nil
      items = []
      areas = map_reduce_area(conds)
      areas.each {|d|
        items << {:item => "#{d['_id']} (#{d['value']['point'].to_i})", :value => d['_id']}
      }
    end

    success = true
    set_to_cache(items, conds.to_s)

  rescue => e
    p e.backtrace.join('\n')
    items = "#{e}"
    success = false
  end

  pp items
  content_type :json
  body[:success] = success
  body[:message] = items
  body.to_json
end

post '/list/area2' do
  trace_print 'post /list/area2'
  trace_print params

  body = {}
  items = []
  success = false

  begin
    conds = get_search_condition(params)
    if conds.size > 0
      areas = map_reduce_area2(conds)
      areas.each {|d|
        items << {:item => "#{d['_id']} (#{d['value']['point'].to_i})", :value => d['_id']}
      }
    end
    success = true

  rescue => e
    p e.backtrace.join('\n')
    items = "#{e}"
    success = false
  end

  pp items
  content_type :json
  body[:success] = success
  body[:message] = items
  body.to_json
end

@@AREAS_INIT = nil
def get_from_cache(cond_str)
  return @@AREAS_INIT if cond_str == ''
  return nil
end

def set_to_cache(items, cond_str)
  @@AREAS_INIT = items if cond_str == '' and @@AREAS_INIT == nil
end