/**********************************************************************
 
	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	This program is distributed in the hope that it will be 
	useful, but WITHOUT ANY WARRANTY; without even the 
	implied warranty of MERCHANTABILITY or FITNESS FOR A 
	PARTICULAR PURPOSE.

**********************************************************************/

#include "vpf_std.h"

#include "vpf_file.h"
#include "vpf_table.h"
#include "vpf_types.h"
#include "vpf_util.h"

#include "change_endian.h"


char	endian_flag;

void
check_endian()
{
	int i=1;
	endian_flag = (*((char*)&i)) == 0;
}

void vpf_read_field(void *ret, FILE *fp, VPF_COL *col, BOOL alloc_dest){
int n;
	switch(col->data_type){
	case 'T':
		{
			if(col->number[0] == '*'){
				vpf_read_text((char**)ret, fp);
			}
			else {
				n=atoi(col->number);
				if(alloc_dest){
					*(char**)ret = (char *)calloc(n+1,1);
					fread(*(char**)ret, n, 1, fp);
					vpf_trim(*(char**)ret);
				}
				else{
					fread((char*)ret, n, 1, fp);
					vpf_trim((char*)ret);
				}
			}
		}
		break;
	case 'F':
		*(SHORT_FLOAT *)ret = vpf_read_short_float(fp);
		break;
	case 'R':
		*(LONG_FLOAT *)ret = vpf_read_long_float(fp);
		break;
	case 'S':
		*(SHORT *)ret = vpf_read_short(fp);
		break;
	case 'I':
		*(LONG *)ret = vpf_read_long(fp);
		break;
	case 'C':
	case 'B':
	case 'Z':
	case 'Y':
		if(atoi(col->number)==1){
			vpf_read_coordinate((VPF_COORDINATE*)ret, fp, col);
		}
		else{
			*(VPF_LIST **)ret = vpf_list_new((vpf_list_data_destructor_type)vpf_coordinate_delete);
			vpf_read_coordinates(*(VPF_LIST **)ret, fp, col);
		}
		break;
	case 'D':
		vpf_read_date(((VPF_DATE*)ret), fp);
		break;
	case 'K':
		*(TRIPLET_ID *)ret = vpf_read_triplet(fp);
		break;
	default:
		printf("error: unsupport field type. %s\n", col->name);
	}
}

LONG vpf_read_long(FILE *fp){
	LONG n;
	fread(&n, 1, sizeof(LONG), fp);
	
	return n;
}

SHORT vpf_read_short(FILE *fp){
	SHORT n;
	fread(&n, 1, sizeof(SHORT), fp);
	return n;
}

void vpf_read_date(VPF_DATE *date, FILE *fp){
char comma;

	memset(date, 0, sizeof(*date));
	fread(date->year, 4, 1, fp);
	fread(date->month, 2, 1, fp);
	fread(date->day, 2, 1, fp);
	fread(date->hour, 2, 1, fp);
	fread(date->min, 2, 1, fp);
	fread(date->sec, 2, 1, fp);

	fread(&comma, 1, 1, fp);
	fread(&date->time_zone, 1, 1, fp);
	fread(date->tz_hour_diff, 2, 1, fp);
	fread(date->tz_min_diff, 2, 1, fp);
}

TRIPLET_ID vpf_read_triplet(FILE *fp){
	TRIPLET_ID ret;
	BYTE triplet_sizes;
	SHORT id_size,tile_size,ext_size;
	LONG tmp;
	
	memset(&ret, 0, sizeof(ret));
	fread(&triplet_sizes, 1, sizeof(BYTE), fp);
	id_size = (triplet_sizes >> 6) & 0x03;
	tile_size = (triplet_sizes >> 4) & 0x03;
	ext_size = (triplet_sizes >> 2) & 0x03;
	tmp=0;
	fread(&tmp, id_size, 1, fp);
	change_endian(tmp)
	ret.cur_id = tmp;
	tmp=0;
	fread(&tmp, tile_size, 1, fp);
	change_endian(tmp)
	ret.tile_id= tmp;
	tmp=0;
	fread(&tmp, ext_size, 1, fp);
	change_endian(tmp)
	ret.ext_id= tmp;
	
	return ret;
}

