/*  binfile.c   */
/*  Copyright 1989 Mountain Math Software  */
/*  All Rights Reserved                    */

#include <string.h>
#include <fcntl.h>
#include <float.h>
#include <unistd.h>
#include "hrdarth.h"
#include "cgidbg.h"
#include "remcom.h"
#include "yacintfc.h"
#include <stream.h>
#include "binfile.h"
#include "sysintfc.h"
#include "newaloc.h"
#include "tarnod.h"
#include "environ.h"
#include "mkstr.h"
#ifdef __NT_VC__
#include <io.h>
#endif


BinaryFile::BinaryFile(const char * f_name, TargetNode * nde,int dta_type):
	file_name(f_name),
	node(nde),
	data_type((data_word_type)dta_type),
	delete_file_name(0),
	exp_file_name(0),
	file(0),
	the_file_state(OK),
	open_flag(0),
	no_conversion_mach(0),
	no_conversion_bin_mach(0),
	val_ptr((char *) &x_val_ptr)
{
	if (!file_name) file_name = node->GetName();
	switch(data_type) {
default:
		DbgError("BinaryFile::BinaryFile","bad data type");
case mach_type:
		bytes_per_word = sizeof(MachWord);
		no_conversion_mach = 1 ;
		switch (TheArithType) {
	default :
				DbgError("BinaryFile::BinaryFile","bad TheArithType");
	case ArithType::ArithInt16 :
			no_conversion_bin_mach = 1;
			data_type = int16_type ;
			break ;
	case ArithType::ArithFloat:
			data_type = float_type ;
			break ;
		}
		break ;
case int8_type:
		bytes_per_word = sizeof(int8) ;
		break ;
case int16_type:
		bytes_per_word = sizeof(int16);
		if (TheArithType == ArithType::ArithInt16) no_conversion_mach =
			no_conversion_bin_mach = 1 ;
		break ;
case int32_type:
		bytes_per_word = sizeof(int32);
		break ;
case float_type:
		bytes_per_word = sizeof(float);
		if (TheArithType == ArithType::ArithFloat) no_conversion_mach = 1 ;
		break ;
case double_type:
		bytes_per_word = sizeof(double);
		break ;
	}
}

BinaryFile::~BinaryFile()
{
	delete delete_file_name ;
	delete exp_file_name ;
}


BinaryInputFile::BinaryInputFile(const char * file_name, TargetNode * node,
		int data_type, int sk):
	BinaryFile(file_name,node,data_type),
	skip(sk)
{
	open(0) ;
}

BinaryOutputFile::BinaryOutputFile(const char * file_name, TargetNode * node,
		int data_type):
	BinaryFile(file_name,node,data_type)
{
}

ErrCode BinaryInputFile::open(int force_error)
{
	for (;;) {
    	const char * new_file_name = file_name ;
    	const char * exp_name = TargetNode::open_file(
        	&new_file_name,file,"binary input file",0);
    	if (!exp_name) if (force_error && file < 1) return
				the_file_state = FatalError ;
			else return OK ;
		open_flag = 1 ;
    	delete exp_file_name ;
		exp_file_name = 0 ;
    	if (exp_name != new_file_name) exp_file_name = Concatenate(exp_name);
    	if (file_name != new_file_name) {
        	delete delete_file_name ;
        	file_name = delete_file_name = Concatenate(new_file_name);
    	}
		if (!skip) return the_file_state;
		off_t dist = 0 ;
		dist = lseek(file,skip,SEEK_SET) ;
		if (dist==skip) return the_file_state ;
		if (force_error || !State.IsInteractive()) {
			State.Error("cannot seek past skip bytes in`", exp_name,"'");
			the_file_state = EndOfData ;
			if (dist < 0) {
				SystemErrorMessage();
				the_file_state = FatalError ;
			}
			return the_file_state ;
		}
#ifdef INTERACTIVE
		*Output + OutputPrompt << "Cannot seek past skip bytes in`" <<
			file_name << "'.\n" ;
		TargetNode::expand_name(file_name,exp_name);
		if (dist < 0) SystemErrorMessage();
#endif
	}
}

ErrCode BinaryOutputFile::open(int force_error)
{
    const char * new_file_name = file_name ;
    const char * exp_name = TargetNode::create_file(
        &new_file_name,file,"binary output file",0);
    if (!exp_name) if (force_error && file < 1) return
            the_file_state = FatalError ;
	else return OK ;
	open_flag = 1 ;
    delete exp_file_name ;
	exp_file_name = 0 ;
    if (exp_name != new_file_name) exp_file_name = Concatenate(exp_name);
    if (file_name != new_file_name) {
        delete delete_file_name ;
        file_name = delete_file_name = Concatenate(new_file_name);
    }
	return OK ;
}

