#!/usr/bin/env ruby
# -*- coding: utf-8 -*-
# alone : web application framework for small embedded systems.
#               Copyright (c) 2009-2010 Inas Co Ltd. All Rights Reserved.
#
# This file is destributed under BSD License. Please read the COPYRIGHT file.
#
# アンケートモデルクラス
#
#(NOTE)
# 通常のこの規模のアプリの場合、このようなモデルクラスは、
# 専用のディレクトリへ入れる方が良いだろう。
# これはサンプルアプリのため、簡易的に、コントローラからの
# 相対ディレクトリで require している。


require 'al_persist_sqlite'
AlPersistSqlite.connect( File.join( AL_CTRL_DIR, "enquete/data/data.db" ) )
class Enquete < AlPersistSqlite
end

#require 'al_persist_postgres'
#AlPersistPostgres.connect( "host=localhost dbname=testdb1 user=testuser1 password=testpass1" )
#class Enquete < AlPersistPostgres
#end


##
# アンケートモデルクラス
#
#@note
# アトリビュートとして、enquete_titleとenquete_questionを使うため、
# ここでは、values は積極的には使わない。
#
class Enquete

  attr_accessor :enquete_title
  attr_accessor :enquete_questions

  ##
  # DB内ステート文字列を日本語に変換
  #
  def self.convert_state_string( s )
    table = { :making=>"作成中", :active=>"回答中", :closed=>"終了" }
    return table[s.to_sym]
  end


  ##
  # POSTされたデータの enquete_id から、オブジェクトを生成して返す
  #
  #@return [Enquete]  アンケートオブジェクト
  #@return [NilClass] エラー時
  #
  def self.create_by_posted_id()
    id = AlForm.get_parameter( AlText.new( 'enquete_id', :validator=>/^\d{8}$/ ) )
    return nil  if ! id

    enq = self.new
    return nil  if ! enq.read( { :enquete_id=>id } )

    return enq
  end


  ##
  # erbテンプレート　設問生成
  #
  #@param [Hash]        q       設問
  #@param [AlForm]      form    フォームオブジェクト
  #@return [String]     テンプレート
  #
  def self.make_template_qa_pair( q, form )
    if q[:answer_pattern] == 'p0'
      r = %Q(<div class="question-note">\n)
      r << %Q(    #{Alone::escape_html_br( q[:question] )}\n)
      r << %Q(</div>\n)
      return r
    end

    r = %Q(<div>\n  <div class="question">\n)
    r << %Q(    Q#{q[:question_number]}：#{Alone::escape_html_br( q[:question] )}\n)
    r << %Q(  </div>\n  <div class="answers">\n)

    case q[:answer_pattern]
    when 'p1','p2','p3'
      r << form.make_tag( "Q#{q[:question_number]}" )

    when 'p4'
      if q[:arrange] == "holizontal"
        no = 1
        q[:answer_text].split(/\r\n|\r|\n/).each do |v|
          r << %Q(#{Alone::escape_html( v )} #{form.make_tag( "Q#{q[:question_number]}-#{no}" )}&nbsp;&nbsp;)
          no += 1
        end
      else
        r << %Q(<table>\n)
        no = 1
        q[:answer_text].split(/\r\n|\r|\n/).each do |v|
          r << "<tr>\n"
          r << "<td>#{Alone::escape_html( v )}</td>\n"
          r << %Q(<td> #{form.make_tag( "Q#{q[:question_number]}-#{no}" )}</td>\n)
          r << "</tr>\n"
          no += 1
        end
        r << %Q(</table>\n)
      end
    end

    r << %Q(  </div>\n)
    r << %Q(</div>\n\n)

    return r
  end


  ##
  # erbテンプレート　回答生成
  #
  #@param [Hash]        q       設問
  #@param [AlForm]      form    フォームオブジェクト
  #@return [String]     テンプレート
  #
  def self.make_template_value( q, form )
    return ""  if q[:answer_pattern] == 'p0'

    r = %Q(<div>\n  <div class="question">\n)
    r << %Q(    Q#{q[:question_number]}：#{Alone::escape_html_br( q[:question] )}\n)
    r << %Q(  </div>\n  <div class="answers">\n)

    case q[:answer_pattern]
    when 'p1','p2','p3'
      r << form.make_value( "Q#{q[:question_number]}" )

    when 'p4'
      if q[:arrange] == "holizontal"
        no = 1
        q[:answer_text].split(/\r\n|\r|\n/).each do |v|
          r << %Q(#{Alone::escape_html( v )} #{form.make_value( "Q#{q[:question_number]}-#{no}" )}&nbsp;&nbsp;)
          no += 1
        end
      else
        r << %Q(<table>\n)
        no = 1
        q[:answer_text].split(/\r\n|\r|\n/).each do |v|
          r << "<tr>\n"
          r << "<td>#{Alone::escape_html( v )}</td>\n"
          r << %Q(<td> #{form.make_value( "Q#{q[:question_number]}-#{no}" )}</td>\n)
          r << "</tr>\n"
          no += 1
        end
        r << %Q(</table>\n)
      end
    end

    r << %Q(  </div>\n)
    r << %Q(</div>\n\n)

    return r
  end



  ##
  # constructor
  #
  #@param  [Hash]       values  プライマリキーを含むHash
  #@note
  # スーパクラスで使うパラメータ（pkey等）は、当モデル内で明らかのため、
  # mifの使用例の時のように、外から与える必要は無い。
  # 
  def initialize( values = nil )
    super( AlRdbw.connect(), 'enquete_title', 'enquete_id' )
    @enquete_title = {}
    @enquete_questions = []

    if values
      read( values )
    end
  end


  ##
  # アンケート読み込み
  #
  #@param  [Hash]       values  プライマリキーを含むHash
  #@return [Boolean]    成功／失敗
  #
  def read( values = nil )
    # アンケートタイトル（本体）の読み込み
    return false if ! super( values )
    @enquete_title = @values

    # 質問データの読み込み
    @enquete_questions = @persist_base.select( 
        "select * from enquete_questions _WHERE_ order by display_order;", 
        { :enquete_id=>@values[:enquete_id] } )

    return true
  end


  ##
  # アンケート新規登録
  #
  #@return [Boolean]    成功／失敗
  #@note
  # 必要性がないので、スーパークラスにある
  # 「引数で情報を与えるセッタとしての機能」はオミットしている。
  #
  def create()
    @enquete_title[:create_date] = Time.now.strftime( "%Y-%m-%d %H:%M:%S" )

    # 主データ登録
    # IDはDB等にたよらずこちらの仕様(数字8桁)で付与したいので、
    # 重なりを考慮して10回までトライする。
    ret = false
    src = ('0'..'9').to_a
    10.times do
      id = ''
      8.times { id += src[rand(src.size)] }
      @enquete_title[:enquete_id] = id

      @persist_base.transaction()
      ret = super( @enquete_title )
      break if ret
      @persist_base.rollback()
    end
    return false if ! ret

    # 質問データ登録
    # サブテーブルへの登録なので、スーパークラスの手助けなしで
    # 自分で登録作業する。
    i = 0
    @enquete_questions.each do |q|
      q[:enquete_id] = @enquete_title[:enquete_id]
      q[:display_order] = i
      ret = @persist_base.insert( 'enquete_questions', q )
      break if ! ret
      i += 1
    end
    if ret
      @persist_base.commit()
    else
      @persist_base.rollback()
    end

    return ret
  end


  ##
  # アンケート更新
  #
  #@return [Boolean]    成功／失敗
  #@note
  # 必要性がないので、スーパークラスにある
  # 「引数で情報を与えるセッタとしての機能」はオミットしている。
  #
  def update()
    @enquete_title[:create_date] = Time.now.strftime( "%Y-%m-%d %H:%M:%S" )

    # 主データ更新
    @persist_base.transaction()
    ret = super( @enquete_title )

    # 質問データ更新
    # (NOTE) 一旦消してから新規登録する
    ret &&= @persist_base.delete( 'enquete_questions', { :enquete_id=>@enquete_title[:enquete_id] } )
    i = 0
    @enquete_questions.each do |q|
      q[:enquete_id] = @enquete_title[:enquete_id]
      q[:display_order] = i
      ret &&= @persist_base.insert( 'enquete_questions', q )
      i += 1
    end

    if ret
      @persist_base.commit()
    else
      @persist_base.rollback()
    end

    return ret
  end


  ##
  # アンケート削除
  #
  #@param  [Hash]       values プライマリキーを含むHash
  #@return [Boolean]    成功／失敗
  #@note
  # valuesには、プライマリキー以外の値が含まれていてもよく、単に無視される。
  #
  def delete( values )
    @persist_base.transaction()
    ret = super( values )
    ret &&= @persist_base.delete( "enquete_questions", { :enquete_id=>values[:enquete_id] } )
    ret &&= @persist_base.delete( "enquete_answers", { :enquete_id=>values[:enquete_id] } )

    if ret
      @persist_base.commit()
    else
      @persist_base.rollback()
    end
    return ret
  end


  ##
  # 自分のアトリビュートから、AlFormオブジェクトを動的生成する。
  #
  def make_form()
    f = AlForm.new
    f.add_widget( AlHidden.new( "enquete_id", :value=>@enquete_title[:enquete_id], :requred=>true ) )

    @enquete_questions.each do |q|
      case q[:answer_pattern]
      when 'p0'
        # nothing to do

      when 'p1'
        if q[:rows] == 1
          f.add_widget( AlText.new( "Q#{q[:question_number]}",
              :filter=>"value.strip",
              :required=>q[:required], :tag_attr=>{"size"=>q[:cols]} ))
        else
          f.add_widget( AlTextArea.new( "Q#{q[:question_number]}",
              :filter=>"value.strip",
              :required=>q[:required], :rows=>q[:rows], :cols=>q[:cols] ))
        end
        
      when 'p2'
        f.add_widget( AlRadios.new( "Q#{q[:question_number]}",
            :options=>make_options(q), :required=>q[:required],
            :separator=> (q[:arrange] == "virtical") ? "<br>" : "" ))

      when 'p3'
        f.add_widget( AlCheckboxes.new( "Q#{q[:question_number]}",
            :options=>make_options(q), :required=>q[:required],
            :max=>q[:max_select], :min=>q[:min_select],
            :separator=> (q[:arrange] == "virtical") ? "<br>" : "" ))

      when 'p4'
        values = q[:answer_text].split(/\r\n|\r|\n/)
        s_no = 1
        values.each do |v|
          case q[:answer_type]
          when 'text'
            f.add_widget( AlText.new( "Q#{q[:question_number]}-#{s_no}",
                :label=>"Q#{q[:question_number]} #{v}", :filter=>"value.strip",
                :tag_attr=>{"size"=>q[:box_size]}, :required=>q[:required] ))

          when 'int'
            f.add_widget( AlInteger.new( "Q#{q[:question_number]}-#{s_no}",
                :label=>"Q#{q[:question_number]} #{v}",
                :tag_attr=>{"size"=>q[:box_size]}, :required=>q[:required] ))

          when 'float'
            f.add_widget( AlFloat.new( "Q#{q[:question_number]}-#{s_no}",
                :label=>"Q#{q[:question_number]} #{v}",
                :tag_attr=>{"size"=>q[:box_size]}, :required=>q[:required] ))
          end
          s_no += 1
        end

      else
        raise "no pattern"
      end
    end
    return f
  end


  ##
  # 回答をデータベースへ保存する。
  #
  #@param [Hash]  answer        回答
  #@return [Boolean]            成功／失敗
  #@note
  # 回答は、make_form()で作ったフォームから送信されたデータを想定している。
  # {:Q1=>"xxx", :Q2-1=>"yyy" } 等
  # answer_idは、厳密に重ならない事をめざしておらず、簡易的なやり方で
  # 重なる可能性を十分小さくする程度のポリシーとしている。
  #
  def save_answer( answer )
    ret = true
    nowtime = Time.now
    create_date = nowtime.to_s
    answer_id = (nowtime.to_i % 1000000) * 100 + rand( 100 )

    @persist_base.transaction()
    answer.each do |k,v|
      next if /^Q(\d+)(-(\d+))?$/ !~ k

      val = { :enquete_id=>answer[:enquete_id], 
        :create_date=>create_date, :answer_id=>answer_id,
        :question_number=>$1, :question_number_sub=>$3, :answer=>v }

      if v.class == Array
        val[:answer] = 1
        v.each do |v1|
          val[:question_number_sub] = v1
          if ! @persist_base.insert( "enquete_answers", val )
            ret = false
          end
        end
      else
        if ! @persist_base.insert( "enquete_answers", val )
          ret = false
        end
      end
    end
    if ret
      @persist_base.commit()
    else
      @persist_base.rollback()
    end

    return ret
  end


  private
  ##
  # フォーム生成用 オプション指示Hashの生成
  # 
  def make_options( q )
    ret = {}
    values = q[:answer_text].split(/\r\n|\r|\n/)
    i = 1
    values.each do |v|
      ret[i.to_s.to_sym] = v
      i += 1
    end
    return ret
  end

end