TRIPLET_ID vpf_read_ki(FILE *fp, VPF_COL *c){
	TRIPLET_ID ret;
	memset(&ret, 0, sizeof(ret));

	if(c->data_type == 'I'){
		ret.cur_id = vpf_read_long(fp);
		change_endian(ret.cur_id)
	}
	else if(c->data_type == 'K'){
		ret = vpf_read_triplet(fp);
	}
	return ret;
}

LONG_FLOAT vpf_read_long_float(FILE *fp)
{
	LONG_FLOAT ret;
	fread(&ret, 8, 1, fp);
	change_endian(ret)
	return ret;
}

SHORT_FLOAT vpf_read_short_float(FILE *fp)
{
	SHORT_FLOAT ret;
	fread(&ret, 4, 1, fp);
	change_endian(ret)
	return ret;
}

void vpf_read_coordinate(VPF_COORDINATE *c, FILE *fp, VPF_COL *col){
	switch(col->data_type){
	case 'C':
		c->x = vpf_read_short_float(fp);
		c->y = vpf_read_short_float(fp);
		break;
	case 'B':
		c->x = vpf_read_long_float(fp);
		c->y = vpf_read_long_float(fp);
		break;
	case 'Z':
		c->x = vpf_read_short_float(fp);
		c->y = vpf_read_short_float(fp);
		c->z = vpf_read_short_float(fp);
		break;
	case 'Y':
		c->x = vpf_read_long_float(fp);
		c->y = vpf_read_long_float(fp);
		c->z = vpf_read_long_float(fp);
		break;
	}	
}

int vpf_read_coordinates(VPF_LIST *ret, FILE *fp, VPF_COL *col){
int i;
int count;
VPF_COORDINATE *c;

	if(atoi(col->number) > 0){
		count = atoi(col->number);
	}
	else{
		count = vpf_read_long(fp);	
	}

	for(i=0; i<count; ++i){
		c = vpf_coordinate_new();
		vpf_read_coordinate(c, fp, col);
		vpf_list_push(ret, c);
	}
	
	return i;
}

void vpf_read_text(char **dst, FILE *fp){
LONG text_length;
	text_length = vpf_read_long(fp);
	*dst = calloc(text_length+1,1);
	fread(*dst, text_length, 1, fp);
}

int vpf_table_load(VPF_TABLE *table, const char *filename){
VARIABLE_LENGTH_INDEX_FILE *index_file;
FILE *fp;
int line;
BOOL result;
char index_filename[MAX_PATH+1];
long filesize;

	result = FALSE;
	index_file = 0;
	
	if(table->f->read_record_func == NULL){
		printf("error: read_record_func must be set before call vpf_table_load.\n");
		goto exit;
	}
	
	if(is_variable_length_record(table)){
		if(strlen(filename) > MAX_PATH){
			printf("error: filename is too long %s.\n", filename);
			goto exit;
		}

		strcpy(index_filename, filename);
		index_filename[strlen(index_filename)-1] = 'x';
		index_file = variable_length_index_file_new();
		if(!variable_length_index_file_load(index_file, index_filename)){
			printf("error: index file can not load %s\n", index_filename);
			goto exit;
		}
	}
	
	fp = fopen(filename, "rb");
	if(fp==0){
		printf("error: cannt open file %s\n", filename);
		goto exit;
	}
	fseek(fp, 0, SEEK_END);
	filesize = ftell(fp);
	fseek(fp, 0, SEEK_SET);
	
	table->header = vpf_table_header_new();
	vpf_table_header_read(table->header, fp);
	
	table->records = vpf_list_new((vpf_list_data_destructor_type)table->f->delete_record_func);

	for(line=0; !feof(fp) && (filesize > ftell(fp)+1); ++line){
		if(!table->f->read_record_func(table, fp, table->header->columns)){
			printf("error: record read error %s:%d\n", filename, line);
			break;
		}
	}
	fclose(fp);
	
	result = TRUE;
exit:
	variable_length_index_file_delete(index_file);
	return result;
}
