require 'rubygems'
require 'sinatra'
require 'haml'
require './models'
require 'rack-flash'

set :root, File.dirname(__FILE__)
enable :sessions
use Rack::Flash

class String
  def pluralize(num)
    if num == 1
      return self
    end
    
    case self[-1]
      when 'y'
        self[0..-2] + 'ies'
      when 's'    
        self + "es"
      else
        self + "s"
    end
  end
end

helpers do

  # Format a number with commas for every ^3
  def commify(num)
    num.to_s.reverse.gsub(/(\d\d\d)(?=\d)(?!\d*\.)/,'\1,').reverse
  end
    
  # From http://snippets.dzone.com/posts/show/593
  def to_ordinal(num)
    num = num.to_i
    if (10...20) === num
      "#{num}th"
    else
      g = %w{ th st nd rd th th th th th th }
      a = num.to_s
      c = a[-1..-1].to_i
      a + g[c]
    end
  end

  def format_percent(num)
    sprintf("%.0f%%", num)
  end

  def short_date(d)
    d.strftime("%e %b %Y")
  end

  def long_date(d)
    d.strftime("%e %B %Y")
  end

  # Exception for Labour/Co-operative candidacies
  def party_name(labcoop, party_name)
    labcoop ? "Labour and Co-operative Party" : party_name
  end

end

