*~  Program...........: MATXTAB.PRG
*~  Author............: Valdis Matison
*~  Version...........: 2.5
*~} Project...........:
*~  Created...........: 11/01/93
*~  Copyright.........: (c) Matison Consulting Group Inc., 1993
*~                          187 Dunblaine Avenue
*~                          Toronto, Ontario
*~                          M5M 2S6
*~
*~                          416-256-4495
*~
*~) Description.......: Replacement for GENXTAB.prg that ships with FoxPro
*~] Dependencies......:
*~  Calling Samples...: do MATXTAB with "XTAB", 1, .t., .f., 1, 4, 5, .t., 2, 3, 0, 0, .t., .t., .t., .t.
*~                      do MATXTAB with "XTAB", 1, .t., .f., 1, 2, 6, .t., 3, 4, 5, 0, .t., .t., .t., .t., la_fldlist, 2, 0, 1
*~  Returns...........: If you initialize a variable named LN_RETURN in the calling program
*~                      this program will return a value in that variable
*~                      This functionality is incomplete
*~
*~                      0 = Success!
*~                      - 1 = Unique column count greater than 256
*~                      - 2 = Escape pressed, procedure cancelled
*~                      - 3 = No dbf in current area
*~                      - 4 = Less than three fields in input dbf
*~                      - 5 = Too many fields in results table
*~                      - 6 = Row field must be character
*~                      - 7 = Column field must be character
*~                      > 0 returns Foxpro's error code generated by error()
*~
*~  Major change list.:
*~  Future............: Create a front end, DOS/Windows/MAC
*~  Notes:............: The error handling is incomplete, the user should decide what errors to trap.
*~                      Naming convention: First letter "l" denotes local variable
*~                                         Second letter denotes type
*~
*~  Parameter List....: lc_outfile : Name of the output file
*~                      lu_struct  : 1 means cursor, 2 means table, 3 means array,
*~                                  .t. means cursor, .f. means table
*~                                   if blank, same format as input
*~                      ll_closein : .t. means close input file, else keep open
*~                      ll_therm   : Included for compatibility with GENXTAB, not used by MATXTAB
*~                      ln_rowfld  : Number of field used for rows - field number
*~                      ln_cellfld : Column used for individual totals in results table  field number
*~                      ln_colhead : Field used for column headings in XTAB report field number
*~                      ll_xtotal  :.t. means create cross totals, else don't bother
*~
*~                      Differences from Genxtab begin here
*~
*~                      ln_extfld1 : First extra field to go into output
*~                      ln_extfld2 : Second extra field to go into output
*~                      ln_extfld3 : Third extra field to go into output
*~                      ln_extfld4 : Fourth extra field to go into output
*~                      ll_cnt     : Display count for each unique row
*~                      ll_avg     : Display average
*~                      ll_min     : Display minimum
*~                      ll_max     : Display maximum
*~                      la_uniqcol : name of array passed that has the uniqe column names already in list
*~                      ln_rowcol  : Number of dimensions in the array that's sent down
*~                      ln_colsort : 0 means smallest to largest
*~                                   1 means largest to smallest
*~                      ln_rowsort : 0 means ascending
*~                                   1 means descending
*~

Parameters lc_outfile, lu_struct, ll_closein, ll_therm, ln_rowfld, ;
	ln_colhead, ln_cellfld, ll_xtotal, ln_extfld1, ln_extfld2, ;
	ln_extfld3, ln_extfld4, ll_cnt, ll_avg, ll_min, ll_max, ;
	la_uniqcol, ln_rowcol, ln_colsort, ln_rowsort

External Array la_uniqcol

Private lc_colsort, lc_dbfname, lc_colhead, lc_cellfld, ;
	ln_dimens, lc_error, lc_escasta, lc_escape, lc_group, ;
	ln_params, lc_program, lc_rowfld, lc_rowsort, lc_safesta, ;
	lc_talksta, lc_type, lc_where, ln_uniqcnt

lc_dbfname = Alias()			&& Source file taken from alias name
lc_error   = On( "ERROR" )
lc_escasta = Set( "ESCAPE" )
lc_escape  = On( "ESCAPE" )
ln_params  = Pcount()
lc_safesta = Set( "SAFETY" )
lc_talksta = Set( "TALK" )

lcOutputDir = ADDBS(SYS(2023))

*On Error Do lo_exit With Error()
Set Escape On
On Escape Do lo_exit With -2
Set Safety Off
Set Talk Off

If Empty( Alias() )		&& A file must be open in the selected area
	Do lo_exit With -3
Endif

If Fcount() < 3			&& At least three fields required
	Do lo_exit With -4
Endif

If ln_params < 1	&& Nothing sent down - Determine filename for output
	lc_outfile = 'XTABX.DBF'
