package spreadsheet

import (
	"go.devnw.com/ooxml"
	"go.devnw.com/ooxml/schema/soo/sml"
)

// CellStyle is a formatting style for a cell.  CellStyles are spreadsheet global
// and can be applied to cells across sheets.
type CellStyle struct {
	wb  *Workbook
	xf  *sml.CT_Xf
	xfs *sml.CT_CellXfs
}

// IsEmpty checks if the cell style contains nothing.
func (cs CellStyle) IsEmpty() bool {
	return cs.wb == nil || cs.xf == nil || cs.xfs == nil || cs.xfs.Xf == nil
}

// HasNumberFormat returns true if the cell style has a number format applied.
func (cs CellStyle) HasNumberFormat() bool {
	return cs.xf.NumFmtIdAttr != nil && cs.xf.ApplyNumberFormatAttr != nil &&
		*cs.xf.ApplyNumberFormatAttr
}

// NumberFormat returns the number format that the cell style uses, or zero if
// it is not set.
func (cs CellStyle) NumberFormat() uint32 {
	if cs.xf.NumFmtIdAttr == nil {
		return 0
	}
	return *cs.xf.NumFmtIdAttr
}

// ClearNumberFormat removes any number formatting from the style.
func (cs CellStyle) ClearNumberFormat() {
	cs.xf.NumFmtIdAttr = nil
	cs.xf.ApplyNumberFormatAttr = nil
}

// SetNumberFormatStandard sets the format based off of the ECMA 376 standard formats.  These
// formats are standardized and don't need to be defined in the styles.
func (cs CellStyle) SetNumberFormatStandard(s StandardFormat) {
	cs.xf.NumFmtIdAttr = office.Uint32(uint32(s))
	cs.xf.ApplyNumberFormatAttr = office.Bool(true)
}

func (cs CellStyle) SetNumberFormat(s string) {
	nf := cs.wb.StyleSheet.AddNumberFormat()
	nf.SetFormat(s)
	cs.xf.ApplyNumberFormatAttr = office.Bool(true)
	cs.xf.NumFmtIdAttr = office.Uint32(nf.ID())
}

// Wrapped returns true if the cell will wrap text.
func (cs CellStyle) Wrapped() bool {
	if cs.xf.Alignment == nil {
		return false
	}
	if cs.xf.Alignment.WrapTextAttr == nil {
		return false
	}
	return *cs.xf.Alignment.WrapTextAttr
}

// SetWrapped configures the cell to wrap text.
func (cs CellStyle) SetWrapped(b bool) {
	if cs.xf.Alignment == nil {
		cs.xf.Alignment = sml.NewCT_CellAlignment()
	}
	if !b {
		cs.xf.Alignment.WrapTextAttr = nil
	} else {
		cs.xf.Alignment.WrapTextAttr = office.Bool(true)
		cs.xf.ApplyAlignmentAttr = office.Bool(true)
	}
}

// SetHorizontalAlignment sets the horizontal alignment of a cell style.
func (cs CellStyle) SetHorizontalAlignment(a sml.ST_HorizontalAlignment) {
	if cs.xf.Alignment == nil {
		cs.xf.Alignment = sml.NewCT_CellAlignment()
	}
	cs.xf.Alignment.HorizontalAttr = a
	cs.xf.ApplyAlignmentAttr = office.Bool(true)
}

// SetRotation configures the cell to be rotated.
func (cs CellStyle) SetRotation(deg uint8) {
	if cs.xf.Alignment == nil {
		cs.xf.Alignment = sml.NewCT_CellAlignment()
	}
	cs.xf.ApplyAlignmentAttr = office.Bool(true)
	cs.xf.Alignment.TextRotationAttr = office.Uint8(deg)
}

// SetVerticalAlignment sets the vertical alignment of a cell style.
func (cs CellStyle) SetVerticalAlignment(a sml.ST_VerticalAlignment) {
	if cs.xf.Alignment == nil {
		cs.xf.Alignment = sml.NewCT_CellAlignment()
	}
	cs.xf.ApplyAlignmentAttr = office.Bool(true)
	cs.xf.Alignment.VerticalAttr = a
}

func (cs CellStyle) SetShrinkToFit(b bool) {
	if cs.xf.Alignment == nil {
		cs.xf.Alignment = sml.NewCT_CellAlignment()
	}
	cs.xf.ApplyAlignmentAttr = office.Bool(true)
	if !b {
		cs.xf.Alignment.ShrinkToFitAttr = nil
	} else {
		cs.xf.Alignment.ShrinkToFitAttr = office.Bool(b)
	}
}

// ClearFont clears any font configuration from the cell style.
func (cs CellStyle) ClearFont() {
	cs.xf.FontIdAttr = nil
	cs.xf.ApplyFontAttr = nil
}

// SetFont applies a font to a cell style.  The font is referenced by its
// index so modifying the font afterward will affect all styles that reference
// it.
func (cs CellStyle) SetFont(f Font) {
	cs.xf.FontIdAttr = office.Uint32(f.Index())
	cs.xf.ApplyFontAttr = office.Bool(true)
}

// SetBorder applies a border to a cell style.  The border is referenced by its
// index so modifying the border afterward will affect all styles that reference
// it.
func (cs CellStyle) SetBorder(b Border) {
	cs.xf.BorderIdAttr = office.Uint32(b.Index())
	cs.xf.ApplyBorderAttr = office.Bool(true)
}

// ClearBorder clears any border configuration from the cell style.
func (cs CellStyle) ClearBorder() {
	cs.xf.BorderIdAttr = nil
	cs.xf.ApplyBorderAttr = nil
}

// SetFill applies a fill to a cell style.  The fill is referenced by its index
// so modifying the fill afterward will affect all styles that reference it.
func (cs CellStyle) SetFill(f Fill) {
	cs.xf.FillIdAttr = office.Uint32(f.Index())
	cs.xf.ApplyFillAttr = office.Bool(true)
}

// ClearFill clears any fill configuration from the cell style.
func (cs CellStyle) ClearFill() {
	cs.xf.FillIdAttr = nil
	cs.xf.ApplyFillAttr = nil
}

func (cs CellStyle) Index() uint32 {
	for i, xf := range cs.xfs.Xf {
		if cs.xf == xf {
			return uint32(i)
		}
	}
	return 0
}
