// XMIInspect container methods

#include "stdafx.h"

int XMIInspect::processContainerData(int block, int container, int size, int type, int subnum, int eindex, int *count)
{
	int stat = 0, i = 0, curvalue = 0;
	unsigned char *endptr = curptr + size, *startptr = curptr;
	char isneg, *str = NULL, hexflag = 0;
	unsigned char c;
	unsigned int u = 0, restdigits = 0, numdigits = 0;
	bool base64 = false;

	if (level >= XMIINS_REPORT_ALL) {
		printf(" processing data for container block %ld/%ld, container %ld/%ld (size: %ld Bytes) of type %s\n",
			block+1, numblocks, container+1, numcontainers[container], size, ExprInfo::type2text(type));
	}

	/* check eindex value */
	if (type == XMIINS_SC_E && (eindex < 0 || eindex >= numglobals)) {
		if (level >= XMIINS_REPORT_ERRORS) {
			printf("eindex value out of range: %ld\n", eindex);
		}
		stat = XMIINS_ERR_INTERNAL;
		goto cleanup;
	}

	switch (type) {
		case XMIINS_SC_T:
			while (curptr < endptr) {
				if (level >= XMIINS_REPORT_ALL) {
					printf ("  text value: '");
				}
				delete[] printlstring(curptr, false, XMIINS_REPORT_ALL);
				if (level >= XMIINS_REPORT_ALL) {
					printf ("'\n");
				}
				(*count)++;
			}
			break;

		case XMIINS_SC_U:			// report the number
		case XMIINS_SC_OR:		// check & report the current choice
		case XMIINS_SC_REP:		// report the repeat count
		case XMIINS_SC_E:			// check & report the index to the current enum table
			while (curptr < endptr) {
				u = Load::UInt32(curptr);
				switch (type) {
					case XMIINS_SC_OR:
						/* check choice no. */
						if (u >= subnum) {
							if (level >= XMIINS_REPORT_ERRORS) {
								printf ("'or' choice value too large: %ld >= %ld\n", u, subnum);
							}
							stat = XMIINS_ERR_OR;
							goto cleanup;
						}
						break;
					case XMIINS_SC_REP:
						if (level >= XMIINS_REPORT_ALL) {
							printf ("  repeat count: %ld\n", u);
						}
						break;
					case XMIINS_SC_U:
						if (level >= XMIINS_REPORT_ALL) {
							printf ("  uint32 value: %ld\n", u);
						}
						break;
					case XMIINS_SC_E:
						/* check index no. */
						if (u >= index[eindex]) {
							/* e container -> global data mapping is bogus, just print a warning 
                     if (level >= XMIINS_REPORT_ERRORS) {
								printf ("'e' dictionary index too large: %ld >= %ld\n", u, index[eindex]);
							}
							stat = XMIINS_ERR_E;
							goto cleanup;*/
                     if (level >= XMIINS_REPORT_WARNINGS) {
								printf ("'e' dictionary index %ld too large: %ld >= %ld. "
                                "Probably an internal mapping error\n", 
                                eindex, u, index[eindex]);
							}
						}
						break;
					default:
						break;
				}
				/* increase the data counter */
				(*count)++;
			}
			break;

		case XMIINS_SC_I:
		case XMIINS_SC_DI:
			while (curptr < endptr) {
				i = Load::SInt32(curptr, &isneg);
				if (isneg) {
					i = -i;
				}
				if (type == XMIINS_SC_DI) {
					curvalue += i;
				} else {
					curvalue = i;
				}
				if (level >= XMIINS_REPORT_ALL) {
					printf ("  sint32/di value: %ld\n", i);
				}
				(*count)++;
			}
			break;

		case XMIINS_SC_RL:			// report run length and text
			while (curptr < endptr) {
				/* repeat count */
				u = Load::UInt32(curptr);
				/* text to repeat */
				str = printlstring(curptr, false, XMIINS_REPORT_VERYMUCH);
				/* report */
				if (level >= XMIINS_REPORT_ALL) {
					printf ("  rl value: %ld times '%s'\n", u, str);
				}
				trydela (str);
				(*count)++;
			}
			break;

		case XMIINS_SC_U8:		// report 8-bit number
			while (curptr < endptr) {
				/* note, this char is used as an 8-bits number, not an ASCII char! */
				c = Load::Char(curptr);
				/* report */
				if (level >= XMIINS_REPORT_ALL) {
					printf ("  u8 value: %ld\n", c);
				}
				(*count)++;
			}
			break;

		case XMIINS_SC_BASE64:
			base64 = true;
			// no break!
		case XMIINS_SC_BASEX:
		case XMIINS_SC_BASE2:
		case XMIINS_SC_BASE16:
		case XMIINS_SC_BASE22:
			while (curptr < endptr) {
				/* flag */
				c = Load::Char(curptr);
				hexflag = c & 0x03;
				restdigits = (c & 0x7C) >> 2;
				/* digit count */
				u = Load::UInt32(curptr);
				/* skip digit data */
				if (base64) {
					curptr += u * 3;
					/* skip rest digits (possible values: 1-3) */
					if (restdigits > 0)
						curptr++;
					if (restdigits > 1)
						curptr++;
					if (restdigits > 2)
						curptr++;
				} else {
					numdigits = u + (restdigits > 0 ? 1 : 0);
					for (i=0; i<numdigits; i++) {
						Load::UInt32(curptr);
					}
				}
				(*count)++;
			}
			break;

		case XMIINS_SC_SEQCOMB:
		case XMIINS_SC_SEQ:
		case XMIINS_SC_C:
		case XMIINS_SC_P:
			/* these types should not be hit at all, but check size just to be sure */
			if (size != 0) {
				if (level >= XMIINS_REPORT_ERRORS) {
					printf ("container block %ld/%ld, container %ld/%ld of type %s hase size %ld, should be 0!\n", 
						block+1, numblocks, container+1, numcontainers[container], ExprInfo::type2text(type), size);
				}
				stat = XMIINS_ERR_DATASIZE;
				goto cleanup;
			}
			break;

		case XMIINS_SC_UNKNOWN:
			/* unknown & default should not be hit at all, but skip size just to be sure */
		case XMIINS_SC_MIXED:
		default:
			/* skip the data, don't check or increase data count */
			curptr += size;
			break;
	}

	/* was too little or too much data processed? */
	if (endptr != curptr) {
		if (level >= XMIINS_REPORT_ERRORS) {
			printf ("container block %ld/%ld, container %ld/%ld of type %s hase size %ld, but %ld Bytes were processed!\n", 
				block+1, numblocks, container+1, numcontainers[block], ExprInfo::type2text(type), size,
				curptr - startptr);
		}
		stat = XMIINS_ERR_DATASIZE;
		goto cleanup;
	}

cleanup:
	return stat;
}

