# encoding: UTF-8
#/* - - - - - - - - - - - - - - - - - - - - -
#
#   Title : WEB Appliances Crest Inc, Web Form Framework
#   Author : Enrique Phillips
#   URL : http://www.wac.bz
#
#- - - - - - - - - - - - - - - - - - - - - */
# the <b>PrintEngine</b> module ...

# http://rarlindseysmash.com/posts/config-and-generators-in-gems
#
#
#
# ARGS                            # first value is default
#
#   collation                     # 'list' | 'record'
#   paper                         # 'A4' | 'label' | ...
#   template                      # '' | 'slip' | 'quote'
#   cmd                           # '' | 'print_label' - a particular method on the printing_class
#
# PRINTPROMPT
#   print[medium]                 # 'display' | 'email' | 'printer' | 'download'
#   print[output_type]            # 'html' | 'pdf' | 'text'
#   print[printer]                # what printer to send output to
#   print[email_to]               # email address
#   print[message]                # body of email
#   print[range]                  # which pages should print
#   print[copies]                 # number of copies
#
# PRINTJOB
#   id,
#   account_id,                   # what 'system' / customer
#   printer_id,                   # on what printer
#   printed_by_id,                # what id has the printed_by entity
#   printed_by_type,              # what entity - like user
#   view_template_path,           # what template
#   name,                         # label the job
#   printing_class,               # what entity provides the data
#   print_driver,                 # what 'driver' - like :pdf, :cab, :zebra, :csv, :html, etc
#   print_format,                 # data collation - like 'record', 'list'
#   state,                        # record the progress
#   paper,                        # what material - like 'label32x42', 'A4', etc
#   copies,                       # number of identical prints
#   print_sql,                    # how to find what will be printed
#   created_at,
#   updated_at
#
#

