Type xlsxStyles = Class

    Function Create(file);
    Begin
		file_  := file;
		style_xml_file_ := file_.WorkBook().GetXmlFileObj('xl/styles.xml');
		style_ := array();
		Init();
    End

	Function Init();
	Begin
		style_node := style_xml_file_.FirstChildElement("styleSheet");
		xfs_node := style_node.FirstChildElement("cellXfs");
		xf_node := xfs_node.FirstChildElement("xf");
		count := 0;
		while ifObj(xf_node) do
		begin
			style_[inttostr(count++)] := xf_node;
			xf_node := xf_node.NextElement("xf");
		end
	End

	Function GetStyleId(style);overload;
	Begin
		node := style_xml_file_.FirstChildElement('styleSheet');
		font_id := insertNode(node, 'fonts', style.Font);
		border_id := insertNode(node, 'borders', style.Border);
		fill_id := insertNode(node, 'fills', style.Fill);

		processNumFmtId(node, 'numFmts', style.NumberFormat);
		numfmt_id := insertNode(node, 'numFmts', style.NumberFormat);

		alignment_marshal := style.Alignment.Marshal();
		if istable(alignment_marshal['children']) or istable(alignment_marshal['attributes']) then
			alignment_flag := 1;
		protection_marshal := style.Protection.Marshal();
		if istable(protection_marshal['children']) or istable(protection_marshal['attributes']) then
			protection_flag := 1;

		xf := TOfficeObj('TXf');
		xf.NumFmtId := style.NumberFormat.NumFmtId ? : 0;
		xf.FontId := font_id;
		xf.FillId := fill_id;
		xf.BorderId := border_id;
		xf.XfId := 0;
		xf.ApplyFont := font_id <> '0' ? 1 : nil;
		xf.ApplyFill := fill_id <> '0' ? 1 : nil;
		xf.ApplyBorder := border_id <> '0' ? 1 : nil;
		xf.ApplyAlignment := alignment_flag ?: nil;
		xf.ApplyProtection := protection_flag ?: nil;
		xf.Alignment := style.Alignment;
		xf.Protection := style.Protection;

		node := node.FirstChildElement('cellXfs');
		count := node.GetAttribute('count');
		node.SetAttribute('count', strtoint(count) + 1);
		xf_node := node.InsertEndChild(xf.Marshal());
		style_[count] := xf_node;
		return count;
	End;

	Function GetStyleId(newStyle, oldStyleId);overload;
	Begin
		if oldStyleId = '' then return nil;
		style_node := style_xml_file_.FirstChildElement('styleSheet');
		cellXfs_node := style_node.FirstChildElement('cellXfs');
		count := strtoint(cellXfs_node.GetAttribute('count'));
		Id := strtoint(oldStyleId);
		if Id > count-1 then return nil;

		xf := getNode(cellXfs_node, 'xf', Id);
		marshal := xf.marshal()[0];
		xf_node := cellXfs_node.InsertEndChild(marshal);
		cellXfs_node.SetAttribute('count', count + 1);
		attrs := marshal['attributes'];

		style := newStyle;

		numfmt_id := trystrtoint(attrs['numFmtId'], r) ? r : 0;
		copyNode(style_node, xf_node, 'numFmts', 'numFmt', 'numFmtId', numfmt_id, style.NumberFormat);

		font_id := trystrtoint(attrs['fontId'], r) ? r : 0;
		copyNode(style_node, xf_node, 'fonts', 'font', 'fontId', font_id, style.Font);

		border_id := trystrtoint(attrs['borderId'], r) ? r : 0;
		copyNode(style_node, xf_node, 'borders', 'border', 'borderId', border_id, style.Border);

		fill_id := trystrtoint(attrs['fillId'], r) ? r : 0;
		copyNode(style_node, xf_node, 'fills', 'fill', 'fillId', fill_id, style.Fill);

		marshal := style.Alignment.Marshal();
		alignment := xf_node.FirstChildElement('alignment');
		if ifObj(alignment) then class(TSXml).UpdateNode(alignment, marshal['attributes'], marshal['children']);
		else begin
			xf_node.InsertEndChild(marshal);
			xf_node.SetAttribute('applyAlignment', 1);
		end
		marshal := style.Protection.Marshal();
		protection := xf_node.FirstChildElement('protection');
		if ifObj(protection) then class(TSXml).UpdateNode(protection, marshal['attributes'], marshal['children']);
		else begin
			xf_node.InsertEndChild(marshal);
			xf_node.SetAttribute('applyProtection', 1);
		end

		style_[inttostr(count)] := xf_node;
		return inttostr(count);
	End;

	Function GetStyle(styleId);
	Begin
		xf := style_[styleId];
		if not ifObj(xf) then return nil;
		attrs := xf.Attributes();
		numfmt_id := trystrtoint(attrs['numFmtId'], r) ? r : 0;
		font_id := trystrtoint(attrs['fontId'], r) ? r : 0;
		border_id := trystrtoint(attrs['borderId'], r) ? r : 0;
		fill_id := trystrtoint(attrs['fillId'], r) ? r : 0;

		alignment := xf.FirstChildElement('alignment');
		protection := xf.FirstChildElement('protection');
		style_node := style_xml_file_.FirstChildElement('styleSheet');
		style := TOfficeObj('TStyle');
		numFmts_node := style_node.FirstChildElement('numFmts');
		fonts_node := style_node.FirstChildElement('fonts');
		borders_node := style_node.FirstChildElement('borders');
		fills_node := style_node.FirstChildElement('fills');
		setRootObj(style.NumberFormat, getNode(numFmts_node, 'numFmt', numfmt_id));
		setRootObj(style.Font, getNode(fonts_node, 'font', font_id));
		setRootObj(style.Border, getNode(borders_node, 'border', border_id));
		node := getNode(fills_node, 'fill', fill_id);
		setRootObj(style.Fill, node);
		setFillGradientObj(style.Fill.Gradient, node);
		style.Alignment.RootObj := ifObj(alignment) ? alignment : nil;
		style.Protection.RootObj := ifObj(protection) ? protection : nil;
		setNodeUri(style);
		return style;
	End;

	Function GetNumFmtId(styleId);
	Begin
		node := style_[styleId];
		if ifnil(node) then return '0';
		numfmt := node.GetAttribute("numFmtId");
		return numfmt = '' ? '' : numfmt;
	End;

	Function GetType(styleId, value);
	Begin
		numfmt_id := GetNumFmtId(styleId);
		if numfmt_id = '' then return array(nil, value);
		numfmt_id := strtoint(numfmt_id);
		if numfmt_id = 0 then return array(nil, value);
		if numfmt_id >= 1 and numfmt_id <= 10 then return array('n', value);
		if numfmt_id >= 11 and numfmt_id <= 26 then
		begin
			if ifstring(value) and (trystrtodate(value, r) or trystrtodatetime(value, r) or trystrtotime(value, r)) then return array('n', r);
			t := ifstring(value) ? 's' : 'n';
			return array(t, value);
		end
		if numfmt_id >= 27 and numfmt_id <= 34 then return array('e', value);
		if numfmt_id >= 35 and numfmt_id <= 48 then return array('n', value);
		if numfmt_id = 49 then return array('s', value);
		if numfmt_id >= 50 and numfmt_id <= 55 then
		begin
			if ifstring(value) and trystrtodate(value, r) then return array('n', r);
			t := ifstring(value) ? 's' : 'n';
			return array(t, value);
		end
		if numfmt_id >= 56 and numfmt_id <= 58 then
		begin
			if ifstring(value) and trystrtodatetime(value, r) then return array('n', r);
			t := ifstring(value) ? 's' : 'n';
			return array(t, value);
		end
		else begin
			if ifstring(value) then
			begin
				if (pos("-", value) or pos("/", value)) and (trystrtodatetime(value, r) or trystrtodate(value, r)) then return array('n', r);
				if trystrtofloat(value, r) then return array("n", value);
				return array("s", value);
			end
			return array("n", value);
		end
	End;