int XMIInspect::inspectContainerBlock(int block, int container, bool issmall)
{
	int stat = 0, exprnum = 0, type = 0;
	long size = containersize[block+numblocks*container], macro, labelid;
	unsigned char *endptr;
	char isneg;
	int numprec = 0;

	if (size == 0) {
		/* empty block, skip it */
		goto cleanup;
	}

	if (issmall) {
		/* small container */
		if (level >= XMIINS_REPORT_MOREINFO) {
			printf("block %ld/%ld, container %ld/%ld has size %ld and is contained in this header\n", 
				block+1, numblocks,
				container+1, numcontainers[block], 
				containersize[block+numblocks*container]);
		}
	} else {
		/* large container */
		if (level >= XMIINS_REPORT_INFO) {
			printf("large block %ld/%ld, container %ld/%ld has size %ld\n", 
				curcontainer+1, numblocks,
				curblock+1, numcontainers[curcontainer], 
				containersize[curcontainer+numblocks*curblock]);
		}
		if (len != containersize[curcontainer+numblocks*curblock]) {
			if (level >= XMIINS_REPORT_ERRORS) {
				printf("size of uncompressed buffer (%ld Bytes) is not equal to container block %ld/%ld (%ld Bytes)!\n",
						len, curcontainer+1, numblocks, containersize[curcontainer+numblocks*curblock]);
			}
			stat = XMIINS_ERR_CONTAINERSIZE;
			goto cleanup;
		}
	}
	/* increase run size */
	runsize += size;
	/* For block 0, container 0-2 we can check and/or print the data:
	    C0 = structure container (a stream of sint32's)
	    C1 = global whitespace container (a stream of lstring's)
	    C2 = special container for CDATA, PI, CDATA and comments (a stream of lstring's)
    */
	if (block == 0) {
		endptr = curptr + size;
		switch (container) {
			case 0:	
				if (level >= XMIINS_REPORT_MOREINFO) {
					printf(" this is the structure container\n");
				}
				do {
					/* read macro & data flag */
					macro = Load::SInt32(curptr, &isneg);
					if (isneg) {
						/* data, we've just read a container index */
						if (macro >= numblocks || macro == 0) {
							if (level >= XMIINS_REPORT_ERRORS) {
								printf(" container index %ld is out of range (min = %ld, max = %ld)!\n", 
										macro, 1, numblocks-1);
							}
							stat = XMIINS_ERR_DATA_INDEX_UNKNOWN;
							goto cleanup;
						}
						/* increase data counts */
						numcontdata[macro]++;
						numdata++;
						/* print info */
						if (level >= XMIINS_REPORT_ALL) {
							/* note: macro 0 would refer to the structure container. negative flag is on, so the stored
							   number cannot be 0. pathindex[0] will always be 0, since the structure container has no
								associated path expression */
							printf("  container index %ld, associated path expression index: %ld\n", macro, pathindex[macro]);
						}
					} else {
						/* element label or special data */
						switch (macro) {
							case TREETOKEN_ENDLABEL:
								numclose++;
								if (level >= XMIINS_REPORT_ALL) {
									printf("  end label\n");
								}
								break;
							case TREETOKEN_EMPTYENDLABEL:
								numclose++;
								if (level >= XMIINS_REPORT_ALL) {
									printf("  empty end label\n");
								}
								break;
							case TREETOKEN_WHITESPACE:
								numwhite++;
								if (level >= XMIINS_REPORT_ALL) {
									printf("  white space\n");
								}
								break;
							case TREETOKEN_ATTRIBWHITESPACE:
								numwhite++;
								if (level >= XMIINS_REPORT_ALL) {
									printf("  attribute white space\n");
								}
								break;
							case TREETOKEN_SPECIAL:
								numspecial++;
								if (level >= XMIINS_REPORT_ALL) {
									printf("  special data\n");
								}
								break;
							default:
								numopen++;
								/* label */
								if ((labelid = macro-LABELIDX_TOKENOFFS) >= numlabels) {
									stat = XMIINS_ERR_UNKNOWNLABEL;
									if (level >= XMIINS_REPORT_ERRORS) {
										printf("label %ld unknown (max = %ld)!\n", labelid, numlabels-1);
									}
									goto cleanup;
								}
								if (level >= XMIINS_REPORT_ALL) {
									printf("  label %ld: '%s'\n", labelid, labels[labelid]);
								}
								break;
						}
					}
				} while (curptr < endptr);
				break;

			case 1:	
				if (level >= XMIINS_REPORT_MOREINFO) {
					printf(" this is the global whitespace container\n");
				}
				do {
					if (level >= XMIINS_REPORT_DEBUG) {
						printf("  whitespace: ");
					}
					delete[] printlstring(curptr, false, XMIINS_REPORT_DEBUG);
					if (level >= XMIINS_REPORT_DEBUG) {
						printf("\n");
					}
				} while (curptr < endptr);
				break;

			case 2:	
				if (level >= XMIINS_REPORT_MOREINFO) {
					printf(" this is the special container\n");
				}
				do {
					if (level >= XMIINS_REPORT_DEBUG) {
						printf("  special data: ");
					}
					delete[] printlstring(curptr, false, XMIINS_REPORT_DEBUG);
					if (level >= XMIINS_REPORT_DEBUG) {
						printf("\n");
					}
				} while (curptr < endptr);
				break;

			default:	
				if (level >= XMIINS_REPORT_ERRORS) {
					printf("Block %ld/%ld, container %ld/%ld is unknown!\n",
						block+1, numblocks,
						container+1, numcontainers[block]);
				}
				stat = XMIINS_ERR_GLOBALCONTAINER;
				goto cleanup;
		}
	} else {
		/* normal container block, inspect & report on the container data */
		exprnum = pathindex[block];
		type = exprinfo[exprnum]->getTranslatedContainerType(container+1);
		if (level >= XMIINS_REPORT_MOREINFO) {
			printf(" the container refers to pathexpr %ld, type: %s\n", exprnum, ExprInfo::type2text(type));
		}
		/* type check */
		if (type == XMIINS_SC_UNKNOWN || (type != XMIINS_SC_MIXED && ExprInfo::numContainers(type) != 1)) {
			if (level >= XMIINS_REPORT_ERRORS) {
				printf ("internal error; the type %ld is not correct\n", type);
			}
			stat = XMIINS_ERR_INTERNAL;
			goto cleanup;
		}

		numprec = numPrecedingBlocks(block, exprnum);
		/* we should now decode the data and increase the numrealcontdata[block+numblocks*container] count */
		if ((stat = processContainerData(block, container, size, type, 
													exprinfo[exprnum]->getTranslatedUsedSubs(container),
													exprinfo[exprnum]->getEnumIndex(block, container, numprec),
													&numrealcontdata[block+numblocks*container])) != 0) {
			goto cleanup;
		}
	}

cleanup:
	return stat;
}

