1 /*
2 	 -------------------------------------------------------------------
3 
4 	 Copyright (C) 2014, Edwin van Leeuwen
5 
6 	 This file is part of plotd plotting library.
7 
8 	 Plotd is free software; you can redistribute it and/or modify
9 	 it under the terms of the GNU General Public License as published by
10 	 the Free Software Foundation; either version 3 of the License, or
11 	 (at your option) any later version.
12 
13 	 Plotd is distributed in the hope that it will be useful,
14 	 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 	 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 	 GNU General Public License for more details.
17 
18 	 You should have received a copy of the GNU General Public License
19 	 along with Plotd. If not, see <http://www.gnu.org/licenses/>.
20 
21 	 -------------------------------------------------------------------
22 	 */
23 
24 module cli.column;
25 
26 import std.conv : to;
27 import std.regex : ctRegex, match;
28 import std.range;
29 import std.string;
30 
31 version( unittest ) {
32 	import std.stdio : writeln;
33 }
34 
35 struct Format {
36 	string mode; /// x,y,lx,ly,h
37 	int dataID = -1; /// -1 is the default value
38 	string plotID; /// plotName/id 
39 }
40 
41 Format parseColumnFormat( string mode ) {
42 	Format colMode;
43 	auto columnRegex = ctRegex!( r"(lx|ly|x|y|hx|hy|hz|h|..)(\d*)(.*)" );
44 	auto m = mode.match( columnRegex );
45 	colMode.mode = m.captures[1];
46 	if ( m.captures[2].length > 0 )
47 		colMode.dataID = m.captures[2].to!int;
48 	colMode.plotID = m.captures[3];
49 	return colMode;
50 }
51 
52 unittest {
53 	auto col = parseColumnFormat( "lx1a" );
54 	assert( col.mode == "lx" );
55 	assert( col.dataID == 1 );
56 	assert( col.plotID == "a" );
57 	col = parseColumnFormat( "ly1a" );
58 	assert( col.mode == "ly" );
59 	col = parseColumnFormat( "xb" );
60 	assert( col.mode == "x" );
61 	assert( col.dataID == -1 );
62 	assert( col.plotID == "b" );
63 
64 	col = parseColumnFormat( "y3" );
65 	assert( col.mode == "y" );
66 	assert( col.dataID == 3 );
67 	assert( col.plotID == "" );
68 
69 	col = parseColumnFormat( "hx" );
70 	assert( col.mode == "hx" );
71 	col = parseColumnFormat( "hy" );
72 	assert( col.mode == "hy" );
73 	col = parseColumnFormat( "h" );
74 	assert( col.mode == "h" );
75 }
76 /// Return true if Format represents an x value
77 bool xCoord( Format cm ) {
78 	return cm.mode.back.to!string == "x";
79 }
80 
81 unittest {
82 	auto cm = Format();
83 	cm.mode = "x";
84 	assert( cm.xCoord );
85 	cm.mode = "lx";
86 	assert( cm.xCoord );
87 	cm.mode = "yx";
88 	assert( cm.xCoord );
89 	cm.mode = "y";
90 	assert( !cm.xCoord );
91 	cm.mode = "ly";
92 	assert( !cm.xCoord );
93 	cm.mode = "xy";
94 	assert( !cm.xCoord );
95 }
96 
97 /// Return true if Format represents an y value
98 bool yCoord( Format cm ) {
99 	return cm.mode.back.to!string == "y";
100 }
101 
102 unittest {
103 	auto cm = Format();
104 	cm.mode = "y";
105 	assert( cm.yCoord );
106 	cm.mode = "ly";
107 	assert( cm.yCoord );
108 	cm.mode = "xy";
109 	assert( cm.yCoord );
110 	cm.mode = "x";
111 	assert( !cm.yCoord );
112 	cm.mode = "lx";
113 	assert( !cm.yCoord );
114 	cm.mode = "yx";
115 	assert( !cm.yCoord );
116 }
117 
118 ///
119 struct Formats {
120 
121 	this( size_t noColumns ) {
122 		if (noColumns > 1)
123 			_formats = parseDataFormat( "x,y,.." )._formats;
124 		else
125 			_formats = parseDataFormat( "h,.." )._formats;
126 	}
127 
128 	@property Format front() {
129 		auto fm = _formats.front;
130 		if ( _formats.length == 1 && fm.mode == ".." ) {
131 			auto pfm = prevFormats[dotdotID%prevFormats.length];
132 
133 			// Increase plot ID if needed
134 			if ( prevFormats.length == 2 
135 					&& prevFormats[0].plotID.length > 0
136 					&& prevFormats[1].plotID.length > 0 ) {
137 				pfm.plotID = pfm.plotID[0..$-1] ~
138 					(prevFormats[1].plotID.back.to!char + 
139 					 (1+dotdotID)*(prevFormats[1].plotID.back.to!char -
140 						prevFormats[0].plotID.back.to!char)).to!char.to!string;
141 			}
142 			return pfm;
143 		}
144 		return fm;
145 	}
146 
147 	void popFront() {
148 		if (_formats.length <= 3 && prevFormats.length == 0
149 				&& _formats.back.mode == "..") {
150 			prevFormats = _formats[0..$-1].dup;
151 		}
152 		if (_formats.front.mode != ".." ) 
153 			_formats.popFront;
154 		else
155 			dotdotID++;
156 	}
157 
158 	@property bool empty() {
159 		return _formats.empty;
160 	}
161 
162 	private:
163 		Format[] _formats;
164 		Format[] prevFormats;
165 		size_t dotdotID = 0;
166 }
167 
168 ///
169 Formats parseDataFormat( string dataFormat ) {
170 	Formats formats;
171 	foreach( fm; dataFormat.split( ',' ) ) {
172 		formats._formats ~= parseColumnFormat( fm ); 
173 	}
174 	return formats;
175 }
176 
177 unittest {
178 	auto fmts = parseDataFormat( "x,y" );
179 	assert( fmts.front.mode == "x" );
180 	fmts = parseDataFormat( "x,.." );
181 	fmts.popFront;
182 	assert( fmts.front.mode == "x" );
183 	fmts.popFront;
184 	assert( fmts.front.mode == "x" );
185 	fmts = parseDataFormat( "x,y,.." );
186 	fmts.popFront;
187 	fmts.popFront;
188 	assert( fmts.front.mode == "x" );
189 	fmts.popFront;
190 	assert( fmts.front.mode == "y" );
191 	fmts = parseDataFormat( "haa,hab,.." );
192 	fmts.popFront;
193 	fmts.popFront;
194 	assert( fmts.front.plotID == "ac" );
195 	fmts.popFront;
196 	assert( fmts.front.plotID == "ad" );
197 	fmts.popFront;
198 	assert( fmts.front.plotID == "ae" );
199 }
200 
201 ///
202 bool validFormat( Formats formats, size_t noColumns ) {
203 	if ( formats.empty )
204 		return false;
205 	if ( formats._formats.length == noColumns ||
206 			formats._formats.back.mode == ".." 	) {
207 		return true;
208 	}
209 	return false;
210 }
211 
212 unittest {
213 	Formats fmts;
214 	assert( !validFormat( fmts, 2 ) );
215 }
216 
217 /// Format and data of a specific column
218 struct ColumnData {
219 	double value;
220 
221 	this( Format fm ) {
222 		_format = fm;
223 	}
224 
225 	this( string mode, int dataID, string plotID, double v ) {
226 		_format.mode = mode;
227 		_format.dataID = dataID;
228 		_format.plotID = plotID;
229 		value = v;
230 	}
231 
232 	alias _format this;
233 
234 	Format _format;
235 }
236 
237 unittest {
238 	auto fm = Format( "lx", 1, "a" );
239 	ColumnData cm = ColumnData( fm );
240 	assert( cm.mode == "lx" );
241 	assert( cm.dataID == 1 );
242 	assert( cm.plotID == "a" );
243 }