BinaryInputFile::~BinaryInputFile()
{
	close(file);
}

BinaryOutputFile::~BinaryOutputFile()
{
	close(file);
}

ErrCode BinaryInputFile::read_to_val_ptr()
{
	int rd = ::read(file,val_ptr,bytes_per_word);
/*
 *	LogOut << "BinaryInputFile::read:" ;
 *	for (int i = 0 ; i < bytes_per_word; i++)
 *		LogOut <<  " 0x" << hex << (unsigned) val_ptr[i] << dec ;
 *	LogOut << "\n" ;
 */
    if (rd < bytes_per_word) the_file_state = EndOfData ;
	return OK ;
}


double BinaryInputFile::case_read(const char * routine)
{
	switch (data_type) { 
default: 
        DbgError(routine,"bad data type"); \
case int8_type: 
		{ 
			int8 in ; 
			memcpy(&in,val_ptr,sizeof(in)); 
			// LogOut << "int8: " << (int) in << "\n" ;
			return in ;
		}
case int16_type:
		{
			int16 in ;
			memcpy(&in,val_ptr,sizeof(in));
			// LogOut << "int16: " << in << "\n" ;
			return in ;
		}
case int32_type:
		{
			int32 in ;
			memcpy(&in,val_ptr,sizeof(in));
			// LogOut << "int32: " << in << "\n" ;
        	return MachWord::convert_int_check(in);
		}
case float_type:
		{
            float in ;
            memcpy(&in,val_ptr,sizeof(in));
            // LogOut << "float: " << in << "\n" ;
            return in ;
        }
case double_type:
		{
            double in ;
            memcpy(&in,val_ptr,sizeof(in));
            // LogOut << "double: " << in << "\n" ;
            return in ;
        }
    }
}


IntegerMachWord BinaryInputFile::read_integer()
{
	if (read_to_val_ptr() >= EndOfData) return 0 ;
	if (no_conversion_bin_mach) return *(IntegerMachWord*) val_ptr ;
	return MachWord::convert_int_check(case_read(
		"BinaryInputFile::read_integer"));
	
}

MachWord BinaryInputFile::read()
{
	if (read_to_val_ptr() >= EndOfData) return 0 ;
	if (no_conversion_mach) return *(MachWord*) val_ptr ;
	double ret =  case_read("BinaryInputFile::read");
	if (data_type >= float_type) return ret ;
	return (int32) ret ;
}

static int32 check_overflow_int(int32 min, int32 max, double val, const char *tp)
{
	int warn = 0 ;
	if (val < min) val = min,warn=1;
	if (val > max) val = max,warn=1;
	if (warn) MachWord::hard_limit_warn(tp);
	return (int32) val ;
}

static double check_overflow_double(double min, double max, double val,
	const char * tp)
{
	int warn = 0 ;
	if (val < min) val = min,warn=1;
	if (val > max) val = max,warn=1;
	if (warn) MachWord::hard_limit_warn(tp);
	return val ;
}

#define case_write(routine) \
	switch(data_type) { \
default:  \
        DbgError(routine,"bad data type");  \
case int8_type: \
		{  \
			int8 out = check_overflow_int(-0x80,0x7f,val,"int8"); \
			::write(file,(char *) &out,sizeof(out)); \
			break ; \
		} \
case int16_type: \
		{  \
			int16 out = check_overflow_int(-0x8000,0x7fff,val,"int16"); \
			::write(file,(char *) &out,sizeof(out)); \
			break ; \
		} \
case int32_type:  \
		{  \
			int32 out = check_overflow_int(-0x80000000,0x7fffffff,val,"int32"); \
			::write(file,(char *) &out,sizeof(out)); \
			break ; \
		} \
case float_type:  \
		{  \
			float out = check_overflow_double(-FLT_MAX,FLT_MAX,val,"float"); \
			::write(file,(char *) &out,sizeof(out)); \
			break ; \
		} \
case double_type:  \
		{  \
			double out = check_overflow_double(-DBL_MAX,DBL_MAX,val,"double"); \
			::write(file,(char *) &out,sizeof(out)); \
			break ; \
		} \
    } 

void BinaryOutputFile::write_integer(IntegerMachWord val)
{
	// LogOut << "BinaryOutputFile::write_integer(" << val << ")\n" ;
	case_write("BinaryOutputFile::write_integer")
}

void BinaryOutputFile::write(MachWord v)
{
	MachWordCast val = v ;
	// LogOut << "BinaryOutputFile::write(" << val << ")\n" ;
	case_write("BinaryOutputFile::write");
}