/* read container info from the run header
 */
int XMIInspect::readContainerInfo()
{
	int stat = 0, exprcontainernum = 0;

	numblocks = Load::UInt32(curptr);
	if (level >= XMIINS_REPORT_SOMEINFO) {
		printf("datasize: %ld Bytes\n# container blocks: %ld\n", datasize, numblocks);
	}
	trydela(numcontainers);
	trydela(containersize);
	numcontainers = new long[numblocks];
	pathindex = new long[numblocks];
	numcontdata = new int[numblocks];
	numrealcontdata = new int[numblocks*MAXCONTAINERS];
	containersize = new long[numblocks*MAXCONTAINERS];
	for (int j = 0; j < numblocks; j++) {
		numcontdata[j] = 0;
		/* load & check path index */
		if ((pathindex[j] = Load::UInt32(curptr)) > numexprs) {
			if (level >= XMIINS_REPORT_ERRORS) {
				printf("path index %ld for container %ld out of range (max. %ld)!\n",
					pathindex[j], j, numexprs);
			}
			stat = XMIINS_ERR_CONTAINER_INDEX_UNKNOWN;
			goto cleanup;
		}
		/* load & check # containers in this block */
		if ((numcontainers[j] = Load::UInt32(curptr)) > MAXCONTAINERS) {
			if (level >= XMIINS_REPORT_ERRORS) {
				printf("too many containers: %ld > %ld!\n", numcontainers[j], MAXCONTAINERS);
			}
			stat = XMIINS_ERR_TOOMANYCONTAINERS;
			goto cleanup;
		}
		/* check the number against the number of container that the path expression defines */
		exprcontainernum = exprinfo[pathindex[j]]->getNumContainers();
		if (numcontainers[j] != exprcontainernum 
			 && exprcontainernum != -1) {
			if (level >= XMIINS_REPORT_ERRORS) {
				printf("# containers (%ld) in block %ld/%ld mismatches the # containers that pathexpr %ld expects: %ld!\n",
						numcontainers[j], j+1, numblocks, pathindex[j], exprcontainernum );
			}
			stat = XMIINS_ERR_NUM_CONTAINERS_MISMATCH;
			goto cleanup;
		}

		/* report some info */
		if (level >= XMIINS_REPORT_MOREINFO) {
			printf(" block %ld/%ld path expr index: %ld, # containers: %ld\n", 
				j+1, numblocks,
				pathindex[j], numcontainers[j]);
		}
		/* read container sizes */
		for (int k = 0; k < numcontainers[j]; k++) {
			containersize[j+numblocks*k] = Load::UInt32(curptr);
			numrealcontdata[j+numblocks*k] = 0;
			if (level >= XMIINS_REPORT_MOREINFO) {
				printf("  containersize: %ld Bytes\n", containersize[j+numblocks*k]);
			}
		}
	}

cleanup:
	return stat;
}

