#
#= spreadsheet処理機能群
# Authors:: Sumiyo Yamamoto
# Copyright:: Copyright (C) OrbusNeich Medical K.K.  2010.
#--
# date        name                   note
# 2010.3.5    Sumiyo Yamamoto        新規作成
#-------------------------------------------------------------------------------
#++
module Comm
  module Module
    #= spreadsheet処理機能モジュール
    # spreadsheet処理に使えるメソッド群を備える。
    #------------------------------------------------------------------------#++
    module EditSpreadsheet
    protected
      #== シート毎初期処理
      #_sheet_ :: シート
      #
      # 戻り値:: 行番号、列番号
      #-----------------------------------------------------------------#++
      def sheet_init(sheet = nil)
        ridx = 0
        cidx = 0
        
        # 見やすさのため、1行目と1列目を余白とする。
        ridx += 1
        sheet.column(cidx).width = 2.6
        cidx += 1
        
        return ridx, cidx
      end

      #== 1セル編集
      #-----------------------------------------------------------------#++
      def edit_one(sheet = nil, ridx = nil, cidx = nil, val = nil, format_opt = {})
        # 値設定
        sheet[ridx, cidx] = val
        
        # フォーマット設定
        unless format_opt.blank?
          if format_opt[:cell_color]
            format_opt[:pattern] = 1
            format_opt[:pattern_fg_color] = format_opt[:cell_color]
            format_opt.delete(:cell_color)
          end
          format = Spreadsheet::Format.new(format_opt)
          sheet.row(ridx).set_format(cidx, format)
        end
        
        ridx += 1
        
        return ridx
      end

      #== 1セル編集（罫線設定）
      #-----------------------------------------------------------------#++
      def edit_one_with_line(
        sheet = nil, ridx = nil, cidx = nil, val = nil, line_opt = nil, add_opt = {}
      )
        # 罫線オプション設定
        all_opt = {:top => true, :bottom => true, :left => true, :right => true}
        format_opt = {:border => true}
        case line_opt
        when :top_left
          format_opt = {:top => true, :left => true}
        when :top_right
          format_opt = {:top => true, :right => true}
        when :bottom_left
          format_opt = {:bottom => true, :left => true}
        when :bottom_right
          format_opt = {:bottom => true, :right => true}
        when :top
          format_opt = {:top => true}
        when :bottom
          format_opt = {:bottom => true}
        when :left
          format_opt = {:left => true}
        when :right
          format_opt = {:right => true}
        when :top_off
          format_opt = all_opt.deep_copy
          format_opt.delete(:top)
        when :bottom_off
          format_opt = all_opt.deep_copy
          format_opt.delete(:bottom)
        when :left_off
          format_opt = all_opt.deep_copy
          format_opt.delete(:left)
        when :right_off
          format_opt = all_opt.deep_copy
          format_opt.delete(:right)
        end
        
        # 追加オプション設定
        unless add_opt.blank?
          format_opt.merge!(add_opt)
        end
        
        # 値・オプション編集
        ridx = edit_one(sheet, ridx, cidx, val, format_opt)
        
        return ridx
      end

      #== タイトル編集処理
      #_sheet_ :: シート
      #_ridx_ :: 行番号
      #_cidx_ :: 列番号
      #_title_ :: タイトル文字列
      #
      # 戻り値:: なし
      #-----------------------------------------------------------------#++
      def edit_title(sheet = nil, ridx = nil, cidx = nil, title = nil)
        # 値・フォーマット編集
        title_format = {:weight => :bold, :size => 16}
        edit_one(sheet, ridx, cidx, title, title_format)
        
        # 行の幅設定
        sheet.row(ridx).height = 20
        
        ridx += 1
        
        return ridx
      end

      #== ヘッダ編集処理
      #_sheet_ :: シート
      #_ridx_ :: 行番号
      #_cidx_ :: 列番号
      #_h_items_ :: ヘッダ情報
      #
      # 戻り値:: 行番号
      #-----------------------------------------------------------------#++
      def edit_table_header(sheet = nil, ridx = nil, cidx = nil, h_items = [])
        # フォーマット
        format_opt = {
          :border => true,
          :cell_color => :yellow
        }
        
        # 編集
        h_items.each do |item|
          # 値・フォーマット編集
          edit_one(sheet, ridx, cidx, item[:name], format_opt)
          
          # 列幅設定
          sheet.column(cidx).width = item[:width]
          
          cidx += 1
        end
        
        ridx += 1
        
        return ridx
      end

      #== 表編集処理
      #_sheet_ :: シート
      #_ridx_ :: 行番号
      #_cidx_ :: 列番号
      #_h_items_ :: ヘッダ情報
      #_records_ :: 表データ
      #
      # 戻り値:: 行番号
      #-----------------------------------------------------------------#++
      def edit_table(sheet = nil, ridx = nil, cidx = nil, h_items = [], records = [])
        #-----------#++
        # ヘッダ部
        #-----------#++
        ridx = edit_table_header(sheet, ridx, cidx, h_items)
        if records.length <= 0
          return ridx, cidx
        end
        
        #-----------#++
        # データ部
        #-----------#++
        # フォーマット
        format_opt = {
          :horizontal_align => :justify,
          :vertical_align => :top
        }
        
        # 編集
        bkup_cidx = cidx
        records.each do |record_items|
          cidx = bkup_cidx
          record_items.each do |val|
            edit_one_with_line(sheet, ridx, cidx, val, nil, format_opt)
            cidx += 1
          end
          ridx += 1
        end
        
        return ridx
      end

      #== 表編集処理（重複レコード整形対応）
      #_sheet_ :: シート
      #_ridx_ :: 行番号
      #_cidx_ :: 列番号
      #_h_items_ :: ヘッダ情報
      #_records_ :: 表データ
      #_dup_cols_ :: 重複レコード整形の対象列
      #_add_format_opts_ :: 追加フォーマットオプション（列毎に指定）
      #
      # 戻り値:: 行番号
      #-----------------------------------------------------------------#++
      def edit_table_dup(
        sheet = nil, ridx = nil, cidx = nil,
        h_items = [], records = [], dup_cols = [], add_format_opts = [])
        #----------#++
        # ヘッダ部
        #----------#++
        ridx = edit_table_header(sheet, ridx, cidx, h_items)
        if records.length <= 0
          return ridx, cidx
        end
        
        #--------------------------#++
        # データ部フォーマット設定
        #--------------------------#++
        comm_format_opt = {
          :horizontal_align => :justify,
          :vertical_align => :top,
          :cell_color => :white
        }
        # 通常フォーマット
        format_opt = {
          :left  => true,
          :right => true,
          :top   => true
        }.merge(comm_format_opt)
        # 重複データフォーマット
        dup_format_opt = {
          :color => :white,
          :left  => true,
          :right => true
        }.merge(comm_format_opt)
        
        #--------------#++
        # データ部編集
        #--------------#++
        row_length = records.length
        col_length = records.first.length
        
        col_length.times do |col_offset|
          # 重複レコードの整形が必要かどうか
          if dup_cols.index(col_offset)
            dup_flag = true
          end
          
          bef = nil
          records.each_with_index do |record_items, row_offset|
            now = record_items[col_offset]
            #-------------------#++
            # フォーマット設定
            #-------------------#++
            if dup_flag && (now == bef)
              now_format_opt = dup_format_opt.deep_copy
            else
              now_format_opt = format_opt.deep_copy
            end
            
            # 最終行は bottom に罫線を追加
            if row_offset == (row_length - 1)
              now_format_opt.merge!({:bottom => true})
            end
            
            # 追加オプション
            unless (add_format_opts[col_offset]).blank?
              now_format_opt.merge!(add_format_opts[col_offset])
            end
            
            #-----------#
            # 1セル編集
            #-----------#
            now_ridx = (ridx + row_offset)
            now_cidx = (cidx + col_offset)
            edit_one(sheet, now_ridx, now_cidx, now, now_format_opt)
            
            bef = now
          end #-- records.each_with_index do #++
        end #-- col_length.times do #++
        
        return (ridx + row_length)
      end

      #== Box編集処理
      #_sheet_ :: シート
      #_ridx_ :: 行番号
      #_cidx_ :: 列番号
      #_rnum_ :: 行数
      #_cnum_ :: 列数
      #_add_opt_ :: 追加フォーマットオプション
      #
      # 戻り値:: 行番号
      #-----------------------------------------------------------------#++
      def edit_box(sheet = nil, ridx = nil, cidx = nil, rnum = nil, cnum = nil, add_opt = {})
        
        rnum.times do |roffset|
          cnum.times do |coffset|
            #-------------------#++
            # フォーマット設定
            #-------------------#++
            now_format_opt = {}
            # 罫線オプション
            if roffset == 0
              now_format_opt.merge!({:top => true})
            elsif roffset == (rnum - 1)
              now_format_opt.merge!({:bottom => true})
            end
            if coffset == 0
              now_format_opt.merge!({:left => true})
            elsif coffset == (cnum - 1)
              now_format_opt.merge!({:right => true})
            end
            # 追加オプション
            unless add_opt.blank?
              now_format_opt.merge!(add_opt)
            end
            
            #----------------------#++
            # 値・フォーマット編集
            #----------------------#++
            edit_one(sheet, (ridx + roffset), (cidx + coffset), nil, now_format_opt)
          end
        end
        
        return (ridx + rnum)
      end

      #== 1列Box編集処理
      #_sheet_ :: シート
      #_ridx_ :: 行番号
      #_cidx_ :: 列番号
      #_records_ :: 行データ情報
      #
      # 戻り値:: 行番号
      #-----------------------------------------------------------------#++
      def edit_one_col_box(sheet = nil, ridx = nil, cidx = nil, records = [])
        #--------------#++
        # フォーマット
        #--------------#++
        # 中間行フォーマットオプション
        mid_opt = {
          :left  => true,
          :right => true
        }
        # 先頭行フォーマットオプション
        first_opt = {
          :top  => true
        }.merge(mid_opt)
        # 末尾行フォーマットオプション
        last_opt = {
          :bottom  => true
        }.merge(mid_opt)
        
        #----------#++
        # 編集処理
        #----------#++
        last_idx = (records.length - 1)
        records.each_with_index do |r, idx|
          # フォーマットオプション設定
          if idx == 0
            format_opt = first_opt.deep_copy
          elsif idx == last_idx
            format_opt = last_opt.deep_copy
          else
            format_opt = mid_opt.deep_copy
          end
          
          if r[:format]
            format_opt.merge!(r[:format])
          end
          
          # 値・フォーマット設定
          ridx = edit_one(sheet, ridx, cidx, r[:val], format_opt)
        end
        
        return ridx
      end
      
      #== 1列挿入処理
      #シートにdata_arrayを挿入します。
      #cidxでdata_array開始位置の設定します。
      #_sheet_ :: シート
      #_ridx_ :: 行番号
      #_cidx_ :: 開始セル
      #_data_array_ :: 行データ情報
      #_formatter_ :: フォーマッタ(省略可)
      #
      # 戻り値:: 行番号
      def add_one_row(sheet, ridx, cidx, data_array, formatter = nil)
        if cidx > 0
          cidx.times {data_arraya_array.insert(0, '')}
        end
        sheet.row(ridx).concat(data_array)
        set_format(sheet, ridx, cidx, formatter) if formatter
        ridx += 1
      end
      
      #== フォーマッタ設定処理
      #行の全セルにフォーマッタを設定します
      #_sheet_ :: シート
      #_ridx_ :: 行番号  
      #_cidx_ :: 開始セル
      #_formatter :: フォーマッタ
      def set_format(sheet, ridx, cidx,formatter)    
        row = sheet.row(ridx)
        if formatter.instance_of?(Hash)
          for i in cidx..row.length-1
            #row.set_format(i, formatter['General'])
            if formatter[row[i].class.to_s].blank?
              row.set_format(i, formatter['General'])
            else
              row.set_format(i, formatter[row[i].class.to_s])
            end
          end
        else
          for i in cidx..row.length-1
            row.set_format(i, formatter)
          end
        end
      end
      
      #== カラムサイズ設定
      #1シートの全データ入力後にカラムサイズを調整・設定します
      #_sheet_ :: シート
      #_inspect_opt :: サンプリング開始行、終了行インデックスを:startと:endで指定します。初期値は1,10です
      def set_column_size(sheet, inspects={})
        #inspects = {:start => 1, :end => 10}.merge!(inspect_opt)
        inspects[:start] ||= 1
        inspects[:end] ||= 10
        size_array = []
        #指定行を評価してカラムごとの最長の文字数を採用
        for i in inspects[:start]..inspects[:end] do
          sheet.row(i).each_with_index do |cell, j|
            cell = cell.to_s
            unless size_array[j]
              size_array[j] = cell.size
            else
              size_array[j] = cell.size if cell.size > size_array[j]
            end
          end
        end
        size_array.length.times do |i|
          #2はマージンです（てきとう）
          sheet.column(i).width = size_array[i] + 2
        end
      end
    end
  end
end