Endif

If ln_params < 2	&& No format given for output

	If Isdigit( Left( Justfname( Dbf() ) ,1 ) )
		lu_struct = 1		&& Cursor
	Else
		lu_struct = 2		&& Table
	Endif

Else

	If Type("lu_struct") = "L"	&& Logical value
		If lu_struct				&& True means create a cursor
			lu_struct = 1
		Else						&& False means table
			lu_struct = 2
		Endif						&&
	Endif						&& Logical or numeric value

Endif				&& No format given for output


If ln_params < 3	&& Close input defaults to yes
	ll_closein = .T.
Endif

If ln_params < 5	&& Number of field for cross tab rows
	ln_rowfld = 1
Endif

If Type( Field( ln_rowfld ) ) <> "C"
	On Escape Do lo_exit With -6
Endif


If ln_params < 6	&& Number of field for columns
	ln_colhead = 2
Endif

If Type( Field( ln_colhead ) ) <> "C"
	On Escape Do lo_exit With -7
Endif

If ln_params < 7	&& Number of field for cross tab cells
	ln_cellfld = 3
Endif

If ln_params < 8
	ll_xtotal = .F.
Endif

If ln_params < 17
	Private la_uniqcol
	Dime la_uniqcol(1)
Endif

If ln_params > 17
	ln_dimens  = ln_rowcol
Else
	ln_dimens  = 1
Endif

If ln_params > 18
	Do Case
		Case ln_colsort = 1
			lc_colsort = "DESC"

		Otherwise
			lc_colsort = "ASC"
	Endcase

Else
	lc_colsort = "ASC"
Endif

If ln_params = 20
	Do Case
		Case ln_rowsort = 1
			lc_rowsort = "DESC"

		Otherwise
			lc_rowsort = "ASC"
	Endcase

Else
	lc_rowsort = "ASC"
Endif

lc_colhead = Field( ln_colhead )
lc_cellfld = lc_dbfname + "." + Field( ln_cellfld )
lc_group   = lc_dbfname + "." + Field( ln_rowfld )
lc_program = Program( 1 )
ln_return  = 0
lc_rowfld  = lc_group
lc_where   = "ZZ" + "." + ;
	lc_colhead + "=" + ;
	lc_dbfname + "." + ;
	lc_colhead


*do matshow

If ln_params < 17

	lc_type = Type( (lc_colhead) )
	Do Case
		Case lc_type = "C"

			Select &lc_colhead ;
				from ( lc_dbfname ) ;
				order By 1 &lc_colsort ;
				group By 1 ;
				into Array la_uniqcol

		Case lc_type = "D"

			Select "D"+Dtoc( &lc_colhead, 1 ) ;
				from (lc_dbfname) ;
				order By 1 &lc_colsort ;
				group By 1 ;
				into Array la_uniqcol

			lc_where = "ZZ" + "." + ;
				lc_colhead + "=" + ;
				"'D' +dtoc(" + ;
				lc_dbfname + "." + ;
				"&lc_colhead, 1 )"

	Endcase

	ln_uniqcnt = Alen( la_uniqcol )
	* If empty cursor
	IF RECCOUNT(lc_dbfname) = 0
		ln_uniqcnt = 0
	ENDIF 

Else

	ln_uniqcnt = Alen( la_uniqcol, ln_dimens )

Endif

If ln_uniqcnt >256
	Do lo_exit With -1
Endif

Dime la_colname[ ln_uniqcnt + 1, 4 ]
la_colname[1,1] = lc_colhead
la_colname[1,2] = "C"

If ln_params < 17 		&& no array sent down
	la_colname[ 1,3 ] = Len( &lc_colhead )
Else
	la_colname[ 1,3 ] = Len( la_uniqcol[ 1,1 ] )	&& What about width ?
Endif

la_colname[1,4] = 0


For ln_k = 2 To ln_uniqcnt + 1
	la_colname[ ln_k, 1 ] = la_uniqcol[ 1, ln_k-1 ]
	la_colname[ ln_k, 2 ] = "N"
	la_colname[ ln_k, 3 ] = 1
	la_colname[ ln_k, 4 ] = 0
Endfor

Create Cursor ZZ From Array la_colname

*--Now populate the table with 1's
For ln_k = 2 To ln_uniqcnt + 1
	Insert Into ZZ ( (lc_colhead), (Field(ln_k)) ) ;
		VALUES (la_colname[ ln_k, 1 ], 1 )
Endfor

*--
*-- Now create the cross tab
*--

lc_sqlxtab  = "SELECT " + lc_rowfld


If ln_params > 8
	If ln_extfld1 > 0
		lc_sqlxtab = lc_sqlxtab + ", " + lc_dbfname + "." + field( ln_extfld1, lc_dbfname )
		lc_group = lc_group + ", " + lc_dbfname + "." + field( ln_extfld1, lc_dbfname )
	Endif