private

	Function setFillGradientObj(obj, node);
	Begin
		gradient_node := node.FirstChildElement('gradientFill');
		if not ifObj(gradient_node) then return;
		stop_node := gradient_node.FirstChildElement('stop');
		if ifObj(stop_node) then
		begin
			obj.Stop1.RootObj := stop_node;
			obj.Stop1.NodeUri := '';
		end
		stop_node := stop_node.NextElement();
		if ifObj(stop_node) then
		begin
			obj.Stop2.RootObj := stop_node;
			obj.Stop2.NodeUri := '';
		end
		stop_node := stop_node.NextElement();
		if ifObj(stop_node) then
		begin
			obj.Stop3.RootObj := stop_node;
			obj.Stop3.NodeUri := '';
		end
	End;

	Function setRootObj(obj, node);
	Begin
		obj.RootObj := node;
		children := obj.GetChildren();
		for i:=0 to length(children)-1 do
		begin
			val := children[i]['obj'];
			if ifObj(val) then
			begin
				val.NodeUri := children[i]['name'];
				val.RootObj := node;
			end
		end;
	End;

	Function setNodeUri(obj);
	Begin
		obj.NodeUri := '';
		children := obj.GetChildren();
		for i:=0 to length(children)-1 do
		begin
			val := children[i]['obj'];
			if ifObj(val) then val.NodeUri := '';
		end;
	End;

	Function getNode(rootNode, name, count);
	Begin
		if name = 'numFmt' then return getNodeNumFmts(rootNode, count);
		if not ifObj(rootNode) then return nil;
		node := rootNode.FirstChildElement(name);
		times := count;
		while times > 0 do
		begin
			node := node.NextElement();
			times--;
		end
		return node;
	End;

	Function getNodeNumFmts(rootNode, id);
	Begin
		if not ifObj(rootNode) then return nil;
		node := rootNode.FirstChildElement('numFmt');
		num_id := inttostr(id);
		while ifObj(node) do
		begin
			if num_id = node.GetAttribute('numFmtId') then return node;
			node := node.NextElement();
		end
		return nil;
	End;

	Function copyNodeNumFmts(root, xf_node, id, obj);
	Begin
		if id = 0 then
		begin
			if ifnil(obj.FormatCode) then xf_node.SetAttribute('numFmtId', 0);
			else begin
				processNumFmtId(root, 'numFmts', obj);
				insertNode(root, 'numFmts', obj);
				xf_node.SetAttribute('numFmtId', obj.NumFmtId);
			end
		end
		else begin
			nodeName_node := root.FirstChildElement('numFmts');
			num_node := getNodeNumFmts(nodeName_node, id);
			processNumFmtId(root, 'numFmts', obj);
			new_node := nodeName_node.InsertEndChild(num_node.Marshal()[0]);
			count := strtoint(nodeName_node.GetAttribute('count'));
			nodeName_node.SetAttribute('count', count + 1);
			xf_node.SetAttribute('numFmtId', obj.NumFmtId);
			marshal := obj.Marshal();
			class(TSXml).UpdateNode(new_node, marshal['attributes'], marshal['children']);
		end
	End;

	Function copyNode(root, xf_node, nodeName, name, idName, id, obj);
	Begin
		if nodeName = 'numFmts' then return copyNodeNumFmts(root, xf_node, id, obj);
		nodeName_node := root.FirstChildElement(nodeName);
		if not ifObj(nodeName_node) then return;
		node := getNode(nodeName_node, name, id);
		new_node := nodeName_node.InsertEndChild(node.Marshal()[0]);
		count := strtoint(nodeName_node.GetAttribute('count'));
		nodeName_node.SetAttribute('count', count + 1);
		xf_node.SetAttribute(idName, count);

		if ifObj(obj) then
		begin
			marshal := obj.Marshal();
			class(TSXml).UpdateNode(new_node, marshal['attributes'], marshal['children']);
		end;
	End;

	Function insertNode(rootNode, childName, obj);
	Begin
		marshal := obj.Marshal();
		if not istable(marshal['children']) and not istable(marshal['attributes']) then return '0';
		node := rootNode.FirstChildElement(childName);
		if not ifObj(node) then
		begin
			info := array('name': childName, 'type': 'element', 'attributes': ('count': '0'));
			rootNode.InsertFirstChild(info);
			node := rootNode.FirstChildElement(childName);
		end
		count := node.GetAttribute('count');
		node.InsertEndChild(marshal);
		node.SetAttribute('count', strtoint(count) + 1);
		return count;
	End

	Function processNumFmtId(rootNode, childName, obj);
	Begin
		if ifnil(obj.FormatCode) then return;
		node := rootNode.FirstChildElement(childName);
		if not ifObj(node) then id := 176;
		else begin
			node := node.LastChildElement('numFmt');
			id := strtoint(node.GetAttribute('numFmtId')) + 1;
		end
		obj.numFmtId := id;
	End

