# encoding: UTF-8

module Wingtips
  PHOTO_CREDIT_SIZE = 18
  CODE_SIZE = 30
  BULLET_POINT_SIZE = 40
  HEADLINE_SIZE = 65
  VERY_BIG_SIZE = 80
  ENORMOUS_SIZE = 140

  class Slide
    include Shoes::Highlighter::Markup

    IMAGES_DIRECTORY  = 'images/'
    CODE_DIRECTORY    = 'code/'

    attr_reader :app

    def initialize(app, wingtips_options = {})
      @app = app
      @effects = []
      @background_color = wingtips_options[:background_color]
      after_initialize
    end

    def content
      # implemented by subclasses
    end

    def show
      @main_slot = stack height: app.height do
        background @background_color if @background_color
        content
      end
    end

    def append(&blk)
      @main_slot.append &blk
    end

    # copied from the URL implementation... weird but I want to have
    # classes not methods for this
    def method_missing(method, *args, &blk)
      if app_should_handle_method? method
        app.send(method, *args, &blk)
      else
        super
      end
    end

    def code(string, demo_as_effect = false, options = {}, &block)
      source = source_from string
      source = source.split("\n").map{|line| '     ' + line}.join("\n")
      opts = {size: CODE_SIZE}.merge options
      highlighted_code = para *highlight(source, nil, @app.code_highlighting), opts
      add_demo_as_effect(source, &block) if demo_as_effect
      highlighted_code
    end

    def demo(string, &block)
      source = source_from string
      eval source

      last_app = Shoes.apps.last
      last_app.keypress do |key|
        last_app.quit if key == :control_w
      end

      yield last_app if block_given?
    end

    def source_from(string)
      file_path = find_file_in(string, [CODE_DIRECTORY])

      if file_path
        File.read file_path
      else
        string
      end
    end

    def add_demo_as_effect(string, &block)
      add_effect do demo(string, &block) end
    end

    def add_effect(&effect)
      @effects << effect
    end

    def headline(string)
      text = title '  ' + string, size: HEADLINE_SIZE
      empty_line
      text
    end

    def bullet(string)
      para '    • ' + string, size: BULLET_POINT_SIZE
    end

    def empty_line
      para ' ', size: BULLET_POINT_SIZE
    end

    def image(path, *args)
      app.image(image_path(path), *args)
    end

    def image_path(path)
      path = find_file_in(path, [IMAGES_DIRECTORY])
      File.expand_path(path)
    end

    def fullscreen_image(path)
      img = image path
      height_ratio = height.to_r/img.height
      width_ratio = width.to_r/img.width
      scale_image_by img, [width_ratio, height_ratio].max
      img
    end

    def fully_shown_image(path, additional_height = 0)
      img = image path
      height_ratio = (height - additional_height).to_r/img.height
      width_ratio = width.to_r/img.width
      scale_image_by img, [width_ratio, height_ratio].min
      img
    end

    def scale_image_by(img, ratio)
      img.width = (img.width * ratio).to_i
      img.height = (img.height * ratio).to_i
      img
    end

    def centered_title(string, opts={})
      para string, defaulted_options(opts,
                                     align: 'center',
                                     size: VERY_BIG_SIZE,
                                     margin_top: 50)
    end

    def centered_subtitle(string, opts={})
      para string, defaulted_options(opts,
                                     align: 'center',
                                     size: BULLET_POINT_SIZE,
                                     margin_top: 50)
    end

    def centered_huge_text(string, opts={})
      para string, defaulted_options(opts,
                                     align: 'center',
                                     size: VERY_BIG_SIZE,
                                     margin_top: 50)
    end

    def centered_enormous_text(string, opts={})
      para string, defaulted_options(opts,
                                     align: 'center',
                                     size: ENORMOUS_SIZE,
                                     margin_top: 50)
    end

    def center_horizontally(element)
      element.left = @app.width / 2 - element.width / 2
    end

    def defaulted_options(passed, defaults={})
      results = defaults.merge(passed)
      if results[:vertical_align] == 'center'
        results[:margin_top] = height/2 - 100
      end
      results
    end

    def after_initialize
      # subclasses if an after hook is needed
    end

    def effects_left?
      !@effects.empty?
    end

    def trigger_effect
      effect = @effects.shift
      @main_slot.append &effect
    end

    private
    def find_file_in(file_path, locations)
      candidate_locations = locations.map {|location| location + file_path}
      candidate_locations << file_path
      candidate_locations.find do |candidate_path|
        is_file_path? candidate_path
      end
    end

    def is_file_path?(file_path)
      File.exist? file_path
    end

    def app_should_handle_method? method_name
      !self.respond_to?(method_name) && app.respond_to?(method_name)
    end

  end
end