Endif

If ln_params > 9
	If ln_extfld2 > 0
		lc_sqlxtab = lc_sqlxtab + ", " + lc_dbfname + "." + field( ln_extfld2, lc_dbfname )
		lc_group = lc_group + ", " + lc_dbfname + "." + field( ln_extfld2, lc_dbfname )
	Endif
Endif

If ln_params > 10
	If ln_extfld3 > 0
		lc_sqlxtab = lc_sqlxtab + ", " + lc_dbfname + "." + field( ln_extfld3, lc_dbfname )
		lc_group = lc_group + ", " + lc_dbfname + "." + field( ln_extfld3, lc_dbfname )		
	Endif
Endif

If ln_params > 11
	If ln_extfld4 > 0
		lc_sqlxtab = lc_sqlxtab + ", " + lc_dbfname + "." + field( ln_extfld4, lc_dbfname )
		lc_group = lc_group + ", " + lc_dbfname + "." + field( ln_extfld4, lc_dbfname )		
	Endif
Endif

For ln_k = 1 To ln_uniqcnt
	lc_sqlxtab = lc_sqlxtab + ", " + ;
		"SUM( " + lc_cellfld + ;
		"*ZZ." + ;
		la_uniqcol[ 1, ln_k ] + ") as " + ;
		la_uniqcol[ ln_dimens, ln_k ]
Endfor

*--
*-- Add totals column if required.
*-- Add count, average, min, max functions as well
*--

If ll_cnt
	lc_sqlxtab = lc_sqlxtab + ;
		", COUNT( " + lc_rowfld + " ) as 'XCNT' "
Endif

If ll_avg
	lc_sqlxtab = lc_sqlxtab + ;
		", AVG( " + lc_cellfld + " ) as 'XAVG' "
Endif

If ll_min
	lc_sqlxtab = lc_sqlxtab + ;
		", MIN( " + lc_cellfld + " ) as 'XMIN' "
Endif

If ll_max
	lc_sqlxtab = lc_sqlxtab + ;
		", MAX( " + lc_cellfld + " ) as 'XMAX' "
Endif

If ll_xtotal
	lc_sqlxtab = lc_sqlxtab + ;
		", SUM( " + lc_cellfld + " ) as 'XTOT' "
Endif

lc_sqlxtab = lc_sqlxtab + ;
	" FROM (lc_dbfname), ZZ " + ;
	" WHERE &lc_where " + ;
	" GROUP BY &lc_group " + ;
	" Order by &lc_group &lc_rowsort " + ;
	" INTO "

Do Case
	Case lu_struct = 2			&& output to table
		lc_sqlxtab = m.lc_sqlxtab + "TABLE " + m.lcOutputDir + lc_outfile

	Case lu_struct = 3			&& output to array
		lc_sqlxtab = lc_sqlxtab + "ARRAY _XTABX "

		Public _xtabx
		Dime _xtabx[ 1,1 ]

	Otherwise					&& output to cursor DEFAULT
		lc_sqlxtab = lc_sqlxtab + "CURSOR " + lc_outfile
Endcase

*-- Run the query

&lc_sqlxtab

Do lo_exit With ln_return
*-- eop MATXTAB


*~!*********************************************
*~!
*~!       Procedure: lo_exit
*~!
*~!*********************************************
Procedure lo_exit
	*~  Author............: Valdis Matison
	*~  Modified by.......:
	*~} Project...........:
	*~  Created...........: 11/01/93
	*~  Copyright.........: (c) Matison Consulting Group Inc., 1993
	*~) Description.......: Shutdown procedure for MATXTAB
	*~)
	*~] Dependencies......:
	*~  Calling Samples...:
	*~  Parameter List....: ln_ret : Error code
	*~  Returns...........:
	*~  Future ideas......:
	*~  Major change list.:

	Parameters ln_ret
	ln_return = ln_ret

	If lc_talksta = "ON"
		Set Talk On
	Endif

	If lc_safesta = "ON"
		Set Safety On
	Endif

	If lc_escasta = "OFF"
		Set Escape Off
	Endif

	*release windows lw_mat
	Clear Typeahead

	*-- Close input files only if successful
	If ln_ret = 0
		USE IN (SELECT('ZZ'))

		If ll_closein			&& close the input file
			USE IN (Select(lc_dbfname))
		Endif
		IF USED(JUSTSTEM(lc_outfile))
			Select (lc_outfile)
		ENDIF 	
	Endif

	* On Error &lc_error
	On Escape &lc_escape

	Return ln_ret

	*--
	*-- eop lo_exit
ENDPROC && lo_exit