private
	file_;  //TSXlsxFile对象
	style_xml_file_;
	style_; // 缓存style
End;

// {*
// 也可参考: https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.wordprocessing.numberingformat?view=openxml-3.0.1
// numfmtid为1表示数值格式，即显示为整数或小数，且根据系统设置或区域设置来决定小数点、千位分隔符和负数的显示方式。
// numfmtid为2表示货币格式，即显示为带有货币符号的数值，且根据系统设置或区域设置来决定货币符号、小数点、千位分隔符和负数的显示方式。
// numfmtid为3表示会计格式，即显示为带有货币符号和千位分隔符的数值，且根据系统设置或区域设置来决定货币符号、小数点和负数的显示方式。会计格式的特点是货币符号和数值之间有一定的空格，且小数位数固定为2。
// numfmtid为4表示百分比格式，即显示为带有百分号的数值，且根据系统设置或区域设置来决定小数点和负数的显示方式。
// numfmtid为5表示千位分隔符格式，即显示为带有千位分隔符的数值。
// numfmtid为6表示负数红色格式，即显示为带有负号和红色字体的数值。
// numfmtid为7表示千位分隔符格式，即显示为带有千位分隔符的数值，且根据系统设置或区域设置来决定小数点和负数的显示方式。
// numfmtid为8表示负数红色格式，即显示为带有负号和红色字体的数值，且根据系统设置或区域设置来决定小数点和千位分隔符的显示方式。
// numfmtid为9表示分数格式，即显示为分数形式的数值，且根据系统设置或区域设置来决定负数的显示方式。分数格式的特点是分子和分母都是一位数，例如1/2。
// numfmtid为10表示指数格式，即显示为带有小数点和指数的数值。
// numfmtid为11表示日期时间格式，即显示为年/月/日 时:分的形式。
// numfmtid为12表示时间格式，即显示为时:分:秒的形式。
// numfmtid为13表示时间格式，即显示为上午/下午 时:分的形式。
// numfmtid为16表示日期格式，即显示为月/日/年的形式。
// numfmtid为17表示日期格式，即显示为日-月-年的形式。
// numfmtid为18表示日期格式，即显示为年-月-日的形式。
// numfmtid为19表示日期格式，即显示为月-年的形式。
// numfmtid为20表示日期格式，即显示为年-月的形式。
// numfmtid为21表示日期时间格式，即显示为年/月/日 时:分:秒的形式。
// numfmtid为22表示日期时间格式，即显示为年/月/日 上午/下午 时:分的形式。
// numfmtid为23表示时间格式，即显示为时:分的形式，带有前导零。
// numfmtid为24表示时间格式，即显示为时:分:秒的形式，带有前导零。
// numfmtid为25表示时间格式，即显示为上午/下午 时:分的形式，带有前导零。
// numfmtid为26表示时间格式，即显示为上午/下午 时:分:秒的形式，带有前导零。
// numfmtid为27表示错误值格式，即显示为#NULL!。
// numfmtid为28表示错误值格式，即显示为#DIV/0!。
// numfmtid为29表示错误值格式，即显示为#VALUE!。
// numfmtid为30表示错误值格式，即显示为#REF!。
// numfmtid为31表示错误值格式，即显示为#NAME?。
// numfmtid为32表示错误值格式，即显示为#NUM!。
// numfmtid为33表示错误值格式，即显示为#N/A。
// numfmtid为34表示错误值格式，即显示为#GETTING_DATA。
// numfmtid是35表示货币格式，即显示为带有货币符号的数值，且小数位数为0。
// numfmtid为36表示货币格式，即显示为带有货币符号和负数红色的数值。
// numfmtid为38表示会计格式，即显示为带有货币符号和负数红色的数值，且小数位数为2。
// numfmtid为39表示会计格式，即显示为带有货币符号和负数红色的数值，且小数位数为0。
// numfmtid为40表示会计格式，即显示为带有货币符号和负数红色的数值，且小数位数为4。
// numfmtid为41表示货币格式，即显示为带有货币符号和千位分隔符的数值，且小数位数为2。
// numfmtid为42表示货币格式，即显示为带有货币符号和千位分隔符的数值，且小数位数为0。
// numfmtid为43表示货币格式，即显示为带有货币符号和千位分隔符的数值，且小数位数为4。
// numfmtid为44表示货币格式，即显示为带有货币符号和千位分隔符的数值，且负数红色，且小数位数为2。
// numfmtid为45表示货币格式，即显示为带有货币符号和千位分隔符的数值，且负数红色，且小数位数为0。
// numfmtid为46表示货币格式，即显示为带有货币符号和千位分隔符的数值，且负数红色，且小数位数为4。
// numfmtid为47表示会计格式，即显示为带有货币符号和千位分隔符的数值，且小数位数为2。
// numfmtid为48表示会计格式，即显示为带有货币符号和千位分隔符的数值，且小数位数为0。
// numfmtid为49表示文本格式，即显示为文本。
// numfmtid为50表示日期格式，即显示为年/月/日的形式，带有前导零。
// numfmtid为51表示日期格式，即显示为月/日/年的形式，带有前导零。
// numfmtid为52表示日期格式，即显示为日-月-年的形式，带有前导零。
// numfmtid为53表示日期格式，即显示为年-月-日的形式，带有前导零。
// numfmtid为54表示日期格式，即显示为月-年的形式，带有前导零。
// numfmtid为55表示日期格式，即显示为年-月的形式，带有前导零。
// numfmtid为56表示日期时间格式，即显示为年/月/日 时:分:秒的形式，带有前导零。
// numfmtid为57表示日期时间格式，即显示为年/月/日 上午/下午 时:分的形式，带有前导零。
// numfmtid为58表示日期时间格式，即显示为年/月/日 上午/下午 时:分:秒的形式，带有前导零。
// *}