/* read a small container block's container
 *
 * structure:
 *  none, as this is dependent on the semantic compressor.
 */
int XMIInspect::readSmallContainerBlock()
{
	int stat = 0;

	for (int j = 0; j < numblocks; j++) {
		for (int k = 0; k < numcontainers[j]; k++) {
			if (containersize[j+numblocks*k] < SMALLCONT_THRESHOLD) {
				/* small container, inspect it */
				if ((stat = inspectContainerBlock(j, k, true)) != 0) {
					goto cleanup;
				}
			} else {
				/* large container, defer processing */
				expectedcontainers++;
			}
		}
	}

cleanup:
	return stat;
}

/* read a large container block's container
 *
 * structure:
 *  none, as this is dependent on the semantic compressor.
 */
int XMIInspect::readLargeContainerBlock()
{
	int stat = 0;

	/* find next block & container */
	while (curcontainer < numblocks && containersize[curcontainer+numblocks*curblock] < SMALLCONT_THRESHOLD) {
		/* next block */
		if (++curblock >= numcontainers[curcontainer]) {
			curblock = 0;
			/* next container */
			curcontainer++;
		}
	} 

	/* found it, now report size & check buffer length */
	foundcontainers++;
	if ((stat = inspectContainerBlock(curcontainer, curblock)) != 0) {
		goto cleanup;
	}

	/* check if all data was consumed */
	bytesleft = len - (long)curptr + (long)buffer;
	if (bytesleft != 0) {
		if (level >= XMIINS_REPORT_WARNINGS) {
			printf("container block %ld/%ld now contains %ld Bytes, should be 0!\n", 
				curcontainer+1, expectedcontainers,
				bytesleft);
		}
	} else if (level >= XMIINS_REPORT_MOREINFO) {
		printf ("large container block %ld/%ld is OK\n", foundcontainers, expectedcontainers);
	}

	/* find next block & container */
	do {
		/* next block */
		if (++curblock >= numcontainers[curcontainer]) {
			curblock = 0;
			/* next container */
			curcontainer++;
		}
	} while (curcontainer < numblocks && containersize[curcontainer+numblocks*curblock] < SMALLCONT_THRESHOLD);

	/* all containers found? */
	if (foundcontainers == expectedcontainers) {
		/* all found, this is the end of the run */
		if ((stat = endThisRun()) != 0) {
			goto cleanup;
		}
	}

cleanup:
	return stat;
}

