//===--------------- Reader.cpp - Open and read files ---------------------===//
//
//                                N3
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include <stdio.h>
#include <string.h>

#include "types.h"

#include "MSCorlib.h"
#include "N3.h"
#include "VMArray.h"
#include "VMClass.h"
#include "VMThread.h"
#include "Reader.h"

using namespace n3;

double Reader::readDouble(int first, int second) {
  int values[2];
  double res[1];
#if defined(__PPC__)
  values[0] = second;
  values[1] = first;
#else
  values[0] = first;
  values[1] = second;
#endif
  memcpy(res, values, 8); 
  return res[0];
}

sint64 Reader::readLong(int first, int second) {
  int values[2];
  sint64 res[1];
#if defined(__PPC__)
  values[0] = second;
  values[1] = first;
#else
  values[0] = first;
  values[1] = second;
#endif
  memcpy(res, values, 8); 
  return res[0];
}

const int Reader::SeekSet = SEEK_SET;
const int Reader::SeekCur = SEEK_CUR;
const int Reader::SeekEnd = SEEK_END;

ByteCode::ByteCode(mvm::BumpPtrAllocator &allocator, int size) {
	this->size = size;
	this->elements = (uint8*)allocator.Allocate(size * sizeof(uint8), "uint8[]");
}

ByteCode* Reader::openFile(mvm::BumpPtrAllocator &allocator, char* path) {
  FILE* fp = fopen(path, "r");
  ByteCode* res = 0;
  if (fp != 0) {
    fseek(fp, 0, SeekEnd);
    long nbb = ftell(fp);
    fseek(fp, 0, SeekSet);
		res = new(allocator, "ByteCode") ByteCode(allocator, nbb);
    fread(res->elements, nbb, 1, fp);
    fclose(fp);
  }
  return res;
}

uint8 Reader::readU1() {
	if(cursor >= (uint32)bytes->size)
		VMThread::get()->getVM()->error("readU1 outside the buffer");
  return bytes->elements[cursor++];
}

sint8 Reader::readS1() {
  return readU1();
}

uint16 Reader::readU2() {
  uint16 tmp = ((uint16)(readU1()));
  return tmp | (((uint16)(readU1())) << 8);
}

sint16 Reader::readS2() {
  sint16 tmp = ((sint16)(readS1()));
  return tmp | (((sint16)(readS1())) << 8);
}

uint32 Reader::readU4() {
  uint32 tmp = ((uint32)(readU2()));
  return tmp | (((uint32)(readU2())) << 16);
}

sint32 Reader::readS4() {
  sint32 tmp = ((sint32)(readS2()));
  return tmp | (((sint32)(readS2())) << 16);
}

uint64 Reader::readU8() {
  uint64 tmp = ((uint64)(readU4()));
  return tmp | (((uint64)(readU4())) << 32);
}

sint64 Reader::readS8() {
  sint64 tmp = ((sint64)(readS8()));
  return tmp | (((sint64)(readS8())) << 32);
}

Reader::Reader(ByteCode* array, uint32 start, uint32 end) {
  if (!end) 
		end = array->size;

  bytes = array;
  cursor = start;
  min = start;
  max = start + end;
}

unsigned int Reader::tell() {
  return cursor - min;
}

// Reader* Reader::derive(uint32 nbb) {
//   return new(allocator, "Reader") Reader(allocator, bytes, cursor, nbb);
// }

void Reader::seek(uint32 pos, int from) {
  uint32 n = 0;
  uint32 start = min;
  uint32 end = max;
  
  if (from == SeekCur) n = cursor + pos;
  else if (from == SeekSet) n = start + pos;
  else if (from == SeekEnd) n = end + pos;
  

  if ((n < start) || (n > end))
    VMThread::get()->getVM()->unknownError("out of range %d %d", n, end);

  cursor = n;
}

void Reader::print(mvm::PrintBuffer* buf) const {
  buf->write("Reader<>");
}
