require 'delegate'
require 'spreadsheet/format'
require 'spreadsheet/excel/internals'

module Spreadsheet
  module Excel
    module Writer
##
# This class encapsulates everything that is needed to write an XF record.
class Format < DelegateClass Spreadsheet::Format
  include Spreadsheet::Excel::Internals
  def Format.boolean *args
    args.each do |key|
      define_method key do
        @format.send("#{key}?") ? 1 : 0
      end
    end
  end
  def Format.color key, default
    define_method key do
      color_code(@format.send(key) || default)
    end
  end
  boolean :hidden, :locked, :merge_range, :shrink, :text_justlast, :text_wrap,
          :cross_down, :cross_up, :left, :right, :top, :bottom
  color :left_color,       :border
  color :right_color,      :border
  color :top_color,        :border
  color :bottom_color,     :border
  color :diagonal_color,   :border
  color :pattern_fg_color, :pattern_bg
  color :pattern_bg_color, :pattern_bg
  attr_accessor :xf_index
  attr_reader :format
  def initialize writer, workbook, format=workbook.default_format, opts={}
    @opts = { :type => :format }.merge opts
    @format = format
    @writer = writer
    @workbook = workbook
    super format
  end
  def color_code color
    SEDOC_ROLOC[color]
  end
  def font_index
    @writer.font_index @workbook, font.key
  end
  def horizontal_align
    XF_H_ALIGN.fetch @format.horizontal_align, 0
  end
  def num_format
    @writer.number_format_index @workbook, @format.number_format
  end
  def text_direction
    XF_TEXT_DIRECTION.fetch @format.text_direction, 0
  end
  def vertical_align
    XF_V_ALIGN.fetch @format.vertical_align, 2
  end
  def write_op writer, op, *args
    data = args.join
    writer.write [op,data.size].pack("v2")
    writer.write data
  end
  def write_xf writer, type=@opts[:type]
    xf_type = xf_type_prot type
    data = [
      font_index,   # Index to FONT record (➜ 6.43)
      num_format,   # Index to FORMAT record (➜ 6.45)
      xf_type,      #  Bit  Mask    Contents
                    #  2-0  0x0007  XF_TYPE_PROT – XF type, cell protection
                    #               Bit  Mask  Contents
                    #                 0  0x01  1 = Cell is locked
                    #                 1  0x02  1 = Formula is hidden
                    #                 2  0x04  0 = Cell XF; 1 = Style XF
                    # 15-4  0xfff0  Index to parent style XF
                    #               (always 0xfff in style XFs)
      xf_align,     #  Bit  Mask    Contents
                    #  2-0  0x07    XF_HOR_ALIGN – Horizontal alignment
                    #               Value  Horizontal alignment
                    #               0x00   General
                    #               0x01   Left
                    #               0x02   Centred
                    #               0x03   Right
                    #               0x04   Filled
                    #               0x05   Justified (BIFF4-BIFF8X)
                    #               0x06   Centred across selection
                    #                      (BIFF4-BIFF8X)
                    #               0x07   Distributed (BIFF8X)
                    #    3  0x08    1 = Text is wrapped at right border
                    #  6-4  0x70    XF_VERT_ALIGN – Vertical alignment
                    #               Value  Vertical alignment
                    #               0x00   Top
                    #               0x01   Centred
                    #               0x02   Bottom
                    #               0x03   Justified (BIFF5-BIFF8X)
                    #               0x04   Distributed (BIFF8X)
      xf_rotation,  # XF_ROTATION:  Text rotation angle
                    #  Value  Text rotation
                    #      0  Not rotated
                    #   1-90  1 to 90 degrees counterclockwise
                    # 91-180  1 to 90 degrees clockwise
                    #    255  Letters are stacked top-to-bottom,
                    #         but not rotated
      xf_indent,    #  Bit  Mask  Contents
                    #  3-0  0x0f  Indent level
                    #    4  0x10  1 = Shrink content to fit into cell
                    #    5  0x40  1 = Merge Range (djberger)
                    #  7-6  0xc0  Text direction (BIFF8X only)
                    #             0 = According to context
                    #             1 = Left-to-right
                    #             2 = Right-to-left
      xf_used_attr, #  Bit  Mask  Contents
                    #  7-2  0xfc  XF_USED_ATTRIB – Used attributes
                    #             Each bit describes the validity of a
                    #             specific group of attributes. In cell XFs
                    #             a cleared bit means the attributes of the
                    #             parent style XF are used (but only if the
                    #             attributes are valid there), a set bit
                    #             means the attributes of this XF are used.
                    #             In style XFs a cleared bit means the
                    #             attribute setting is valid, a set bit
                    #             means the attribute should be ignored.
                    #             Bit  Mask  Contents
                    #               0  0x01  Flag for number format
                    #               1  0x02  Flag for font
                    #               2  0x04  Flag for horizontal and
                    #                        vertical alignment, text wrap,
                    #                        indentation, orientation,
                    #                        rotation, and text direction
                    #               3  0x08  Flag for border lines
                    #               4  0x10  Flag for background area style
                    #               5  0x20  Flag for cell protection (cell
                    #                        locked and formula hidden)
      xf_borders,   # Cell border lines and background area:
                    #   Bit  Mask        Contents
                    #  3- 0  0x0000000f  Left line style (➜ 3.10)
                    #  7- 4  0x000000f0  Right line style (➜ 3.10)
                    # 11- 8  0x00000f00  Top line style (➜ 3.10)
                    # 15-12  0x0000f000  Bottom line style (➜ 3.10)
                    # 22-16  0x007f0000  Colour index (➜ 6.70)
                    #                    for left line colour
                    # 29-23  0x3f800000  Colour index (➜ 6.70)
                    #                    for right line colour
                    #    30  0x40000000  1 = Diagonal line
                    #                    from top left to right bottom
                    #    31  0x80000000  1 = Diagonal line
                    #                    from bottom left to right top
      xf_brdcolors, #   Bit  Mask        Contents
                    #  6- 0  0x0000007f  Colour index (➜ 6.70)
                    #                    for top line colour
                    # 13- 7  0x00003f80  Colour index (➜ 6.70)
                    #                    for bottom line colour
                    # 20-14  0x001fc000  Colour index (➜ 6.70)
                    #                    for diagonal line colour
                    # 24-21  0x01e00000  Diagonal line style (➜ 3.10)
                    # 31-26  0xfc000000  Fill pattern (➜ 3.11)
      xf_pattern    #   Bit  Mask        Contents
                    #   6-0  0x007f      Colour index (➜ 6.70)
                    #                    for pattern colour
                    #  13-7  0x3f80      Colour index (➜ 6.70)
                    #                    for pattern background
    ]
    write_op writer, 0x00e0, data.pack(binfmt(:xf))
  end
  def xf_align
    align = horizontal_align
    align |= text_wrap      << 3
    align |= vertical_align << 4
    align |= text_justlast  << 7
    align
  end
  def xf_borders
    border  = left
    border |= right       <<  4
    border |= top         <<  8
    border |= bottom      << 12
    border |= left_color  << 16
    border |= right_color << 23
    border |= cross_down  << 30
    border |= cross_up    << 31
    border
  end
  def xf_brdcolors
    border  = top_color
    border |= bottom_color   << 7
    border |= diagonal_color << 14
    border |= pattern        << 26
    border
  end
  def xf_indent
    indent  = indent_level & 0x0f
    indent |= shrink         << 4
    indent |= merge_range    << 5
    indent |= text_direction << 6
    indent
  end
  def xf_pattern
    ptrn  = pattern_fg_color
    ptrn |= pattern_bg_color << 7
    ptrn
  end
  def xf_rotation
    rot = @format.rotation
    if @format.rotation_stacked?
      rot = 255
    elsif rot >= -90 or rotation <= 90
      rot = -rot + 90 if rot < 0
    else
      warn "rotation outside -90..90; rotation set to 0"
      rot = 0
    end
    rot
  end
  def xf_type_prot type
    type = type.to_s.downcase == 'style' ? 0xfff5 : 0x0000
    type |= locked
    type |= hidden << 1
    type
  end
  def xf_used_attr
    atr_num = num_format & 1
    atr_fnt = font_index & 1
    atr_fnt = 1 unless @format.font.color == :text
    atr_alc = 0
    if horizontal_align != 0 \
      || vertical_align != 2 \
      || indent_level > 0 \
      || shrink? || merge_range? || text_wrap?
    then
      atr_alc = 1
    end
    atr_bdr = [top, bottom, left, right, cross_up, cross_down].max
    atr_pat = 0
    if  @format.pattern_fg_color != :border \
      || @format.pattern_bg_color != :pattern_bg \
      || pattern != 0x00
    then
      atr_pat = 1
    end
    atr_prot = hidden? || locked? ? 1 : 0
    attrs  = atr_num
    attrs |= atr_fnt << 1
    attrs |= atr_alc << 2
    attrs |= atr_bdr << 3
    attrs |= atr_pat << 4
    attrs |= atr_prot << 5
    attrs << 2
  end
end
    end
  end
end