#
module PrintEngine

  include Exceptions

  #
  #
  # # list_title
  # def list_title
  #   self.respond_to?( "name") ? self.name : "please define list_title on model (#{self.class.to_s})!"
  # end
  #
  # # implement on relevant models
  def find_template template, paper="A4"
    raise 'you have to implement "def find_template(paper)" on your Model'
    # 'label.html.haml'
  end
  #
  #
  #
  # def set_print_defaults options
  #   options[:context]                         ||= ""
  #   options[:print_job][:download]            =   options[:print][:medium]=="download" || false
  #   options[:print_job][:print_driver]        =   spot_the_driver(options)
  #   options[:print_job][:paper]               =   options[:paper] || "A4"
  #   options[:print_job][:view_template_path]  =   find_template( options[:template], options[:paper] ) || params[:print_job][:view_template_path]
  #   options
  # end
  #

  def print_label(options={})

    options[:context]                   ||= ""
    options[:print]                     ||= {}
    options[:print][:collation]           = 'record'
    options[:print][:paper]             ||= 'label'
    options[:print][:output_type]       ||= 'raw'

    options = self.class.set_print_job_defaults [self], options
    options[:print_job][:print_driver]        =   options[:print][:print_driver] || :cab
    options[:print_job][:paper]               =   options[:print][:paper] || "label"
    options[:print_job][:print_format]        =   options[:print][:collation] || 'record'
    options[:print_job][:view_template_path]  =   "stock_items/print/zebra_stock_items_label.html.haml"

    pj = self.class.print [self], options
  end


  # options[:print][:printer_name] = options[:print][:printer_name] || ''
  # options[:user] = options[:user] || current_user


  def print_record(options={})

    options[:context]                   ||= ""
    options[:print]                     ||= {}
    options[:print][:collation]           = 'record'
    options[:print][:paper]             ||= 'A4'
    options[:print][:output_type]       ||= 'pdf'

    options = self.class.set_print_job_defaults [self], options
    options[:print_job][:print_driver]        =   options[:print][:print_driver] || :pdf
    options[:print_job][:paper]               =   options[:print][:paper] || "A4"
    options[:print_job][:print_format]        =   options[:print][:collation] || 'record'
    options[:print_job][:view_template_path]  =   "stock_items/print/slip.html.haml"

    pj = self.class.print [self], options
  end



  def self.included(base)

    #
    def base.print_list(options={})
      raise NoContextFound.new('ViewContext missing!') if options[:context].blank?
      options[:print]                     ||= {}
      options[:print][:collation]           = 'list'
      options[:print][:paper]             ||= 'A4'
      options[:print][:output_type]       ||= 'pdf'

      options = set_print_job_defaults options[:resources], options
      options[:print_job][:print_driver]        =   options[:print][:print_driver] || :pdf
      options[:print_job][:paper]               =   options[:print][:paper] || "A4"
      options[:print_job][:print_format]        =   options[:print][:collation] || 'list'
      options[:print_job][:view_template_path]  =   "stock_items/print/pdf_stock_items_list.html.haml"

      pj = print options[:resources], options
    end

    #
    # def base.default_print_template_path(params,usr)
    #   # TODO 2013-09-02 - select template according to the user/employee
    #   params[:print_job][:view_template_path] || "/test.html.haml"
    # end

    # Creates the PrintJob for any model in need of printing
    # If creating the PrintJob is successful, cycle will be called in print_job.rb
    # and the PrintJob will be enqueued in the delayed_job queue called 'printing'.
    # cycle_error will be called in print_job.rb if it cannot be enqueued sucessfully.
    #   print[medium]                 # 'display' | 'email' | 'printer' | 'download'
    #   print[output_type]            # 'html' | 'pdf' | 'text'
    #   print[printer]                # what printer to send output to
    #   print[email_to]               # email address
    #   print[message]                # body of email
    #   print[range]                  # which pages should print
    #   print[copies]                 # number of copies

    def base.print( resources, params )
      pj = create_print_job( resources, params )
      if Rails.env=='development' or pj.download
        return pj.perform params
      else
        user = User.find(pj.printed_by_id)
        BackgroundPrinterJob.perform_later pj, queue: "printing", account_id: user.account.id, print: params[:print]
        # Delayed::Job.enqueue pj, :queue => 'printing'
        # pj.cycle if Delayed::Job.enqueue pj, :queue => 'printing'
        # If you comment out above line and use below line with pj.perform instead, you thereby surpass delayed_job, and thus don't have to wait for it
        # pj.perform queue: "printing", account_id: user.account.id, print: params[:print]
        return pj
      end
    end

    #
    #
    def base.create_print_job( resources, params )
      params = ActionController::Parameters.new( params) unless params.class == ActionController::Parameters
      pj=PrintJob.create( params_permit(params) )
      raise PrintJobNotCreatedError.new('arguments not permitted!') unless pj
      pj
    end

    def base.params_permit(params)
      params[:print_job].permit(:download, :snap_shot, :account_id, :printer_id, :printed_by_id,
        :printed_by_type, :view_template_path, :printing_class, :name, :print_driver, :print_format, :paper, :copies, :state, :print_sql)
    end

    # Sets print_job defaults using the provided params
    # and looks up any print_job settings to be tweaked
    # as per klass and user
    def base.set_print_job_defaults(resources, options)
      klass = self.to_s #resources.first.class.to_s rescue "class not found!"
      pb = options[:printed_by]
      printer_name = options[:print].delete(:printer) || "default"

      options[:print_job]                       ||= {}
      options[:print_job][:printer_id]          ||= self.default_printer(pb,printer_name,options[:print][:paper]).id
      options[:print_job][:state]               = "drafted"
      options[:print_job][:snap_shot]           ||= false
      options[:print_job][:print_sql]           = set_resource_sql(resources,options)
      options[:print_job][:printing_class]      ||= klass
      options[:print_job][:account_id]          ||= pb.account.id
      options[:print_job][:printed_by_id]       = pb.id
      options[:print_job][:printed_by_type]     = pb.class.to_s
      options[:print_job][:name]                ||= "#{klass} print at #{I18n.l Time.now, format: :short_date }"
      options[:print_job][:copies]              ||= 1
      options[:print_job][:download]            =   options[:print][:medium]=="download" || false
      # options[:print_job][:view_template_path]  =   find_template( options[:print][:template], options[:print][:paper] ) || options[:print_job][:view_template_path]
      #
      # this is what we ultimately return
      options
    end

    #
    # find the best printer for the job
    def base.default_printer usr, printer_name, paper=nil
      if printer_name!="default"
        return Printer.find(printer_name) if printer_name.to_i.to_s == printer_name
        Printer.active.find_by( 'name like ?',  "%#{printer_name.downcase}%") or raise NoPreferredPrintersFound.new('Printer with name not found!')
      else
        raise NoPreferredPrintersFound.new('No printers found!') if usr.printers.empty?
        if paper.nil?
          usr.printers.active.preferred_printer.first or raise NoPreferredPrintersFound.new('No preferred printers found!')
        else
          usr.printers.active.preferred_printer.on_paper(paper).first || usr.printers.on_paper(paper).first or raise NoPreferredPrintersFound.new('No preferred printers found!')
        end
      end
      # # usr.printers.where{ (printownerables.preferred==true) & (printownerables.preferred==true) }
      #
      # # raise PrintJobPrinterNotAvailableError
      # printer || Printer.first
    end

    def base.set_resource_sql(resources,params)
      return resources if resources.class == String && resources.downcase =~ /select/
      raise PrintJobResourceError.new('No items found to print?!') unless resources.respond_to?(:any?) and resources.any?
      params[:print_job][:snap_shot] ? resources.to_yaml : (resources.class==Array ? array_to_arel(resources) : resources.to_sql)
    end

    def base.array_to_arel resources
      arel_instance = Arel::Table.new(resources.first.class.table_name)
      ar_rel = resources.first.class
      ar_rel.where(arel_instance[:id].in(resources.map(&:id)).to_sql).to_sql
    end

  end

end