get '/' do
  if params[:postcode]
    @postcode = params[:postcode].strip.upcase

    if @p = Postcode.get(@postcode)
      # Postcode is valid and in LB Sutton
      @ward = Ward.get(@p.ward_id)
      flash[:notice] = "Postcode <strong>#{@postcode}</strong> is in #{@ward.name} ward"

      if @p.polling_station
        @ps_postcode = Postcode.get(@p.polling_station.postcode)
        flash[:polling_station] = "Your polling station is \
          <a href=\"http://www.openstreetmap.org/?mlat=%s&mlon=%s&zoom=16\">%s, %s, %s</a>" \
          % [ @ps_postcode.lat, @ps_postcode.lng, @p.polling_station.name, \
            @p.polling_station.address, @p.polling_station.postcode]
      end

      redirect "/bodies/sutton-council/elections/2014-05-22/wards/#{@ward.slug}"
    else
      flash.now[:error] = "<strong>#{@postcode}</strong> is not a postcode in Sutton"
    end
  end
  
  # Display a random postcode as default search term
  @random_pc = repository(:default).adapter.select("
    SELECT postcode
    FROM postcodes
    ORDER BY RANDOM()
    LIMIT 1
  ")

  @default_pc = @random_pc[0]

  @future_elections = Election.future
  @past_elections = Election.past
  haml :index
end

get '/bodies/:body/elections/:date' do
  @body = Body.first(:slug => params[:body])
  @election = Election.first(:body => @body, :d => params[:date])
  @elections_for_this_body = Election.all(:body => @body, :order => [:d])
  @total_seats =  Candidacy.sum(:seats, :election => @election)
  @total_votes = Candidacy.sum(:votes, :election => @election)

  # There's got to be a better way to do this, either with SQL or Datamapper
  @total_districts = repository(:default).adapter.select("
    SELECT district_id
    FROM candidacies
    WHERE election_id = ?
    GROUP BY district_id
    ORDER BY district_id
  ", @election.id).count

  @results_by_party = repository(:default).adapter.select("
    SELECT
      p.colour,
      p.name,
      SUM(c.votes) AS votez,
      SUM(c.seats) AS seatz,
      COUNT(*) AS cands

    FROM candidacies c

    LEFT JOIN parties p ON p.id = c.party_id

    WHERE c.election_id = ?

    GROUP BY c.party_id, p.colour, p.name

    ORDER BY seatz DESC, votez DESC
  ", @election.id)

  @results_by_district = repository(:default).adapter.select("
    SELECT
      d.name,
      d.slug AS district_slug,
      SUM(c.seats) AS seats,
      SUM(c.votes) AS votez,
      COUNT(c.id) AS num_candidates
      
    FROM districts d, candidacies c

    WHERE
      c.district_id = d.id
      AND c.election_id = ?

    GROUP BY c.district_id, d.name, d.slug

    ORDER BY d.name
  ", @election.id)
  
  # For elections that haven't yet been held
  @districts_in_this_election = repository(:default).adapter.select("
    SELECT DISTINCT d.name, d.slug

    FROM candidacies c
    LEFT JOIN districts d
    ON c.district_id = d.id

    WHERE c.election_id = ?

    ORDER BY d.name
  ", @election.id)
  haml :electionsummary
end

# get '/bodies/:body/elections/:date/parties/:party' do
#   Not written yet. Show how this party did at this election.
# end

get '/bodies/?' do
  @bodies = Body.all
  haml :bodies
end

get '/bodies/:body/?' do
  @body = Body.first(:slug => params[:body])
  @elections = Election.all(:body => @body, :order => [:d.desc])
  @districts = District.all(:body => @body, :order => [:name])
  haml :body
end

# get '/wards/:slug/postcode/:postcode/?' do
#   @ward = Ward.first(:slug => params[:slug])
#   @postcode = params[:postcode]
#   haml :wards
# end

get '/candidates/:id/?' do
  if @deleted_candidate = DeletedCandidate.get(params[:id])
    redirect "/candidates/#{@deleted_candidate.candidate_id}", 301 # HTTP 301 Moved Permanently
  end

  if @candidate = Candidate.get(params[:id])
    @candidacies = repository(:default).adapter.select("
      SELECT
        e.d,
        c.*,
        p.name AS party_name,
        p.colour AS party_colour,
        b.name AS body_name,
        b.slug AS body_slug,
        b.districts_name AS districts_name,
        d.name AS district_name,
        d.slug AS district_slug
      
      FROM candidacies c
      
      INNER JOIN elections e
      ON c.election_id = e.id
      
      INNER JOIN parties p
      ON c.party_id = p.id
      
      INNER JOIN bodies b
      ON e.body_id = b.id
      
      INNER JOIN districts d
      ON c.district_id = d.id
      
      WHERE c.candidate_id = ?
      
      ORDER BY d
    ", @candidate.id)
  
    haml :candidate
  else
    404
  end
end

get '/candidates/?' do
  @candidates = Candidate.all(:order => [ :surname, :forenames ])
  haml :candidates
end

get '/bodies/:body/elections/:date/:districts_name/:district' do
  @district =     District.first(:slug => params[:district])
  @body =         Body.first(:slug => params[:body])
  @election =     Election.first(:body => @body, :d => params[:date])
  @candidacies =  Candidacy.all(:district => @district, :election => @election, :order => [:votes.desc])
  @total_votes =  Candidacy.sum(:votes, :district => @district, :election => @election)
  @total_candidates =  Candidacy.count(:district => @district, :election => @election)
  @total_seats =  Candidacy.sum(:seats, :district => @district, :election => @election)
  @districts_in_this_election = @election.candidacies.districts
  @poll =         Poll.get(@district.id, @election.id)

  # Postgres: All the columns selected when using GROUP BY must either be aggregate functions or appear in the GROUP BY clause
  @results_by_party = repository(:default).adapter.select("
    SELECT 
      p.name AS party_name,
      p.colour AS party_colour,
      COUNT(c.id) AS num_candidates,
      SUM(c.seats) AS num_seats,
      SUM(c.votes) AS total_votes

    FROM candidacies c
    
    LEFT JOIN parties p
    ON c.party_id = p.id
    
    WHERE c.district_id = ?
    AND c.election_id = ?
    
    GROUP BY p.name, p.colour
    
    ORDER BY total_votes DESC
  ", @district.id, @election.id)
  
  haml :resultsdistrict
end

get '/bodies/:body/:districts_name/:district' do
  @district =     District.first(:slug => params[:district])
  @body =         Body.first(:slug => params[:body])
  haml :district
end

get '/how-the-council-election-works' do
  haml :election
end

get '/how-the-parliament-election-works' do
  haml :parliament
end

# get '/voting' do
#   haml :voting
# end

get '/error' do
  haml :error
end

get '/about' do
  haml :about
end

# get '/aliens' do
#   haml :aliens
# end

get '/polling-stations' do
  @stations = PollingStation.all
  haml :pollingstations
end

not_found do
  haml :not_found
end
