require 'GD'

class EventsController < ApplicationController
  model :event
  scaffold :event

  GUTTER_WIDTH=5
  BORDER_WIDTH=1

  def halfhour(time)
    time.hour * 2 + (time.min / 30).floor
  end

  def make_handle(t)
    DateTime.new(t.year,t.mon,t.day,t.hour,t.min,t.sec).jd
  end

  def list
    @events = Event.find_new

    #organize events by day
    daylist = {}
    for event in @events
      handle = make_handle(event["start"])
      if !daylist[handle]
        daylist[handle] = (0..48).map { |i| EventList.new }
      end

      for i in halfhour(event["start"]) ... halfhour(event["end"])
        #if there's already an event of this type in the queue somewhere, 
        #change the color

        if daylist[handle][i].any? { |e| e.event_type == event.event_type }
          event.event_type ^= 256
        end
        daylist[handle][i].push(event)
      end
    end


    for day in daylist.keys
      theday = daylist[day]
      last = 0
      live_events = []

      for halfhour in 0...theday.length
        if last != 0
          if theday[halfhour] == last && !last.multistart && last.length != 0
            theday[halfhour] = 0
            last.rowspan += 1
          else
            if theday[halfhour]
              theday[halfhour].last_event = last
            end
          end

          #create dummies and handle other pre-render details
          if theday[halfhour] != 0
            #for events in this event.  try to find event in last.  if fail,
            #insert rest of new events into live_events; done
            #if found, insert n blanks, where n is pos in last - pos here
            old_live_events = live_events
            live_events = []
            theday[halfhour].each_with_index { |event, i|
              last_idx = old_live_events.index event
              if last_idx == nil
                live_events.push *theday[halfhour][i,100]
                #make a note when many events start at same time
                if i < theday[halfhour].length - 1
                  theday[halfhour].multistart = theday[halfhour].length - i
                end
                break
              else
                live_events.push *([nil] * (last_idx - i))
                live_events.push event
              end
            }
            clean_dummies live_events
            theday[halfhour].bg = live_events
          end
        else
          live_events = theday[halfhour][0,100]
          theday[halfhour].bg = live_events
        end

        if theday[halfhour] != 0
          last = theday[halfhour]
        end
      end
    end
    @daylist = daylist

  end

  def clean_dummies(events)
    bad = []
    seen = 0
    events.reverse.each_with_index { |e, i| 
      if seen < 2 && e == nil
        bad.push events.length - i - 1
      else
        seen += 1
      end
    }
    bad.each_with_index { |b, i| events.delete_at b  }
  end

  def build_background(colorset)
    bg = GD::Image.new 400, 1
    bg.colorAllocate "#ffcc99"#for debugging
    border = bg.colorAllocate "#000000"
    
    Range.new(0,colorset.length - 3).step(2) { |i|
      idx = i/2
      endx = (idx + 1) * (GUTTER_WIDTH + BORDER_WIDTH) - 1
      color = @@event_colors [Event.type_names[colorset[i].to_i & 0xff]][colorset[i].to_i >> 8]
      bg.line(idx * (GUTTER_WIDTH + BORDER_WIDTH) + BORDER_WIDTH - 1, 0, endx, 0, bg.colorResolve(color))
      if colorset[i + 1].to_i != 0
        bg.line(endx, 0, endx + BORDER_WIDTH - 1, 0, border)
      end
    }
    color = @@event_colors [Event.type_names[colorset[-2].to_i & 0xff]][colorset[-2].to_i >> 8]
    bg.line((colorset.length/2 - 1) * (GUTTER_WIDTH + BORDER_WIDTH) + BORDER_WIDTH - 1, 0, 500, 0, bg.colorResolve(color))
    bg.pngStr
  end

  def build_border(colorset)
    bg = GD::Image.new 400, BORDER_WIDTH
    bg.colorAllocate "#ffcc99"
    colors = colorset.map {|color| bg.colorAllocate "#{color}"}
    border = bg.colorAllocate "#000000"
    
    colors.slice(0...-1).each_with_index {|color, idx| 
      endx = (idx + 1) * (GUTTER_WIDTH + BORDER_WIDTH) - 1
      bg.filledRectangle(idx * (GUTTER_WIDTH + BORDER_WIDTH), 0, endx, BORDER_WIDTH - 1, color)
      bg.filledRectangle(endx, 0, endx, BORDER_WIDTH - 1, border)
    }
    bg.filledRectangle((colors.length - 1) * (GUTTER_WIDTH + BORDER_WIDTH) + BORDER_WIDTH - 1, 0, 500, BORDER_WIDTH - 1, colors [-1])
    bg.pngStr
  end

  @@event_colors = { \
    'social' => ["#ff9999", "#ffcccc"], \
    'affiliated' => ["#9999ff", "#ccccff"], \
    'official' => ["#99ff99", "#ccffcc"], \
    'sig' => ["#ffff99", "#ffffcc"], \
  }

  def background
    colorset = @params["colors"].map { |c| c.to_i }
    send_data(self.build_background(colorset), :type => "image/png", :filename => "colors.png")
  end

  def border
    colorset = @params["colors"].map { |c| 
      if c.to_i == -1
        "#000000" 
      else
        @@event_colors [Event.type_names[c.to_i & 0xff]][c.to_i >> 8]
      end
    }
    send_data(self.build_border(colorset), :type => "image/png")
  end

  def build_border_colors(evlist)
    bordercolors = []
    prev = evlist.last_event

    if prev != nil
      if prev.length > evlist.length
        prev.bg[0,prev.bg.length - 1].each_with_index { |e, i| 
          if e == evlist.bg[i]
            bordercolors.push e.event_type
          else
            bordercolors.push -1
          end
        }

        if prev.bg[-1] == evlist.bg[-1]
          bordercolors.push prev.bg[-1].event_type
        else
          bordercolors.push -1
        end
      else
        evlist.bg[0,evlist.bg.length - 1].each_with_index { |e, i| 
          if e == prev.bg[i]
            bordercolors.push e.event_type
          else
            bordercolors.push -1
          end
        }

        if evlist.bg[-1] == prev.bg[-1]
          bordercolors.push evlist.bg[-1].event_type
        else
          bordercolors.push -1
        end

      end
    else
      bordercolors.push -1
    end
    bordercolors
  end

  def render_multistart(evlist)
    out = %(<td><table cellpadding=0 cellspacing=0 width="100%">)
    #find new events
    new = []
    old = EventList.new
    old.last_event = evlist.last_event
    if evlist.last_event
      for event in evlist
        if evlist.last_event.include? event
          old.push event
        else
          new.push event
        end
      end
    else
      new.push *evlist
    end

    #one-by-one, add old to events.  Oh, fuck, need to do colors! Or not. :)

    old.bg = evlist.bg[0,old.length]

    i = old.length
    for event in new
      old.push event
      old.bg.push evlist.bg[i]

      out += "<tr>" + render_ev(old) + "</tr>\n"

      i += 1
      old_old = old.clone
      old.last_event = old_old

    end

    out += "</table></td>"

    out
  end

  def render_ev(evlist)
    if evlist.multistart
      return render_multistart(evlist)
    end

    if evlist.length == 0 or evlist == nil
      return %(<td class="e">&nbsp;</td>)
    end

    last = nil
    colors = evlist.bg.reverse.map { |event| 
      if event != nil
        last = event.event_type
        [last, 1]
      else
        [last, 0]
      end
    }.reverse.flatten

    bordercolors = build_border_colors(evlist)

    bg = url_for :controller => "events", :action => "background", :colors => colors
    border = url_for :controller => "events", :action => "border", :colors => bordercolors
    padding = (GUTTER_WIDTH + BORDER_WIDTH) * (evlist.length + 1) + 3

    %(<td style="background: url(#{bg}); padding-left: #{padding};" rowspan=#{evlist.rowspan} >
<div style="overflow:hidden; margin-left: -#{padding-BORDER_WIDTH+1};"><img src="#{border}"></div>
<a href="#{url_for :action => 'show', :id => evlist.id}">
#{evlist.title}</a>
</td>)
end


end


class EventList < Array
  attr_writer :rowspan, :last_event, :bg, :multistart
  attr_reader :rowspan, :last_event, :bg, :multistart

  def initialize()
    super()
    @rowspan = 1
  end

  def to_s()
    (self.map {|e| e == nil ? '' : e.title}.join ", ") + ".rowspan = #{@rowspan}"
  end

  def title()
    if !@last_event
      self[-1].title
    else
      @last_event.include?(self[-1]) ? '' : self[-1].title
    end
  end

  def id()
    self[-1].id
  end

end
