module Comm
  module Module
    module OutputXlsReal
      require 'spreadsheet'
      
      #== 基本のspreadsheetフォーマッタ
      class NormalFormat < Spreadsheet::Format
        def initialize(opts={})
          super opts.merge(:size => 11,
                           :border => true,
                           :border_color => :black)
        end
      end
      #== テーブルヘッダ用フォーマッタ
      class HeadFormat < NormalFormat
        def initialize
          super(:color => :white,
                :pattern => 1,
                :pattern_fg_color => :gray,
                :horizontal_align => :center)
        end
      end
      
      #== 概要
      #Spreadsheetを生成して<b>行単位</b>で追加します。
      #現状ひたすら追加していくだけ。
      class MyWorkbook
        include Comm::Module::EditSpreadsheet
        include Comm::Const::Print
        attr_accessor :tmp_path
        #== コンストラクタ
        #ワークブックオブジェクトと最初の1シートを生成します
        #_first_sheet_name_ :: 1つめのシート名(省略可)
        def initialize(first_sheet_name = '')
          #ワークブック
          @book = Spreadsheet::Workbook.new
          #最初のシート追加
          add_worksheet(first_sheet_name)
          #フォーマッタ...使いまわしできるように。。
          @head_format = HeadFormat.new
          @normal_format = {'General' => NormalFormat.new(),
                            'Date' => NormalFormat.new({:number_format=>'YYYY/MM/DD'}),
                            'Time' => NormalFormat.new({:number_format=>'YYYY/MM/DD hh:mm:ss'})}
          #行追加時の開始セル（左マージン）
          @cell_mergin = 1
          #初期設定
          @tmp_path = Dir.tmpdir+'/'+rand(100000).to_s+Time.now.to_i.to_s
        end
        
        #== ワークシート追加
        #_name_ :: 追加するシート名(省略可)
        def add_worksheet(name = '')
          #カレントを差し替える前に1つ前のシートのカラム幅調整
          set_column_size(@sheet) if @book.worksheets.size > 0
          #新しくシートを生成してワーキングに
          @sheet = @book.create_worksheet(:name => name.empty? ? "sheet#{@book.worksheets.size+1}" : name)
          #行インデックスを初期化
          @ridx = 1
        end
        
        #== データ行追加
        #_data_array_ :: 各セルに設定するデータの配列
        def add_row(data_array)
          @ridx = add_one_row(@sheet, @ridx, @cell_mergin, data_array, @normal_format)
        end
        
        #== テーブルヘッダ行追加
        #_data_array_ :: 各セルに設定するデータの配列
        def add_head_row(data_array)
          @ridx = add_one_row(@sheet, @ridx, @cell_mergin, data_array, @head_format)
        end
        
        #== 出力
        #_output_ :: 出力対象
        def output(output=@tmp_path)
          set_column_size(@sheet)
          @book.write(output)
          output
        end
        
        def add_one_row(sheet, ridx, cidx, data_array, formatter = nil)
          if cidx > 0
            cidx.times { data_array = data_array.unshift('') }
          end
          sheet.row(ridx).concat(data_array)
          if formatter
            set_format(sheet, ridx, cidx, formatter)
          end
          ridx += 1
        end
        
        def get_file_type
          return FILE_TYPE_INFO[0][:ctype]
        end
        
        def get_file_name(opts={})
          unless opts[:basename]
            opts[:basename] = @mcls ? @mcls.name.underscore : ''
          end
          opts[:uniqstr] ||= Time.now.strftime("%y%m%d-%H%M")
          opts[:extension] ||= '.xls'
          return opts[:basename]+'-'+opts[:uniqstr]+opts[:extension]
        end
        
        def get_tmp_path
          return @tmp_path
        end
      end
      
      class OutputXls < Comm::Module::OutputSpreadSheet
        def initialize(params = {})
          @worksheet_name = params['worksheet_name']
          super
        end
        #== 概要
        #excelを生成します。
        #output_csvと第2引数までは同じです。
        #_hr_ :: データhashの配列
        #_col_info_ :: カラム名の配列
        #_sheet_name_ :: シート名(省略可)
        #正規表現の生成
        REGEX_DATE_STR = Regexp.new('^[12]\d{3}-[01]\d-[0-3]\d$')
        REGEX_SIMPLE_INT = Regexp.new('^\d+$')
        #数値変換（to_i）のために、"0"だけ以外の0から始まる文字列を外しています
        #REGEX_FLOAT = Regexp.new('(?!^0.+$)^[-+]?\d+\.\d+$') => 0.xxが文字列になってしまうのでクビ
        REGEX_FLOAT = Regexp.new('(?!^0[^\.].+$)^[-+]?\d+\.\d+$')
        REGEX_INT = Regexp.new('(?!^0.+$)^[-+]?\d+$')
        REGEX_DISP_NAME_KEY = Regexp.new('(_code|_id)$')
        def output(hr = [], col_info = {}, sheet_name = '')
          begin
            #Excelブックと最初のシートを生成
            sheet_name = @worksheet_name if sheet_name.empty?
            xls = MyWorkbook.new(sheet_name) 
            #テーブルヘッダの生成
            cr = []
            col_info.length.times do |i|
              value = col_info[i.to_s]
              tmp_value = value["v"]
              #listの場合はid/code用のカラムを足す
              if value["t"]=="list" and output_field?(value['f'].strip) and value["a"].blank?
                cr.push(tmp_value+"(ID)")
              end
              cr.push(tmp_value)
            end
            #ヘッダとしてExcelに設定
            xls.add_head_row(cr)
            
            #テーブルデータの生成
            cr.clear
            for hash in hr
              col_info.length.times do |i|
                value = col_info[i.to_s]
#                #テーブル名と連結されている場合があるため、.でsplit
#                field_array = value['f'].split('.')
#                #最終要素の１つ手前までのデータを取る。
#                (field_array.length-1).times do |i|
#                  hash = hash[field_array[i].singularize]
#                end
#                #最終要素のフィールド名
#                tmp_field = field_array[field_array.length-1]
                tmp_field = value['f']
                if tmp_field == nil
                  #とりあえず空文字列でごまかす、、、
                  tmp_value = ''
                else
                  field = tmp_field.strip
                  tmp_value = hash[field]
                  #fieldが数値の時に、値が取れない場合は、数値化してリトライ
                  if tmp_value.blank? && field =~ REGEX_SIMPLE_INT
                    tmp_value = hash[field.to_i]
                  end
                  tmp_value = set_value_with_type(tmp_value)
                end
                sum_up_if_need(field, tmp_value) if @total_temp
                if value["t"]=="list"
                  if output_field?(field) && value["a"].blank?
                    cr.push(tmp_value)
                  end
                  #表示用文字列のキーを作る　hoge_id -> hoge_dn, hoge_code -> hoge_dn
                  key = field.gsub(REGEX_DISP_NAME_KEY,'') + (value["a"].blank? ? '_dn' : ('_'+value["a"]))
                  tmp_value = hash[key].to_s.strip
                  tmp_value = set_value_with_type(tmp_value)
                  cr.push(tmp_value)
                else
                  cr.push(tmp_value)
                end
              end
              #Excelに一行追加
              xls.add_row(cr)
              cr.clear
            end
            if @total_temp
              cr.clear
              col_info.length.times do |i|
                value = col_info[i.to_s]
                field = value['f'].strip
                val = output_sum_ups(field)
                cr.push(val)
                if value["t"]=="list" and output_field?(field)
                  cr.push('')
                end
              end
              xls.add_row(cr)
            end
            xls.output
            treat_additions_on_end(hr, col_info)
            return {:success=>true,
                    :file_name=>xls.get_file_name,
                    :tmp_file_name=>xls.get_tmp_path,
                    :file_type=>xls.get_file_type}.update(add_keys_to_reply)
          end
        end
        
        def set_value_with_type(tmp_value)
          unless tmp_value.blank?
            #Time型はフォーマットを指定
            if tmp_value.instance_of?(Time) || tmp_value.instance_of?(Date)
              #tmp_value = hash[field]
              #tmp_value = hash[field].strftime("%Y/%m/%d %H:%M:%S")
              #...処理なくなってしまった
            else
              tmp_value = tmp_value.to_s.strip
              if tmp_value =~ REGEX_INT
                #整数
                tmp_value = tmp_value.to_i
              elsif tmp_value =~ REGEX_FLOAT
                #小数
                tmp_value = tmp_value.to_f
              elsif tmp_value =~ REGEX_DATE_STR
                #日付文字列
                y,m,d = tmp_value.split('-')
                if Date.valid_date?(y.to_i,m.to_i,d.to_i)
                  tmp_value = Date.parse(tmp_value)
                end
              end
            end
          end
          return tmp_value
        end
      end
    end
  end
end